"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DAY_SHIFT_VERSION_ENV = void 0;
exports.resolveManagedVersionSelection = resolveManagedVersionSelection;
exports.listInstalledVersions = listInstalledVersions;
exports.isExactStableVersion = isExactStableVersion;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const toml = __importStar(require("@iarna/toml"));
const state_layout_1 = require("./state-layout");
exports.DAY_SHIFT_VERSION_ENV = "DAY_SHIFT_VERSION";
function resolveManagedVersionSelection(options = {}) {
    const env = options.env ?? process.env;
    const cwd = node_path_1.default.resolve(options.cwd ?? process.cwd());
    const versionManagerRoot = (0, state_layout_1.resolveVersionManagerRoot)(options);
    const installedVersions = listInstalledVersions(versionManagerRoot);
    const diagnostics = [];
    const envSelector = readEnvironmentSelector(env);
    diagnostics.push({
        source: "environment",
        label: exports.DAY_SHIFT_VERSION_ENV,
        kind: envSelector === null ? "not-found" : "configured",
        selector: envSelector,
        path: null,
        message: envSelector === null
            ? `${exports.DAY_SHIFT_VERSION_ENV} is not set.`
            : `${exports.DAY_SHIFT_VERSION_ENV} selected '${envSelector}'.`
    });
    if (envSelector !== null) {
        return resolveConfiguredSelector({
            selector: envSelector,
            source: "environment",
            sourcePath: null,
            seenAliases: new Set(),
            versionManagerRoot,
            installedVersions,
            diagnostics
        });
    }
    const repositoryPin = findNearestRepositoryPin(cwd);
    diagnostics.push({
        source: "repository-pin",
        label: "nearest .day-shift-version",
        kind: repositoryPin === null ? "not-found" : "configured",
        selector: repositoryPin?.selector ?? null,
        path: repositoryPin?.path ?? null,
        message: repositoryPin === null
            ? "No .day-shift-version file was found while walking upward from the current directory."
            : `.day-shift-version selected '${repositoryPin.selector}'.`
    });
    if (repositoryPin !== null) {
        return resolveConfiguredSelector({
            selector: repositoryPin.selector,
            source: "repository-pin",
            sourcePath: repositoryPin.path,
            seenAliases: new Set(),
            versionManagerRoot,
            installedVersions,
            diagnostics
        });
    }
    let repositoryConfig;
    try {
        repositoryConfig = readNearestRepositoryVersionConfig(cwd);
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        diagnostics.push({
            source: "repository-local-config",
            label: ".day-shift/local.toml",
            kind: "configured",
            selector: null,
            path: cwd,
            message
        });
        return {
            ok: false,
            reason: "malformed-config-selector",
            message: "Repository version selector config is malformed.",
            details: [message],
            source: null,
            selector: null,
            versionManagerRoot,
            installedVersions,
            diagnostics
        };
    }
    diagnostics.push({
        source: "repository-local-config",
        label: ".day-shift/local.toml",
        kind: repositoryConfig.localConfig === null ? "not-found" : "configured",
        selector: repositoryConfig.localConfig?.selector ?? null,
        path: repositoryConfig.localConfig?.path ?? repositoryConfig.localConfigPath,
        message: repositoryConfig.localConfig === null
            ? "No local version selector was found in the nearest .day-shift/local.toml."
            : `.day-shift/local.toml selected '${repositoryConfig.localConfig.selector}'.`
    });
    if (repositoryConfig.localConfig !== null) {
        return resolveConfiguredSelector({
            selector: repositoryConfig.localConfig.selector,
            source: "repository-local-config",
            sourcePath: repositoryConfig.localConfig.path,
            seenAliases: new Set(),
            versionManagerRoot,
            installedVersions,
            diagnostics
        });
    }
    diagnostics.push({
        source: "repository-config",
        label: ".day-shift/config.toml",
        kind: repositoryConfig.sharedConfig === null ? "not-found" : "configured",
        selector: repositoryConfig.sharedConfig?.selector ?? null,
        path: repositoryConfig.sharedConfig?.path ?? repositoryConfig.sharedConfigPath,
        message: repositoryConfig.sharedConfig === null
            ? "No shared version selector was found in the nearest .day-shift/config.toml."
            : `.day-shift/config.toml selected '${repositoryConfig.sharedConfig.selector}'.`
    });
    if (repositoryConfig.sharedConfig !== null) {
        return resolveConfiguredSelector({
            selector: repositoryConfig.sharedConfig.selector,
            source: "repository-config",
            sourcePath: repositoryConfig.sharedConfig.path,
            seenAliases: new Set(),
            versionManagerRoot,
            installedVersions,
            diagnostics
        });
    }
    const defaultAliasPath = node_path_1.default.join(versionManagerRoot, "aliases", "default");
    const defaultAlias = readAliasSelector(defaultAliasPath);
    diagnostics.push({
        source: "global-alias",
        label: "default alias",
        kind: defaultAlias === null ? "not-found" : "configured",
        selector: defaultAlias,
        path: defaultAliasPath,
        message: defaultAlias === null
            ? "No default alias is configured."
            : `default alias selected '${defaultAlias}'.`
    });
    if (defaultAlias !== null) {
        return resolveConfiguredSelector({
            selector: defaultAlias,
            source: "global-alias",
            sourcePath: defaultAliasPath,
            seenAliases: new Set(["default"]),
            versionManagerRoot,
            installedVersions,
            diagnostics
        });
    }
    if (installedVersions.length === 1) {
        const installedVersion = installedVersions[0];
        diagnostics.push({
            source: "single-installed-version",
            label: "single installed version fallback",
            kind: "configured",
            selector: installedVersion.version,
            path: installedVersion.versionDirectory,
            message: `Exactly one installed version is available: ${installedVersion.version}.`
        });
        return createSelectionSuccess({
            versionManagerRoot,
            installedVersions,
            diagnostics,
            installedVersion,
            source: "single-installed-version",
            selector: installedVersion.version
        });
    }
    diagnostics.push({
        source: "single-installed-version",
        label: "single installed version fallback",
        kind: "not-found",
        selector: null,
        path: node_path_1.default.join(versionManagerRoot, "versions"),
        message: installedVersions.length === 0
            ? "No installed versions are available."
            : "Multiple installed versions are available; an explicit selector is required."
    });
    if (installedVersions.length === 0) {
        return {
            ok: false,
            reason: "no-installed-versions",
            message: "No installed Day Shift versions are available.",
            details: [
                `Version manager root: ${versionManagerRoot}`,
                `Versions path: ${node_path_1.default.join(versionManagerRoot, "versions")}`,
                `Set ${state_layout_1.DAY_SHIFT_VERSION_MANAGER_ROOT_ENV} to inspect alternate local state.`
            ],
            source: null,
            selector: null,
            versionManagerRoot,
            installedVersions,
            diagnostics
        };
    }
    return {
        ok: false,
        reason: "multiple-installed-versions",
        message: "Multiple installed Day Shift versions are available; an explicit selector is required.",
        details: [
            `Installed versions: ${installedVersions.map((entry) => entry.version).join(", ")}`,
            `Set ${exports.DAY_SHIFT_VERSION_ENV}, a repository selector, or the default alias.`
        ],
        source: null,
        selector: null,
        versionManagerRoot,
        installedVersions,
        diagnostics
    };
}
function listInstalledVersions(versionManagerRoot) {
    const versionsDirectory = node_path_1.default.join(versionManagerRoot, "versions");
    if (!node_fs_1.default.existsSync(versionsDirectory)) {
        return [];
    }
    return node_fs_1.default.readdirSync(versionsDirectory, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .map((entry) => entry.name)
        .filter((name) => isExactStableVersion(name))
        .filter((version) => installedVersionMetadataMatchesDirectory(node_path_1.default.join(versionsDirectory, version), version))
        .sort(compareStableVersions)
        .map((version) => {
        const versionDirectory = node_path_1.default.join(versionsDirectory, version);
        return {
            version,
            versionDirectory,
            packageDirectory: node_path_1.default.join(versionDirectory, "package"),
            metadataPath: node_path_1.default.join(versionDirectory, "metadata.json")
        };
    });
}
function installedVersionMetadataMatchesDirectory(versionDirectory, version) {
    const metadataPath = node_path_1.default.join(versionDirectory, "metadata.json");
    if (!node_fs_1.default.existsSync(metadataPath) || !node_fs_1.default.statSync(metadataPath).isFile()) {
        return false;
    }
    try {
        const parsed = JSON.parse(node_fs_1.default.readFileSync(metadataPath, "utf8"));
        return typeof parsed === "object" &&
            parsed !== null &&
            parsed.version === version;
    }
    catch {
        return false;
    }
}
function isExactStableVersion(value) {
    return /^\d+\.\d+\.\d+$/.test(value);
}
function readEnvironmentSelector(env) {
    const value = env[exports.DAY_SHIFT_VERSION_ENV];
    if (value === undefined) {
        return null;
    }
    return value.trim();
}
function resolveConfiguredSelector(options) {
    if (options.selector === "") {
        return {
            ok: false,
            reason: "empty-configured-selector",
            message: "Configured Day Shift version selector is empty.",
            details: [
                `Selection source: ${options.source}`,
                `Source path: ${options.sourcePath ?? "(none)"}`,
                "Provide an exact installed version selector."
            ],
            source: options.source,
            selector: options.selector,
            versionManagerRoot: options.versionManagerRoot,
            installedVersions: options.installedVersions,
            diagnostics: options.diagnostics
        };
    }
    const installedVersion = resolveInstalledVersionSelector(options.selector, options.installedVersions);
    if (installedVersion.status === "ambiguous") {
        return {
            ok: false,
            reason: "ambiguous-selector",
            message: "Configured Day Shift version selector is ambiguous.",
            details: [
                `Selection source: ${options.source}`,
                `Source path: ${options.sourcePath ?? "(none)"}`,
                `Requested selector: ${options.selector}`,
                `Matching installed versions: ${installedVersion.matches.map((entry) => entry.version).join(", ")}`
            ],
            source: options.source,
            selector: options.selector,
            versionManagerRoot: options.versionManagerRoot,
            installedVersions: options.installedVersions,
            diagnostics: options.diagnostics
        };
    }
    if (installedVersion.status === "missing" && isAliasName(options.selector)) {
        const aliasPath = node_path_1.default.join(options.versionManagerRoot, "aliases", options.selector);
        const aliasSelector = readAliasSelector(aliasPath);
        if (aliasSelector !== null) {
            if (options.seenAliases.has(options.selector)) {
                return {
                    ok: false,
                    reason: "alias-cycle",
                    message: "Version alias resolution encountered a cycle.",
                    details: [
                        `Alias: ${options.selector}`,
                        `Alias path: ${aliasPath}`
                    ],
                    source: "global-alias",
                    selector: options.selector,
                    versionManagerRoot: options.versionManagerRoot,
                    installedVersions: options.installedVersions,
                    diagnostics: options.diagnostics
                };
            }
            options.diagnostics.push({
                source: "global-alias",
                label: `${options.selector} alias`,
                kind: "configured",
                selector: aliasSelector,
                path: aliasPath,
                message: `${options.selector} alias selected '${aliasSelector}'.`
            });
            const seenAliases = new Set(options.seenAliases);
            seenAliases.add(options.selector);
            return resolveConfiguredSelector({
                selector: aliasSelector,
                source: "global-alias",
                sourcePath: aliasPath,
                seenAliases,
                versionManagerRoot: options.versionManagerRoot,
                installedVersions: options.installedVersions,
                diagnostics: options.diagnostics
            });
        }
    }
    if (installedVersion.status === "missing") {
        return {
            ok: false,
            reason: "missing-selected-version",
            message: "Configured Day Shift version is not installed.",
            details: [
                `Selection source: ${options.source}`,
                `Source path: ${options.sourcePath ?? "(none)"}`,
                `Requested selector: ${options.selector}`,
                `Installed versions: ${options.installedVersions.length === 0
                    ? "(none)"
                    : options.installedVersions.map((entry) => entry.version).join(", ")}`
            ],
            source: options.source,
            selector: options.selector,
            versionManagerRoot: options.versionManagerRoot,
            installedVersions: options.installedVersions,
            diagnostics: options.diagnostics
        };
    }
    return createSelectionSuccess({
        versionManagerRoot: options.versionManagerRoot,
        installedVersions: options.installedVersions,
        diagnostics: options.diagnostics,
        installedVersion: installedVersion.installedVersion,
        source: options.source,
        selector: options.selector
    });
}
function resolveInstalledVersionSelector(selector, installedVersions) {
    const exactMatch = installedVersions.find((entry) => entry.version === selector);
    if (exactMatch !== undefined) {
        return { status: "found", installedVersion: exactMatch };
    }
    if (selector === "latest-installed") {
        const latestInstalled = installedVersions.at(-1);
        return latestInstalled === undefined
            ? { status: "missing" }
            : { status: "found", installedVersion: latestInstalled };
    }
    if (!/^\d+(?:\.\d+)?$/.test(selector)) {
        return { status: "missing" };
    }
    const prefix = `${selector}.`;
    const matches = installedVersions.filter((entry) => entry.version.startsWith(prefix));
    if (matches.length === 1) {
        return { status: "found", installedVersion: matches[0] };
    }
    if (matches.length > 1) {
        return { status: "ambiguous", matches };
    }
    return { status: "missing" };
}
function readAliasSelector(aliasPath) {
    if (!node_fs_1.default.existsSync(aliasPath) || !node_fs_1.default.statSync(aliasPath).isFile()) {
        return null;
    }
    return node_fs_1.default.readFileSync(aliasPath, "utf8").trim();
}
function isAliasName(value) {
    return /^[a-z][a-z0-9-]*$/.test(value);
}
function findNearestRepositoryPin(cwd) {
    let currentDirectory = node_path_1.default.resolve(cwd);
    while (true) {
        const candidatePath = node_path_1.default.join(currentDirectory, ".day-shift-version");
        if (node_fs_1.default.existsSync(candidatePath) && node_fs_1.default.statSync(candidatePath).isFile()) {
            return {
                path: candidatePath,
                selector: node_fs_1.default.readFileSync(candidatePath, "utf8").trim()
            };
        }
        const parentDirectory = node_path_1.default.dirname(currentDirectory);
        if (parentDirectory === currentDirectory) {
            return null;
        }
        currentDirectory = parentDirectory;
    }
}
function readNearestRepositoryVersionConfig(cwd) {
    const workspaceRoot = findNearestDayShiftConfigWorkspace(cwd);
    if (workspaceRoot === null) {
        return {
            workspaceRoot: null,
            localConfigPath: null,
            sharedConfigPath: null,
            localConfig: null,
            sharedConfig: null
        };
    }
    const localConfigPath = node_path_1.default.join(workspaceRoot, ".day-shift", "local.toml");
    const sharedConfigPath = node_path_1.default.join(workspaceRoot, ".day-shift", "config.toml");
    return {
        workspaceRoot,
        localConfigPath,
        sharedConfigPath,
        localConfig: readConfigVersionSelector(localConfigPath),
        sharedConfig: readConfigVersionSelector(sharedConfigPath)
    };
}
function findNearestDayShiftConfigWorkspace(cwd) {
    let currentDirectory = node_path_1.default.resolve(cwd);
    while (true) {
        const dayShiftDirectory = node_path_1.default.join(currentDirectory, ".day-shift");
        if (node_fs_1.default.existsSync(node_path_1.default.join(dayShiftDirectory, "local.toml")) ||
            node_fs_1.default.existsSync(node_path_1.default.join(dayShiftDirectory, "config.toml"))) {
            return currentDirectory;
        }
        const parentDirectory = node_path_1.default.dirname(currentDirectory);
        if (parentDirectory === currentDirectory) {
            return null;
        }
        currentDirectory = parentDirectory;
    }
}
function readConfigVersionSelector(configPath) {
    if (!node_fs_1.default.existsSync(configPath) || !node_fs_1.default.statSync(configPath).isFile()) {
        return null;
    }
    const contents = node_fs_1.default.readFileSync(configPath, "utf8");
    if (contents.trim() === "") {
        return null;
    }
    const parsed = toml.parse(contents);
    const version = parsed.version;
    if (version === undefined) {
        return null;
    }
    if (typeof version !== "string") {
        throw new Error(`Version selector in ${configPath} must be a string.`);
    }
    return {
        path: configPath,
        selector: version.trim()
    };
}
function createSelectionSuccess(options) {
    return {
        ok: true,
        version: options.installedVersion.version,
        source: options.source,
        selector: options.selector,
        versionDirectory: options.installedVersion.versionDirectory,
        packageDirectory: options.installedVersion.packageDirectory,
        metadataPath: options.installedVersion.metadataPath,
        versionManagerRoot: options.versionManagerRoot,
        installedVersions: options.installedVersions,
        diagnostics: options.diagnostics
    };
}
function createDeferredDiagnostic(options) {
    return {
        source: options.source,
        label: options.label,
        kind: "deferred",
        selector: null,
        path: options.cwd,
        message: options.message
    };
}
function compareStableVersions(left, right) {
    const leftParts = left.split(".").map((part) => Number(part));
    const rightParts = right.split(".").map((part) => Number(part));
    for (let index = 0; index < 3; index += 1) {
        const difference = leftParts[index] - rightParts[index];
        if (difference !== 0) {
            return difference;
        }
    }
    return 0;
}
