"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDoctorCommand = createDoctorCommand;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const errors_1 = require("../../lib/errors");
const cli_constants_1 = require("../../lib/config/cli-constants");
const output_1 = require("../../lib/output");
const doctor_1 = require("../../lib/doctor");
const config_contract_1 = require("../../lib/config/config-contract");
const paths_1 = require("../../lib/paths");
const runtime_contract_1 = require("../../lib/runtime/runtime-contract");
async function createDoctorCommand() {
    const { Command } = await import('commander');
    const command = new Command('doctor');
    command
        .description('Diagnose baseline Day Shift workspace health using read-only checks.')
        .option('--runtime-only', 'Run only runtime and workspace-setup health checks.')
        .option('--artifact <path>', 'Run health checks for one selected specification or planning artifact.')
        .option('--planning-directory <path>', 'Run health checks for one selected planning directory.')
        .option('--scan-surface', 'Run the bounded read-only workspace scan-surface diagnostic.')
        .option('--scan-path <path>', 'Start a scan-surface diagnostic at one repository-relative subtree.')
        .option('--repository-audit', 'Run the authoritative repository-wide audit (the default).')
        .option('--action <command>', 'Classify findings for one exact authoritative command descriptor.')
        .action((_, commandInstance) => {
        const format = (0, output_1.resolveOutputFormat)(commandInstance.optsWithGlobals().format);
        if (format === null) {
            throw (0, errors_1.createUsageError)('Output format must be one of: text, json.', [
                `Received: ${String(commandInstance.optsWithGlobals().format)}`,
            ]);
        }
        const repositoryContext = resolveRepositoryContext(commandInstance);
        const options = commandInstance.opts();
        const selection = resolveDoctorSelection({
            repositoryContext,
            runtimeOnly: options.runtimeOnly === true,
            artifact: options.artifact,
            planningDirectory: options.planningDirectory,
            scanSurface: options.scanSurface === true,
            scanPath: options.scanPath,
            repositoryAudit: options.repositoryAudit === true,
            action: options.action,
        });
        const report = (0, doctor_1.runScopedDoctorChecks)(selection);
        const readinessStatus = (0, doctor_1.deriveDoctorReadinessStatus)(report);
        const blockingFailure = report.actionRelevance === null
            ? readinessStatus === 'blocked' || (0, doctor_1.isDoctorBlockingFailure)(report)
            : report.actionRelevance.currentActionBlockers.length > 0;
        if (blockingFailure) {
            throw new errors_1.RuntimeError({
                message: 'Doctor found core workspace health errors.',
                code: 'DOCTOR_CHECK_FAILED',
                exitCode: errors_1.RuntimeExitCode.ERROR,
                details: [...(0, doctor_1.renderDoctorErrorDetails)(report)],
                findings: report.actionRelevance === null
                    ? [...report.findings]
                    : report.actionRelevance.currentActionBlockers.map(doctor_1.toActionRelevanceValidationFinding),
            });
        }
        (0, output_1.emitSuccess)({
            command: 'doctor',
            format,
            text: (0, doctor_1.renderScopedDoctorReportText)(report),
            data: {
                repositoryRoot: report.repositoryRoot,
                detectionSource: report.detectionSource,
                runtimeProvenance: report.runtimeProvenance,
                domain: report.domain,
                scope: report.scope,
                readOnly: report.readOnly,
                producerFamilies: report.producerFamilies,
                completeness: report.completeness,
                scanSurface: report.scanSurface ?? null,
                actionRelevance: report.actionRelevance,
                checks: report.checks,
                summary: report.summary,
                readinessStatus,
            },
            findings: [...report.findings],
        });
    });
    return command;
}
function resolveDoctorSelection(options) {
    const selectors = [
        options.runtimeOnly,
        options.artifact !== undefined,
        options.planningDirectory !== undefined,
        options.scanSurface,
        options.repositoryAudit && !options.scanSurface,
    ].filter(Boolean).length;
    if (selectors > 1) {
        throw (0, errors_1.createUsageError)('Doctor scope inputs are mutually exclusive.', [
            'Choose only one of --runtime-only, --artifact, --planning-directory, or --scan-surface; --repository-audit may deepen --scan-surface.',
        ]);
    }
    const repositoryRoot = options.repositoryContext?.root ?? process.cwd();
    if (options.scanPath !== undefined && !options.scanSurface) {
        throw (0, errors_1.createUsageError)('Doctor --scan-path requires --scan-surface.');
    }
    const scanPath = options.scanPath === undefined ? undefined : validateScanPath(options.scanPath, repositoryRoot);
    const actionId = resolveActionId(options.action);
    if (options.runtimeOnly)
        return { domain: 'runtime', repositoryRoot, actionId };
    if (options.scanSurface)
        return { domain: 'scan-surface', repositoryRoot, repositoryAudit: options.repositoryAudit, scanPath, actionId };
    if (options.artifact !== undefined) {
        return {
            domain: 'artifact',
            path: validateDoctorTarget(options.artifact, 'file', repositoryRoot, options.repositoryContext),
            repositoryRoot,
            actionId,
        };
    }
    if (options.planningDirectory !== undefined) {
        return {
            domain: 'planning',
            path: validateDoctorTarget(options.planningDirectory, 'directory', repositoryRoot, options.repositoryContext),
            repositoryRoot,
            actionId,
        };
    }
    return { domain: 'repository-audit', repositoryRoot, actionId };
}
function validateScanPath(input, repositoryRoot) {
    const absolutePath = node_path_1.default.resolve(repositoryRoot, input);
    const relativePath = node_path_1.default.relative(repositoryRoot, absolutePath);
    if (relativePath === '..' || relativePath.startsWith(`..${node_path_1.default.sep}`) || node_path_1.default.isAbsolute(relativePath)) {
        throw (0, errors_1.createUsageError)('Doctor scan path must remain inside the repository root.', [`Received: ${input}`]);
    }
    if (!node_fs_1.default.existsSync(absolutePath) || !node_fs_1.default.statSync(absolutePath).isDirectory()) {
        throw (0, errors_1.createUsageError)('Doctor scan path must be an existing directory.', [`Received: ${input}`]);
    }
    return (0, paths_1.toRepositoryRelativePath)(repositoryRoot, absolutePath);
}
function resolveRepositoryContext(commandInstance) {
    const rootCommand = getRootCommand(commandInstance);
    if (rootCommand.configRepositoryContext) {
        return rootCommand.configRepositoryContext;
    }
    return (0, runtime_contract_1.createRepositoryContext)((0, runtime_contract_1.detectRepositoryRoot)(), false);
}
function resolveActionId(input) {
    if (input === undefined)
        return undefined;
    const actionId = input.trim();
    const descriptor = (0, output_1.getRegisteredCommandDescriptor)(actionId);
    if (actionId === '' || descriptor === null || descriptor.availability !== 'callable') {
        throw (0, errors_1.createUsageError)('Doctor action must match one authoritative callable command descriptor.', [
            `Received: ${input}`,
            'Use day-shift commands search <query> --format json to discover exact command identities.',
        ]);
    }
    return actionId;
}
function validateDoctorTarget(input, expectedKind, repositoryRoot, repositoryContext) {
    const resolvedConfig = (0, config_contract_1.resolveDayShiftConfig)({ repositoryContext });
    const planningRoot = node_path_1.default.resolve(resolvedConfig.derived.planningRootPath);
    const specificationRoot = node_path_1.default.resolve(repositoryRoot, node_path_1.default.posix.dirname(cli_constants_1.CLI_SPEC_INDEX_PATH));
    const absolutePath = node_path_1.default.resolve(repositoryRoot, input);
    const withinPlanningRoot = absolutePath === planningRoot || absolutePath.startsWith(`${planningRoot}${node_path_1.default.sep}`);
    const withinSpecificationRoot = absolutePath === specificationRoot || absolutePath.startsWith(`${specificationRoot}${node_path_1.default.sep}`);
    const allowed = expectedKind === 'file'
        ? withinPlanningRoot || withinSpecificationRoot
        : withinPlanningRoot;
    if (!allowed) {
        throw (0, errors_1.createUsageError)(expectedKind === 'file'
            ? 'Doctor selected artifact must stay inside the configured planning root or canonical specification root.'
            : 'Doctor selected scope must stay inside the configured planning root.', [
            `Received: ${input}`,
            `Planning root: ${(0, paths_1.toRepositoryRelativePath)(repositoryRoot, planningRoot)}`,
            ...(expectedKind === 'file'
                ? [`Specification root: ${(0, paths_1.toRepositoryRelativePath)(repositoryRoot, specificationRoot)}`]
                : []),
        ]);
    }
    if (!node_fs_1.default.existsSync(absolutePath)) {
        throw (0, errors_1.createUsageError)('Doctor selected scope does not exist.', [`Received: ${input}`]);
    }
    const stats = node_fs_1.default.statSync(absolutePath);
    if ((expectedKind === 'file' && !stats.isFile()) || (expectedKind === 'directory' && !stats.isDirectory())) {
        throw (0, errors_1.createUsageError)(`Doctor selected scope must be an existing ${expectedKind}.`, [`Received: ${input}`]);
    }
    if (expectedKind === 'file' && node_path_1.default.extname(absolutePath).toLowerCase() !== '.md') {
        throw (0, errors_1.createUsageError)('Doctor selected artifact must be a Markdown specification or planning artifact.', [
            `Received: ${input}`,
        ]);
    }
    return (0, paths_1.toRepositoryRelativePath)(repositoryRoot, absolutePath);
}
function getRootCommand(commandInstance) {
    let current = commandInstance;
    while (current?.parent)
        current = current.parent;
    return (current ?? commandInstance);
}
