import type { OneboxDatabase } from '../ts/database/index.js';
import * as plugins from '../ts/plugins.js';
import type { IServiceDomainRoute } from '../ts/types.js';

const valuesDiffer = (valueArg: unknown, otherValueArg: unknown): boolean => {
  return JSON.stringify(valueArg) !== JSON.stringify(otherValueArg);
};

const getDomainTarget = (
  targetPortsArg: plugins.servezoneInterfaces.data.IServiceTargetPort[],
): plugins.servezoneInterfaces.data.IServiceTargetPort | undefined => {
  return targetPortsArg.find((targetArg) => targetArg.default && ['http', 'https'].includes(targetArg.protocol || ''))
    || targetPortsArg.find((targetArg) => targetArg.name === 'web' && ['http', 'https'].includes(targetArg.protocol || ''))
    || targetPortsArg.find((targetArg) => ['http', 'https'].includes(targetArg.protocol || ''))
    || targetPortsArg.find((targetArg) => targetArg.name === 'web');
};

const getMigratedDomains = (
  domainArg: string | undefined,
  targetPortsArg: plugins.servezoneInterfaces.data.IServiceTargetPort[],
): IServiceDomainRoute[] => {
  if (!domainArg) return [];
  const domainTarget = getDomainTarget(targetPortsArg);
  const domainRoute: IServiceDomainRoute = { name: domainArg, protocol: 'https' };
  if (domainTarget?.name) {
    domainRoute.targetPort = domainTarget.name;
  }
  return [domainRoute];
};

export const runServiceTargetPortsMigration = async (
  databaseArg: OneboxDatabase,
): Promise<void> => {
  const services = await databaseArg.CService.getInstances({});
  for (const service of services) {
    const previousTargetPorts = service.targetPorts;
    const previousDomains = service.domains;
    const previousPublicPortMappings = service.publicPortMappings;
    const targetPorts: plugins.servezoneInterfaces.data.IServiceTargetPort[] = Array.isArray(service.targetPorts) && service.targetPorts.length > 0
      ? service.targetPorts
      : [{
        name: 'web',
        port: service.port,
        protocol: 'http',
        default: true,
      }];
    const domains: IServiceDomainRoute[] = Array.isArray(service.domains)
      ? service.domains
      : getMigratedDomains(service.domain, targetPorts);
    const publicPortMappings = Array.isArray(service.publicPortMappings) ? service.publicPortMappings : [];
    const normalizedPortData = plugins.servezoneInterfaces.data.normalizeServicePortData({
      name: service.name,
      ports: { web: service.port },
      targetPorts,
      domains,
      publicPortMappings,
    });
    const changed = valuesDiffer(previousTargetPorts, normalizedPortData.targetPorts)
      || valuesDiffer(previousDomains, normalizedPortData.domains)
      || valuesDiffer(previousPublicPortMappings, normalizedPortData.publicPortMappings);
    if (!changed) continue;
    service.targetPorts = normalizedPortData.targetPorts as typeof service.targetPorts;
    service.domains = normalizedPortData.domains as typeof service.domains;
    service.publicPortMappings = normalizedPortData.publicPortMappings as typeof service.publicPortMappings;
    service.updatedAt = Date.now();
    await service.save();
  }
};
