"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectInitInputs = collectInitInputs;
exports.executeInitPlan = executeInitPlan;
exports.resolveInitServiceConnections = resolveInitServiceConnections;
exports.runInitCommand = runInitCommand;
exports.resolveInitWorkerConfigs = resolveInitWorkerConfigs;
const node_path_1 = __importDefault(require("node:path"));
const availability_1 = require("../../auth-profiles/availability");
const storage_1 = require("../../auth-profiles/storage");
const storage_helpers_1 = require("../../auth-profiles/storage-helpers");
const store_1 = require("../../auth-profiles/store");
const control_plane_config_1 = require("../../config/control-plane-config");
const branding_1 = require("../../core/branding");
const health_1 = require("../../core/health");
const repo_registry_1 = require("../../core/repo-registry");
const tracked_project_1 = require("../../core/tracked-project");
const repository_setup_1 = require("../../git/repository-setup");
const remote_base_branch_1 = require("../../git/remote-base-branch");
const catalog_1 = require("../../i18n/catalog");
const factory_1 = require("../../platform/auth/factory");
const service_connections_1 = require("../../platform/auth/service-connections");
const repository_setup_2 = require("../../platform/gitea/repository-setup");
const provider_resolution_1 = require("../../platform/provider-resolution");
const factory_2 = require("../../platform/ticket-tracker/factory");
const auth_1 = require("../auth");
const check_names_1 = require("../doctor/check-names");
const repo_1 = require("../doctor/repo");
const locale_1 = require("../locale");
const prompts_1 = require("../prompts");
const install_1 = require("../service/install");
const shared_1 = require("../shared");
const locale_2 = require("./locale");
const project_ref_discovery_1 = require("./project-ref-discovery");
const rendering_1 = require("./rendering");
const targeted_gitea_1 = require("./targeted-gitea");
async function collectInitInputs(cwd, options, deps = {}) {
    const locale = await (0, locale_2.resolveInitLocale)(cwd, options);
    const prompter = deps.prompter ?? (0, prompts_1.createCliPrompter)({ locale });
    const loadAccounts = deps.loadAccounts ?? store_1.loadAuthProfiles;
    const getAuthAvailability = deps.getAuthAvailability ?? availability_1.getSavedAuthAvailability;
    const runInlineAuthSetup = deps.runInlineAuthSetup ?? auth_1.runAuthSetupFlow;
    const resolveWorkerConfigs = deps.resolveWorkerConfigs ?? resolveInitWorkerConfigs;
    const resolveServiceSelection = deps.resolveServiceSelection ?? shared_1.resolveInitServiceSelection;
    const resolveServiceConnections = deps.resolveServiceConnections ?? resolveInitServiceConnections;
    const serviceSelection = await resolveServiceSelection(options, {
        cwd,
        prompter,
    });
    const repositoryHostRemoteName = await (0, shared_1.resolveRepositoryHostRemoteName)(cwd, options, prompter);
    const resolvedOptions = {
        ...options,
        repositoryHostRemoteName,
    };
    const serviceOptions = {
        ...resolvedOptions,
        ...(await resolveInitServiceProjectRefs(cwd, serviceSelection, resolvedOptions, prompter, locale)),
    };
    (0, targeted_gitea_1.validateTargetedGiteaProjectRefs)(serviceOptions, locale);
    const hasServiceConnections = await resolveServiceConnections(cwd, serviceSelection, serviceOptions, {
        prompter,
    });
    if (!hasServiceConnections) {
        return { kind: "blocked" };
    }
    let accounts = (await loadAccounts()).profiles;
    while (true) {
        const selectableAccounts = await getSelectableInitAuthProfiles(accounts, getAuthAvailability);
        if (selectableAccounts.length > 0) {
            const workers = await resolveWorkerConfigs(selectableAccounts, {
                prompter,
            });
            const { config } = (0, provider_resolution_1.resolveSupportedProviders)(await (0, shared_1.resolveInitValues)(cwd, serviceOptions, {
                prompter,
                workers,
                serviceSelection,
            }));
            return {
                kind: "ready",
                cwd,
                config,
                createRepo: options.createRepo === true,
                ensureGitRepository: (0, targeted_gitea_1.isTargetedGiteaInit)(options),
                warnUnpushedSetup: (0, targeted_gitea_1.isTargetedGiteaInit)(options),
            };
        }
        if (!prompter.interactive) {
            throw new Error((0, catalog_1.translate)(locale, "init.command.auth.requiredBeforeContinue", {
                message: accounts.length === 0
                    ? (0, catalog_1.translate)(locale, "auth.command.noAuthsConfigured")
                    : (0, catalog_1.translate)(locale, "init.command.auth.noWorkingSignInsConfigured"),
            }));
        }
        if (accounts.length === 0) {
            console.log((0, catalog_1.translate)(locale, "init.command.auth.needsSignIn"));
            console.log((0, catalog_1.translate)(locale, "init.command.auth.setupFirst"));
        }
        else {
            console.log((0, catalog_1.translate)(locale, "init.command.auth.noneUsable"));
            console.log((0, catalog_1.translate)(locale, "init.command.auth.setupWorking"));
        }
        const setupResult = await runInlineAuthSetup(locale, { prompter });
        if (!setupResult) {
            console.log((0, catalog_1.translate)(locale, "init.command.canceled"));
            return { kind: "blocked" };
        }
        console.log((0, catalog_1.translate)(locale, "init.command.auth.setupCompleted"));
        accounts = (await loadAccounts()).profiles;
    }
}
async function getSelectableInitAuthProfiles(profiles, getAuthAvailability = availability_1.getSavedAuthAvailability) {
    const results = await Promise.all(profiles.map(async (profile) => ({
        profile,
        availability: await getAuthAvailability(profile),
    })));
    return results
        .filter((result) => result.availability.available)
        .map((result) => result.profile);
}
async function executeInitPlan(input, deps = {}) {
    const locale = input.config.i18n.defaultLanguage;
    const registry = await (0, repo_registry_1.loadRepoRegistry)(undefined, locale);
    const { repositoryHost, ticketTracker } = input.config;
    const trackedProjectKey = (0, provider_resolution_1.getTrackedProjectKey)(input.config);
    const existing = registry.repos.find((entry) => (0, provider_resolution_1.getTrackedProjectKey)(entry) === trackedProjectKey);
    if (existing && node_path_1.default.resolve(existing.cwd) !== node_path_1.default.resolve(input.cwd)) {
        throw new Error((0, catalog_1.translate)(locale, "init.command.alreadyRegistered", {
            repo: (0, tracked_project_1.formatTrackedProjectRef)(input.config, locale),
            cwd: existing.cwd,
        }));
    }
    if (input.ensureGitRepository === true) {
        await (deps.ensureGitRepository ?? repository_setup_1.ensureGitRepository)({
            repositoryRoot: input.cwd,
            defaultBranch: repositoryHost.baseBranch,
        });
    }
    if (input.createRepo === true) {
        const authProvider = (0, factory_1.createAuthProvider)(input.config, {
            retryCommand: `${branding_1.CLI_COMMAND_NAME} init`,
            locale: input.config.i18n.defaultLanguage,
        });
        const repositoryHostToken = await authProvider.getRepositoryHostToken(repositoryHost);
        const repository = await (deps.ensureGiteaRepository ?? repository_setup_2.ensureGiteaRepositoryExists)({
            projectRef: repositoryHost.projectRef,
            credential: repositoryHostToken,
            defaultBranch: repositoryHost.baseBranch,
            beforeCreate: () => (deps.assertCurrentHeadCanBePushed ?? repository_setup_1.assertCurrentHeadCanBePushed)({
                repositoryRoot: input.cwd,
                remoteName: repositoryHost.remoteName,
                branchName: repositoryHost.baseBranch,
            }),
        });
        await (deps.ensureGitRemote ?? repository_setup_1.ensureGitRemote)({
            repositoryRoot: input.cwd,
            remoteName: repositoryHost.remoteName,
            remoteUrl: repository.remoteUrl,
        });
        if (repository.created) {
            try {
                await (deps.pushCurrentHeadToRemoteBranch ?? repository_setup_1.pushCurrentHeadToRemoteBranch)({
                    repositoryRoot: input.cwd,
                    remoteName: repositoryHost.remoteName,
                    branchName: repositoryHost.baseBranch,
                    credentials: await authProvider.getGitCredentials(repositoryHost),
                });
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                throw new Error(`${message}\n${(0, remote_base_branch_1.formatRemoteBaseBranchMissingMessage)({
                    cwd: input.cwd,
                    remoteName: repositoryHost.remoteName,
                    baseBranch: repositoryHost.baseBranch,
                    locale,
                    projectRef: repositoryHost.projectRef,
                    includePartialCreateHint: true,
                })}`);
            }
        }
    }
    const prewriteChecks = await runInitPrewriteVerification(input.cwd, input.config);
    const visiblePrewriteChecks = filterDisplayedInitChecks(prewriteChecks);
    if (visiblePrewriteChecks.length > 0) {
        console.log((0, health_1.renderDoctorReport)(visiblePrewriteChecks));
        console.log("");
    }
    if ((0, health_1.hasFailure)(prewriteChecks)) {
        console.log((0, catalog_1.translate)(locale, "init.command.prewriteFailed"));
        return 1;
    }
    await (0, control_plane_config_1.writeConfig)(input.cwd, input.config);
    await (0, shared_1.installRepositoryAssets)(input.cwd);
    await (0, repo_registry_1.registerRepo)({
        repositoryHost,
        ticketTracker,
        cwd: input.cwd,
    });
    const enableService = deps.runServiceEnable ?? install_1.runServiceEnableCommand;
    try {
        const exitCode = await enableService(undefined, { quiet: true });
        if (exitCode !== 0) {
            console.log((0, rendering_1.renderInitServiceEnableFailure)(locale, new Error((0, catalog_1.translate)(locale, "init.command.serviceEnableFailed.exitCode", {
                exitCode,
            }))));
            return 1;
        }
    }
    catch (error) {
        console.log((0, rendering_1.renderInitServiceEnableFailure)(locale, error));
        return 1;
    }
    console.log((0, rendering_1.renderInitSuccessSummary)(locale, input.config, {
        includeIssueListUrl: input.createRepo === true,
        registeredCwd: node_path_1.default.resolve(input.cwd),
    }));
    if (input.warnUnpushedSetup === true &&
        (await (deps.hasUncommittedChanges ?? repository_setup_1.hasUncommittedChanges)(input.cwd))) {
        console.log("");
        console.log((0, catalog_1.translate)(locale, "init.command.success.publishSetupReminder", {
            remoteName: repositoryHost.remoteName,
            baseBranch: repositoryHost.baseBranch,
        }));
    }
    return 0;
}
async function runInitPrewriteVerification(cwd, config) {
    const authProvider = (0, factory_1.createAuthProvider)(config, {
        retryCommand: `${branding_1.CLI_COMMAND_NAME} init`,
        locale: config.i18n.defaultLanguage,
    });
    const bootstrapCheck = await bootstrapTrackerSchema(config, authProvider);
    const repositorySizeCheck = await (0, repo_1.checkRepositorySize)(cwd, config.i18n.defaultLanguage);
    return [
        bootstrapCheck,
        ...(repositorySizeCheck ? [repositorySizeCheck] : []),
        ...(await (0, shared_1.runDoctorChecks)(cwd, config, {
            includeInstructionFiles: false,
            includeConfigCheck: false,
            includeRepoRegistrationCheck: false,
            retryCommand: `${branding_1.CLI_COMMAND_NAME} init`,
            locale: config.i18n.defaultLanguage,
        })),
    ];
}
async function bootstrapTrackerSchema(config, authProvider) {
    try {
        await (0, factory_2.syncTicketTrackerWorkflowSchema)(config, authProvider);
        return {
            name: (0, check_names_1.getDoctorCheckName)("trackerSchemaBootstrap", config.i18n.defaultLanguage),
            status: "ok",
            message: (0, catalog_1.translate)(config.i18n.defaultLanguage, "init.command.bootstrapTrackerSchema.createdMissingIfNeeded"),
        };
    }
    catch (error) {
        return {
            name: (0, check_names_1.getDoctorCheckName)("trackerSchemaBootstrap", config.i18n.defaultLanguage),
            status: "fail",
            message: error instanceof Error
                ? error.message
                : (0, catalog_1.translate)(config.i18n.defaultLanguage, "init.command.bootstrapTrackerSchema.failed"),
        };
    }
}
async function resolveInitServiceConnections(cwd, selection, options, input = {}) {
    const locale = await (0, locale_2.resolveInitLocale)(cwd, options);
    const prompter = input.prompter ??
        (0, prompts_1.createCliPrompter)({
            locale,
        });
    const repositoryHostRemoteName = await (0, shared_1.resolveRepositoryHostRemoteName)(cwd, options, prompter);
    const resolvedOptions = {
        ...options,
        repositoryHostRemoteName,
    };
    const serviceOptions = {
        ...resolvedOptions,
        ...(await resolveInitServiceProjectRefs(cwd, selection, resolvedOptions, prompter, locale)),
    };
    const targets = await resolveInitServiceConnectionTargets(cwd, selection, serviceOptions);
    return ((await (0, service_connections_1.ensureServiceConnections)(targets, {
        ...(input.env ? { env: input.env } : {}),
        interactive: prompter.interactive,
        retryCommand: `${branding_1.CLI_COMMAND_NAME} init`,
        locale,
    })) === "ready");
}
async function resolveInitServiceProjectRefs(cwd, selection, options, prompter, locale) {
    const repositoryHostProjectRef = options.repositoryHostProjectRef ??
        (await (0, project_ref_discovery_1.discoverInitProjectRefFromRemote)(cwd, options.repositoryHostRemoteName, {
            providerType: "repositoryHost",
            kind: selection.repositoryHostKind,
        })) ??
        (await promptServiceProjectRefIfNeeded({
            kind: selection.repositoryHostKind,
            projectRefType: "repositoryHost",
            label: (0, catalog_1.translate)(locale, "init.command.prompt.repositoryProject"),
            prompter,
            locale,
        }));
    const ticketTrackerProjectRef = options.ticketTrackerProjectRef ??
        (selection.ticketTrackerKind === selection.repositoryHostKind
            ? repositoryHostProjectRef
            : ((await (0, project_ref_discovery_1.discoverInitProjectRefFromRemote)(cwd, options.repositoryHostRemoteName, {
                providerType: "ticketTracker",
                kind: selection.ticketTrackerKind,
            })) ??
                (await promptServiceProjectRefIfNeeded({
                    kind: selection.ticketTrackerKind,
                    projectRefType: "ticketTracker",
                    label: (0, catalog_1.translate)(locale, "init.command.prompt.issueTrackingProject"),
                    prompter,
                    locale,
                }))));
    return {
        ...(repositoryHostProjectRef ? { repositoryHostProjectRef } : {}),
        ...(ticketTrackerProjectRef ? { ticketTrackerProjectRef } : {}),
    };
}
async function promptServiceProjectRefIfNeeded(input) {
    if (!input.prompter.interactive ||
        !PROVIDERS_REQUIRING_AUTH_PROJECT_REF.has(input.kind)) {
        return undefined;
    }
    const projectRef = await input.prompter.input({
        label: input.label,
        defaultValue: DEFAULT_PROJECT_REF_BY_PROVIDER.get(input.kind) ?? "",
        validate: (value) => {
            if (input.projectRefType === "repositoryHost") {
                (0, provider_resolution_1.resolveRepositoryHostProjectRef)({
                    kind: input.kind,
                    projectRef: value,
                }, input.locale);
                return;
            }
            (0, provider_resolution_1.resolveTicketTrackerProjectRef)({
                kind: input.kind,
                projectRef: value,
            }, input.locale);
        },
    });
    return projectRef;
}
const PROVIDERS_REQUIRING_AUTH_PROJECT_REF = new Set(["gitea", "gitlab"]);
const DEFAULT_PROJECT_REF_BY_PROVIDER = new Map([
    ["gitea", "https://gitea.example.com/owner/repo"],
    ["gitlab", "https://gitlab.com/group/project"],
]);
async function resolveInitServiceConnectionTargets(cwd, selection, options) {
    const repositoryHostProjectRef = options.repositoryHostProjectRef ??
        (await (0, project_ref_discovery_1.discoverInitProjectRefFromRemote)(cwd, options.repositoryHostRemoteName, {
            providerType: "repositoryHost",
            kind: selection.repositoryHostKind,
        }));
    const ticketTrackerProjectRef = options.ticketTrackerProjectRef ??
        (selection.ticketTrackerKind === selection.repositoryHostKind
            ? repositoryHostProjectRef
            : await (0, project_ref_discovery_1.discoverInitProjectRefFromRemote)(cwd, options.repositoryHostRemoteName, {
                providerType: "ticketTracker",
                kind: selection.ticketTrackerKind,
            }));
    return {
        repositoryHost: {
            kind: selection.repositoryHostKind,
            ...(repositoryHostProjectRef
                ? { projectRef: repositoryHostProjectRef }
                : {}),
        },
        ticketTracker: {
            kind: selection.ticketTrackerKind,
            ...(ticketTrackerProjectRef
                ? { projectRef: ticketTrackerProjectRef }
                : {}),
        },
    };
}
async function runInitCommand(cwd, options, deps = {}) {
    const targetCwd = await (0, targeted_gitea_1.resolveInitTargetCwd)(cwd, options);
    try {
        (0, targeted_gitea_1.validateTargetedInitOptions)(options);
        const collected = await collectInitInputs(targetCwd, options, deps);
        if (collected.kind === "blocked") {
            return 1;
        }
        return await executeInitPlan(collected, {
            ...(deps.runServiceEnable
                ? { runServiceEnable: deps.runServiceEnable }
                : {}),
            ...(deps.ensureGitRepository
                ? { ensureGitRepository: deps.ensureGitRepository }
                : {}),
            ...(deps.ensureGiteaRepository
                ? { ensureGiteaRepository: deps.ensureGiteaRepository }
                : {}),
            ...(deps.ensureGitRemote
                ? { ensureGitRemote: deps.ensureGitRemote }
                : {}),
            ...(deps.assertCurrentHeadCanBePushed
                ? { assertCurrentHeadCanBePushed: deps.assertCurrentHeadCanBePushed }
                : {}),
            ...(deps.pushCurrentHeadToRemoteBranch
                ? { pushCurrentHeadToRemoteBranch: deps.pushCurrentHeadToRemoteBranch }
                : {}),
            ...(deps.hasUncommittedChanges
                ? { hasUncommittedChanges: deps.hasUncommittedChanges }
                : {}),
        });
    }
    catch (error) {
        if (error instanceof prompts_1.PromptCanceledError) {
            console.log((0, catalog_1.translate)(await (0, locale_2.resolveInitLocale)(targetCwd, options), "init.command.canceled"));
            return 1;
        }
        throw error;
    }
}
async function resolveInitWorkerConfigs(profiles, input = {}) {
    const locale = await (0, locale_1.loadCommandLocale)(process.cwd());
    if (profiles.length === 0) {
        throw new Error((0, catalog_1.translate)(locale, "auth.command.noAuthsConfigured"));
    }
    const prompter = input.prompter ?? (0, prompts_1.createCliPrompter)({ locale });
    const selectedProfile = await selectInitSavedAuth(profiles, prompter);
    const runtimeProfiles = (0, storage_helpers_1.getProviderAuthProfiles)(profiles, (0, store_1.getAuthProfileProvider)(selectedProfile));
    const runtimeLabel = (0, store_1.formatAuthAccountDisplay)(selectedProfile, runtimeProfiles, locale);
    console.log((0, catalog_1.translate)(locale, "init.command.auth.usingSignIn", {
        label: runtimeLabel,
    }));
    await persistSelectedSavedAuthOrder(profiles, selectedProfile);
    return {
        default: [buildWorkerName(selectedProfile)],
    };
}
async function selectInitSavedAuth(profiles, prompter) {
    const [defaultProfile] = profiles;
    if (!defaultProfile) {
        throw new Error((0, catalog_1.translate)(await (0, locale_1.loadCommandLocale)(process.cwd()), "auth.command.noAuthsConfigured"));
    }
    const locale = await (0, locale_1.loadCommandLocale)(process.cwd());
    if (profiles.length === 1) {
        return defaultProfile;
    }
    const profileById = new Map(profiles.map((profile) => [profile.id, profile]));
    const selectedId = await prompter.select({
        label: (0, catalog_1.translate)(locale, "init.command.auth.prompt.whichSignIn"),
        options: profiles.map((profile) => ({
            value: profile.id,
            label: (0, store_1.formatAuthAccountDisplay)(profile, profiles, locale),
        })),
        defaultValue: defaultProfile.id,
    });
    const selectedProfile = profileById.get(selectedId);
    if (!selectedProfile) {
        throw new Error((0, catalog_1.translate)(locale, "init.command.auth.selectedMissing", {
            value: selectedId,
        }));
    }
    return selectedProfile;
}
function buildWorkerName(profile) {
    return (0, store_1.getAuthProfileProvider)(profile);
}
async function persistSelectedSavedAuthOrder(currentProfiles, selectedProfile) {
    const provider = (0, store_1.getAuthProfileProvider)(selectedProfile);
    const currentIds = (0, storage_helpers_1.getProviderAuthProfiles)(currentProfiles, provider).map((profile) => profile.id);
    if (currentIds.length < 2 || currentIds[0] === selectedProfile.id) {
        return;
    }
    await (0, storage_1.setAuthProfileProviderOrder)(provider, [
        selectedProfile.id,
        ...currentIds.filter((profileId) => profileId !== selectedProfile.id),
    ]);
}
function filterDisplayedInitChecks(checks) {
    return checks.filter((check) => check.status !== "ok");
}
