"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.stopDockerComposeStack = exports.startDockerComposeStack = exports.deleteDockerComposeFile = exports.generateContainersDockerComposeFile = exports.DockerUnableToStartError = exports.DockerUnableToPullProxySidecarImage = exports.InvalidContainerServicePort = void 0;
const tslib_1 = require("tslib");
const fs = tslib_1.__importStar(require("fs"));
const path = tslib_1.__importStar(require("path"));
const yaml = tslib_1.__importStar(require("yaml"));
const docker_compose_1 = require("docker-compose");
const shared_1 = require("../shared");
const text_1 = require("../ui/text");
const tunnel_options_1 = require("./tunnel-options");
const HIDDEN_DIR = '.services';
class InvalidContainerServicePort extends shared_1.UserError {
    constructor(serviceKey) {
        super(text_1.Text.error.invalidServicePort(serviceKey));
    }
}
exports.InvalidContainerServicePort = InvalidContainerServicePort;
class DockerUnableToPullProxySidecarImage extends shared_1.UserError {
    constructor(err) {
        super(text_1.Text.tunnel.unableToPullProxySidecarImage(err?.message ?? 'Unknown Error Occurred.'));
    }
}
exports.DockerUnableToPullProxySidecarImage = DockerUnableToPullProxySidecarImage;
class DockerUnableToStartError extends shared_1.UserError {
    constructor(err) {
        super(text_1.Text.tunnel.unableToStartDockerComposeStack(err?.message ?? 'Unknown Error Occurred.'));
    }
}
exports.DockerUnableToStartError = DockerUnableToStartError;
const generateContainersDockerComposeFile = async (services, appId, envId) => {
    const filesGenerated = {};
    for (const service of services) {
        const { key: serviceKey, containers } = service;
        const containersWithTunnelConfig = containers.filter((container) => !!container.tunnel);
        if (containersWithTunnelConfig.length > 0) {
            const port = await (0, tunnel_options_1.getServicePort)(services, serviceKey);
            if (!port || port < 1024 || port > 49152) {
                throw new InvalidContainerServicePort(serviceKey);
            }
            const containerConfig = Object.fromEntries(containersWithTunnelConfig.map((container) => {
                const config = {
                    container_name: container.key,
                    ...container.tunnel?.docker
                };
                const envArray = container?.tunnel?.docker.environment ?? [];
                const filteredEnvArray = envArray.filter((envVar) => !envVar.startsWith('FORGE_EGRESS_PROXY_URL='));
                filteredEnvArray.push('FORGE_EGRESS_PROXY_URL=http://proxy-sidecar:7072');
                config.environment = filteredEnvArray;
                return [container.key, config];
            }));
            const dockerComposeConfig = {
                services: {
                    ...containerConfig,
                    ...(await getProxySidecarConfig(serviceKey, Object.keys(containerConfig), port, appId, envId))
                }
            };
            const yamlString = yaml.stringify(dockerComposeConfig);
            const filePath = getContainerDockerComposePath(serviceKey);
            fs.writeFileSync(filePath, yamlString);
            filesGenerated[serviceKey] = filePath;
        }
    }
    return filesGenerated;
};
exports.generateContainersDockerComposeFile = generateContainersDockerComposeFile;
const getProxySidecarConfig = async (serviceKey, containerKeys, port, appId, envId) => {
    let fopBaseUrl = 'https://forge-outbound-proxy.services.atlassian.com';
    let jwksUrl = 'https://forge.cdn.prod.atlassian-dev.net/.well-known/jwks.json';
    let proxySidecarImage = 'forge-ecr.services.atlassian.com/forge-platform/proxy-sidecar:latest';
    if (process.env.FORGE_GRAPHQL_GATEWAY?.startsWith('https://api-private.stg.atlassian.com/graphql')) {
        fopBaseUrl = 'https://forge-outbound-proxy.stg.services.atlassian.com';
        jwksUrl = 'https://forge.cdn.stg.atlassian-dev.net/.well-known/jwks.json';
        proxySidecarImage = 'forge-ecr.stg.services.atlassian.com/forge-platform/proxy-sidecar:latest';
    }
    let appIdShort = appId;
    if (appId.startsWith('ari:cloud:ecosystem::app/')) {
        appIdShort = appId.split('/')[1];
    }
    return {
        'proxy-sidecar': {
            image: proxySidecarImage,
            container_name: `proxy-sidecar-${serviceKey}`,
            environment: [
                `SERVICE_URL=http://${containerKeys[0]}:8080`,
                `FOP_BASE_URL=${fopBaseUrl}`,
                `APP_ID=ari:cloud:ecosystem::app/${appIdShort}`,
                `ENV_ID=ari:cloud:ecosystem::environment/${appIdShort}/${envId}`,
                `JWKS_URL=${jwksUrl}`,
                `IS_LOCAL_DEV=true`
            ],
            ports: [`${port}:${tunnel_options_1.DEFAULT_PROXY_INGRESS_PORT}`],
            depends_on: containerKeys
        }
    };
};
const getContainerDockerComposePath = (serviceKey) => {
    const hiddenDir = path.join(process.cwd(), HIDDEN_DIR);
    if (!fs.existsSync(hiddenDir)) {
        fs.mkdirSync(hiddenDir, { recursive: true });
    }
    return path.join(hiddenDir, `docker-compose-${serviceKey}.yml`);
};
const deleteDockerComposeFile = async (composeFile) => {
    if (fs.existsSync(composeFile)) {
        fs.unlinkSync(composeFile);
    }
    const hiddenDir = path.join(process.cwd(), HIDDEN_DIR);
    if (fs.existsSync(hiddenDir)) {
        const files = fs.readdirSync(hiddenDir);
        if (files.length === 0) {
            fs.rmdirSync(hiddenDir);
        }
    }
};
exports.deleteDockerComposeFile = deleteDockerComposeFile;
const startDockerComposeStack = async (dockerComposeFilePath, serviceKey) => {
    try {
        await (0, docker_compose_1.pullOne)('proxy-sidecar', {
            cwd: path.dirname(dockerComposeFilePath),
            log: true,
            config: dockerComposeFilePath
        });
    }
    catch (err) {
        throw new DockerUnableToPullProxySidecarImage(err);
    }
    try {
        await (0, docker_compose_1.upAll)({
            cwd: path.dirname(dockerComposeFilePath),
            log: true,
            config: dockerComposeFilePath,
            composeOptions: [`-p${serviceKey}`]
        });
    }
    catch (err) {
        throw new DockerUnableToStartError(err);
    }
};
exports.startDockerComposeStack = startDockerComposeStack;
const stopDockerComposeStack = async (configFile, composeFiles) => {
    if (!composeFiles || Object.keys(composeFiles).length === 0)
        return;
    const { services } = await configFile.readConfig();
    const serviceWithTunnelConfigExists = services?.some((service) => service.containers?.some((container) => {
        return !!container.tunnel;
    }));
    if (!services || services.length === 0 || !serviceWithTunnelConfigExists)
        return;
    await Promise.all(Object.entries(composeFiles).map(async ([serviceKey, file]) => {
        try {
            await (0, docker_compose_1.downAll)({ cwd: '.', log: true, config: file, composeOptions: [`-p${serviceKey}`] });
            await (0, exports.deleteDockerComposeFile)(file);
        }
        catch (err) {
            throw new Error(text_1.Text.tunnel.unableToStopDockerComposeStack(serviceKey, err.message ?? 'Unknown Error Occurred.'));
        }
    }));
};
exports.stopDockerComposeStack = stopDockerComposeStack;
