"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectServiceConnectionRequirements = collectServiceConnectionRequirements;
exports.formatServiceConnectionNeed = formatServiceConnectionNeed;
exports.ensureServiceConnections = ensureServiceConnections;
const service_auth_definition_1 = require("./service-auth-definition");
const service_connection_session_1 = require("./service-connection-session");
const catalog_1 = require("../../i18n/catalog");
const registry_1 = require("../repository-host/registry");
const registry_2 = require("../ticket-tracker/registry");
function collectServiceConnectionRequirements(input) {
    const locale = input.locale ?? catalog_1.DEFAULT_APP_LOCALE;
    const requirements = new Map();
    addServiceConnectionRequirement(requirements, {
        auth: (0, registry_1.requireRepositoryHostProvider)(input.repositoryHost.kind, locale).auth,
        role: "repository hosting",
        target: input.repositoryHost,
        type: "repositoryHost",
    });
    addServiceConnectionRequirement(requirements, {
        auth: (0, registry_2.requireTicketTrackerProvider)(input.ticketTracker.kind, locale).auth,
        role: "issue tracking",
        target: input.ticketTracker,
        type: "ticketTracker",
    });
    return [...requirements.values()];
}
function formatServiceConnectionNeed(requirement, locale = catalog_1.DEFAULT_APP_LOCALE) {
    return (0, catalog_1.translate)(locale, "serviceConnection.need", {
        displayName: requirement.displayName,
        roles: formatServiceConnectionRoles(requirement.roles, locale),
    });
}
async function ensureServiceConnections(services, input = {}) {
    const env = input.env ?? process.env;
    const interactive = input.interactive ?? false;
    const locale = input.locale ?? catalog_1.DEFAULT_APP_LOCALE;
    for (const requirement of collectServiceConnectionRequirements({
        ...services,
        locale,
    })) {
        console.log(formatServiceConnectionNeed(requirement, locale));
        const status = await ensureSingleServiceConnection(requirement, {
            env,
            interactive,
            locale,
            ...(input.retryCommand
                ? {
                    retryCommand: input.retryCommand,
                }
                : {}),
        });
        if (status === "missing") {
            return "missing";
        }
    }
    return "ready";
}
function addServiceConnectionRequirement(requirements, input) {
    const key = input.auth.connectionKey(input.target);
    const existing = requirements.get(key);
    if (existing) {
        if (!existing.roles.includes(input.role)) {
            existing.roles.push(input.role);
        }
        if (input.type === "repositoryHost") {
            existing.repositoryHost = {
                auth: input.auth,
                target: input.target,
            };
        }
        else {
            existing.ticketTracker = {
                auth: input.auth,
                target: input.target,
            };
        }
        return;
    }
    requirements.set(key, {
        key,
        displayName: input.auth.displayName,
        roles: [input.role],
        ...(input.type === "repositoryHost"
            ? {
                repositoryHost: {
                    auth: input.auth,
                    target: input.target,
                },
            }
            : {
                ticketTracker: {
                    auth: input.auth,
                    target: input.target,
                },
            }),
    });
}
function getPrimaryServiceConnection(requirement, locale = catalog_1.DEFAULT_APP_LOCALE) {
    if (requirement.repositoryHost) {
        return requirement.repositoryHost;
    }
    if (requirement.ticketTracker) {
        return requirement.ticketTracker;
    }
    throw new Error((0, catalog_1.translate)(locale, "serviceConnection.error.emptyRequirement"));
}
async function ensureSingleServiceConnection(requirement, input) {
    const { auth, target } = getPrimaryServiceConnection(requirement, input.locale);
    const initialCheck = await (0, service_connection_session_1.checkServiceConnectionSession)(auth, target, input.env, input.locale);
    if (initialCheck.status === "ready") {
        return "ready";
    }
    if (!(await canRunInlineServiceConnectionSetup(auth, target, input.interactive))) {
        logServiceConnectionFailure(auth, target, {
            locale: input.locale,
            ...(initialCheck.reason
                ? {
                    reason: initialCheck.reason,
                }
                : {}),
            ...(input.retryCommand
                ? {
                    retryCommand: input.retryCommand,
                }
                : {}),
        });
        return "missing";
    }
    const setupStatus = await runInlineServiceConnectionSetup(auth, target, input.locale);
    if (setupStatus.status === "missing") {
        logServiceConnectionFailure(auth, target, {
            locale: input.locale,
            reason: setupStatus.reason,
            ...(input.retryCommand
                ? {
                    retryCommand: input.retryCommand,
                }
                : {}),
        });
        return "missing";
    }
    const retryCheck = await (0, service_connection_session_1.checkServiceConnectionSession)(auth, target, input.env, input.locale);
    if (retryCheck.status === "ready") {
        return "ready";
    }
    logServiceConnectionFailure(auth, target, {
        locale: input.locale,
        ...(retryCheck.reason
            ? {
                reason: retryCheck.reason,
            }
            : {}),
        ...(input.retryCommand
            ? {
                retryCommand: input.retryCommand,
            }
            : {}),
    });
    return "missing";
}
async function canRunInlineServiceConnectionSetup(auth, target, interactive) {
    return Boolean(interactive &&
        auth.getSetupMode(target) === "inline" &&
        (await auth.isSetupSupported(target)) &&
        auth.runSetup);
}
async function runInlineServiceConnectionSetup(auth, target, locale) {
    const runSetup = auth.runSetup;
    if (!runSetup) {
        throw new Error((0, catalog_1.translate)(locale, "serviceConnection.error.missingInlineSetup"));
    }
    try {
        await runSetup(target, locale);
        return {
            status: "ready",
        };
    }
    catch (error) {
        return {
            status: "missing",
            reason: error instanceof Error
                ? error.message
                : (0, catalog_1.translate)(locale, "serviceConnection.setupFailed"),
        };
    }
}
function logServiceConnectionFailure(auth, target, input) {
    console.log((0, service_auth_definition_1.renderServiceConnectionFailureMessage)(auth, target, {
        ...(input.reason
            ? {
                reason: input.reason,
            }
            : {}),
        ...(input.retryCommand
            ? {
                retryCommand: input.retryCommand,
            }
            : {}),
    }, input.locale));
}
function formatServiceConnectionRoles(roles, locale) {
    if (roles.length === 1) {
        const [onlyRole] = roles;
        if (!onlyRole) {
            throw new Error((0, catalog_1.translate)(locale, "serviceConnection.error.missingRole"));
        }
        return translateServiceConnectionRole(onlyRole, locale);
    }
    if (roles.length === 2) {
        const [first, second] = roles;
        if (!first || !second) {
            throw new Error((0, catalog_1.translate)(locale, "serviceConnection.error.missingRole"));
        }
        return `${translateServiceConnectionRole(first, locale)} ${(0, catalog_1.translate)(locale, "serviceConnection.roles.and")} ${translateServiceConnectionRole(second, locale)}`;
    }
    const localizedRoles = roles.map((role) => translateServiceConnectionRole(role, locale));
    const initial = localizedRoles.slice(0, -1).join(", ");
    const last = roles.at(-1);
    if (!last) {
        throw new Error((0, catalog_1.translate)(locale, "serviceConnection.error.missingRole"));
    }
    return `${initial}, ${(0, catalog_1.translate)(locale, "serviceConnection.roles.and")} ${translateServiceConnectionRole(last, locale)}`;
}
function translateServiceConnectionRole(role, locale) {
    switch (role) {
        case "repository hosting":
            return (0, catalog_1.translate)(locale, "serviceConnection.role.repositoryHosting");
        case "issue tracking":
            return (0, catalog_1.translate)(locale, "serviceConnection.role.issueTracking");
    }
}
