"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;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.listRegisteredFixHandlers = listRegisteredFixHandlers;
exports.getFixHandlersForRule = getFixHandlersForRule;
exports.hasFixHandlerForRule = hasFixHandlerForRule;
const path = __importStar(require("node:path"));
const node_fs_1 = require("node:fs");
const adr_1 = require("../adr");
const fileHelpers_1 = require("./fileHelpers");
function relative(filePath, workspaceRoot) {
    return path.relative(workspaceRoot, filePath).replace(/\\/g, '/');
}
function parseModulesFromArch004Message(message) {
    const prefixMatch = /^Module baseline drift detected in ([^:]+):\s*/u.exec(message);
    if (!prefixMatch?.[1] || !message.endsWith('.')) {
        return undefined;
    }
    const modulesRoot = prefixMatch[1].trim();
    if (modulesRoot.length === 0) {
        return undefined;
    }
    const payload = message.slice(prefixMatch[0].length, -1).trim();
    if (!payload) {
        return undefined;
    }
    const segments = payload
        .split(';')
        .map((segment) => segment.trim())
        .filter((segment) => segment.length > 0);
    if (segments.length === 0) {
        return undefined;
    }
    const parsedModules = [];
    const allowedMissingParts = new Set(['readme.md', 'index.ts', 'public entrypoint']);
    for (const segment of segments) {
        const match = /^([a-z0-9-]+)\s+\(([^)]+)\)$/u.exec(segment);
        if (!match?.[1] || !match[2]) {
            return undefined;
        }
        const missing = [...new Set(match[2]
                .split(',')
                .map((entry) => entry.trim().toLowerCase())
                .filter((entry) => allowedMissingParts.has(entry)))];
        if (missing.length === 0) {
            return undefined;
        }
        parsedModules.push({
            module: match[1],
            missing: missing.sort((left, right) => left.localeCompare(right)),
        });
    }
    return {
        modulesRoot,
        modules: parsedModules.sort((left, right) => left.module.localeCompare(right.module)),
    };
}
function parseModulePathFromArch005Message(message) {
    const match = /registered at ([^ ]+) but the directory does not exist/u.exec(message);
    return match?.[1];
}
function parseContractPathFromArch006Message(message) {
    const match = /references contract ([^ ]+) but the file does not exist/u.exec(message);
    return match?.[1];
}
function parseModuleName(message) {
    const match = /Module '([^']+)'/u.exec(message);
    return match?.[1];
}
function parseDep006Message(message) {
    const match = /Module '([^']+)' declares dependency on '([^']+)'/u.exec(message);
    if (!match?.[1] || !match?.[2]) {
        return undefined;
    }
    return {
        sourceModule: match[1],
        dependency: match[2],
    };
}
const architectureOverviewStarterTemplate = `# Architecture Overview

## Purpose
Describe the purpose of this system and its primary responsibilities.

## Architecture Style
Document the chosen architecture style and why it was selected.

## Module Structure
List the major modules or capability areas and their responsibilities.

## Dependency Boundaries
Describe how modules are allowed to depend on each other.

## Key Decisions
Reference ADRs that define major architectural decisions.

## Validation Expectations
Describe what ArchPilot validates in this repository and why.
`;
async function previewArch001(context) {
    const targetPaths = [
        path.join(context.workspaceRoot, 'src', 'modules'),
        path.join(context.workspaceRoot, 'src', 'modules', 'example-module'),
        path.join(context.workspaceRoot, 'src', 'modules', 'example-module', 'api'),
        path.join(context.workspaceRoot, 'src', 'modules', 'example-module', 'service'),
        path.join(context.workspaceRoot, 'src', 'modules', 'example-module', 'repository'),
    ];
    const filesCreated = [];
    for (const targetPath of targetPaths) {
        if (!(await (0, fileHelpers_1.pathExists)(targetPath))) {
            filesCreated.push(relative(targetPath, context.workspaceRoot));
        }
    }
    return {
        filesCreated,
        filesModified: [],
        diffPreview: filesCreated.length === 0
            ? '(no changes)'
            : filesCreated.map((filePath) => `+ create directory ${filePath}`).join('\n'),
        description: 'Create the baseline modular folder structure under src/modules.',
    };
}
async function applyArch001(context) {
    const targetPaths = [
        path.join(context.workspaceRoot, 'src', 'modules'),
        path.join(context.workspaceRoot, 'src', 'modules', 'example-module'),
        path.join(context.workspaceRoot, 'src', 'modules', 'example-module', 'api'),
        path.join(context.workspaceRoot, 'src', 'modules', 'example-module', 'service'),
        path.join(context.workspaceRoot, 'src', 'modules', 'example-module', 'repository'),
    ];
    const filesCreated = [];
    for (const targetPath of targetPaths) {
        const existed = await (0, fileHelpers_1.pathExists)(targetPath);
        await (0, fileHelpers_1.ensureDirectory)(targetPath);
        if (!existed) {
            filesCreated.push(relative(targetPath, context.workspaceRoot));
        }
    }
    return {
        applied: filesCreated.length > 0,
        filesCreated,
        filesModified: [],
        description: filesCreated.length > 0
            ? 'Created the baseline modular folder structure under src/modules.'
            : 'Baseline modular folder structure already exists.',
    };
}
async function previewArch003(context) {
    const directoryPath = path.join(context.workspaceRoot, 'docs', 'architecture');
    const filePath = path.join(directoryPath, 'overview.md');
    const directoryExists = await (0, fileHelpers_1.pathExists)(directoryPath);
    const fileExists = await (0, fileHelpers_1.pathExists)(filePath);
    const filesCreated = [];
    if (!directoryExists) {
        filesCreated.push(relative(directoryPath, context.workspaceRoot));
    }
    if (!fileExists) {
        filesCreated.push(relative(filePath, context.workspaceRoot));
    }
    return {
        filesCreated,
        filesModified: [],
        diffPreview: fileExists
            ? '(no changes)'
            : `${directoryExists ? '' : `+ create directory ${relative(directoryPath, context.workspaceRoot)}\n`}+ ${architectureOverviewStarterTemplate
                .trimEnd()
                .replace(/\n/g, '\n+ ')}`,
        description: 'Create a starter architecture overview document if missing.',
    };
}
async function applyArch003(context) {
    const directoryPath = path.join(context.workspaceRoot, 'docs', 'architecture');
    const filePath = path.join(directoryPath, 'overview.md');
    const directoryExisted = await (0, fileHelpers_1.pathExists)(directoryPath);
    const fileCreated = await (0, fileHelpers_1.createFileIfMissing)(filePath, architectureOverviewStarterTemplate);
    const filesCreated = [];
    if (!directoryExisted) {
        filesCreated.push(relative(directoryPath, context.workspaceRoot));
    }
    if (fileCreated) {
        filesCreated.push(relative(filePath, context.workspaceRoot));
    }
    return {
        applied: fileCreated,
        filesCreated,
        filesModified: [],
        description: fileCreated
            ? 'Created docs/architecture/overview.md starter template.'
            : 'docs/architecture/overview.md already exists.',
    };
}
async function previewApi001(context) {
    const filePath = path.join(context.workspaceRoot, 'contracts', 'openapi.yaml');
    const exists = await (0, fileHelpers_1.pathExists)(filePath);
    return {
        filesCreated: exists ? [] : [relative(filePath, context.workspaceRoot)],
        filesModified: [],
        diffPreview: exists
            ? '(no changes)'
            : `+ openapi: 3.0.0
+ info:
+   title: Project API
+   version: 1.0.0
+ paths: {}`,
        description: 'Create a missing OpenAPI specification file.',
    };
}
async function applyApi001(context) {
    const filePath = path.join(context.workspaceRoot, 'contracts', 'openapi.yaml');
    const created = await (0, fileHelpers_1.createFileIfMissing)(filePath, `openapi: 3.0.0
info:
  title: Project API
  version: 1.0.0
paths: {}
`);
    return {
        applied: created,
        filesCreated: created ? [relative(filePath, context.workspaceRoot)] : [],
        filesModified: [],
        description: created
            ? 'Created contracts/openapi.yaml.'
            : 'contracts/openapi.yaml already exists.',
    };
}
async function previewArch005(context) {
    const modulePath = context.finding.sourceFile ?? parseModulePathFromArch005Message(context.finding.message);
    if (!modulePath) {
        return {
            filesCreated: [],
            filesModified: [],
            diffPreview: '(no changes)',
            description: 'Unable to infer missing module path from finding.',
        };
    }
    const absolutePath = path.join(context.workspaceRoot, ...modulePath.split('/'));
    const exists = await (0, fileHelpers_1.pathExists)(absolutePath);
    return {
        filesCreated: exists ? [] : [modulePath],
        filesModified: [],
        diffPreview: exists ? '(no changes)' : `+ create directory ${modulePath}`,
        description: `Ensure module registry path exists at ${modulePath}.`,
    };
}
async function applyArch005(context) {
    const modulePath = context.finding.sourceFile ?? parseModulePathFromArch005Message(context.finding.message);
    if (!modulePath) {
        return {
            applied: false,
            filesCreated: [],
            filesModified: [],
            description: 'Skipped: unable to infer missing module path from finding.',
        };
    }
    const absolutePath = path.join(context.workspaceRoot, ...modulePath.split('/'));
    const existed = await (0, fileHelpers_1.pathExists)(absolutePath);
    await (0, fileHelpers_1.ensureDirectory)(absolutePath);
    return {
        applied: !existed,
        filesCreated: existed ? [] : [modulePath],
        filesModified: [],
        description: `Ensured module path exists at ${modulePath}.`,
    };
}
async function previewArch006(context) {
    const contractPath = context.finding.sourceFile ?? parseContractPathFromArch006Message(context.finding.message);
    const moduleName = parseModuleName(context.finding.message) ?? 'module';
    if (!contractPath) {
        return {
            filesCreated: [],
            filesModified: [],
            diffPreview: '(no changes)',
            description: 'Unable to infer missing contract path from finding.',
        };
    }
    const absolutePath = path.join(context.workspaceRoot, ...contractPath.split('/'));
    const exists = await (0, fileHelpers_1.pathExists)(absolutePath);
    return {
        filesCreated: exists ? [] : [contractPath],
        filesModified: [],
        diffPreview: exists
            ? '(no changes)'
            : `+ {
+   "module": "${moduleName}",
+   "dependsOn": [],
+   "publicEntrypoints": ["src/modules/${moduleName}/index.ts"],
+   "ownedTables": [],
+   "exposedApis": []
+ }`,
        description: `Create missing module contract file at ${contractPath}.`,
    };
}
async function applyArch006(context) {
    const contractPath = context.finding.sourceFile ?? parseContractPathFromArch006Message(context.finding.message);
    const moduleName = parseModuleName(context.finding.message) ?? 'module';
    if (!contractPath) {
        return {
            applied: false,
            filesCreated: [],
            filesModified: [],
            description: 'Skipped: unable to infer missing contract path from finding.',
        };
    }
    const absolutePath = path.join(context.workspaceRoot, ...contractPath.split('/'));
    const created = await (0, fileHelpers_1.writeJsonIfMissing)(absolutePath, {
        module: moduleName,
        dependsOn: [],
        publicEntrypoints: [`src/modules/${moduleName}/index.ts`],
        ownedTables: [],
        exposedApis: [],
    });
    return {
        applied: created,
        filesCreated: created ? [contractPath] : [],
        filesModified: [],
        description: created
            ? `Created module contract file at ${contractPath}.`
            : `Module contract file already exists at ${contractPath}.`,
    };
}
async function previewDoc001(context) {
    const filePath = path.join(context.workspaceRoot, 'docs', 'adrs', 'adr-001-tenant-model.md');
    const exists = await (0, fileHelpers_1.pathExists)(filePath);
    return {
        filesCreated: exists ? [] : [relative(filePath, context.workspaceRoot)],
        filesModified: [],
        diffPreview: exists
            ? '(no changes)'
            : '+ create tenant model ADR template',
        description: 'Create tenant model ADR template if missing.',
    };
}
async function applyDoc001(context) {
    const filePath = path.join(context.workspaceRoot, 'docs', 'adrs', 'adr-001-tenant-model.md');
    const created = await (0, fileHelpers_1.createFileIfMissing)(filePath, (0, adr_1.renderAdrTemplate)(adr_1.canonicalAdrTemplate, {
        adrId: 'ADR-001',
        title: 'Tenant Model',
        status: 'Accepted',
    })
        .replace('Describe the architectural problem or constraint that requires a decision.', 'Document tenant isolation model and constraints.')
        .replace('Clearly describe the chosen approach.', 'The system uses same database with separate schemas per tenant.')
        .replace('Explain the implications of the decision, including:\n- benefits\n- trade-offs\n- operational impact\n- potential future reconsiderations.', '- Simpler operational model than separate databases.\n- Requires strict migration and schema governance.'));
    return {
        applied: created,
        filesCreated: created ? [relative(filePath, context.workspaceRoot)] : [],
        filesModified: [],
        description: created ? 'Created tenant model ADR template.' : 'Tenant model ADR already exists.',
    };
}
async function previewDoc002(context) {
    const filePath = path.join(context.workspaceRoot, 'docs', 'adrs', 'adr-rbac-model.md');
    const exists = await (0, fileHelpers_1.pathExists)(filePath);
    return {
        filesCreated: exists ? [] : [relative(filePath, context.workspaceRoot)],
        filesModified: [],
        diffPreview: exists ? '(no changes)' : '+ create RBAC ADR template',
        description: 'Create an RBAC ADR template if missing.',
    };
}
async function applyDoc002(context) {
    const filePath = path.join(context.workspaceRoot, 'docs', 'adrs', 'adr-rbac-model.md');
    const created = await (0, fileHelpers_1.createFileIfMissing)(filePath, (0, adr_1.renderAdrTemplate)(adr_1.canonicalAdrTemplate, {
        adrId: 'ADR-XXX',
        title: 'RBAC Model',
    })
        .replace('Describe the architectural problem or constraint that requires a decision.', 'Document the RBAC model used in the system.')
        .replace('Clearly describe the chosen approach.', 'Describe roles, permissions, and scope inheritance.')
        .replace('Explain the implications of the decision, including:\n- benefits\n- trade-offs\n- operational impact\n- potential future reconsiderations.', 'Explain how RBAC will be enforced.'));
    return {
        applied: created,
        filesCreated: created ? [relative(filePath, context.workspaceRoot)] : [],
        filesModified: [],
        description: created
            ? 'Created docs/adrs/adr-rbac-model.md.'
            : 'docs/adrs/adr-rbac-model.md already exists.',
    };
}
async function previewArch004(context) {
    const parsed = parseModulesFromArch004Message(context.finding.message);
    if (!parsed) {
        return {
            filesCreated: [],
            filesModified: [],
            diffPreview: '(no changes)',
            description: 'Unable to parse AP-ARCH-004 finding details safely. No files would be changed.',
        };
    }
    const filesCreated = [];
    for (const moduleFinding of parsed.modules) {
        if (moduleFinding.missing.includes('readme.md')) {
            filesCreated.push(`${parsed.modulesRoot}/${moduleFinding.module}/README.md`);
        }
        if (moduleFinding.missing.includes('index.ts')) {
            filesCreated.push(`${parsed.modulesRoot}/${moduleFinding.module}/index.ts`);
        }
    }
    return {
        filesCreated,
        filesModified: [],
        diffPreview: filesCreated.length === 0 ? '(no changes)' : `+ create ${filesCreated.join(', ')}`,
        description: 'Create missing module baseline files.',
    };
}
async function applyArch004(context) {
    const parsed = parseModulesFromArch004Message(context.finding.message);
    if (!parsed) {
        return {
            applied: false,
            filesCreated: [],
            filesModified: [],
            description: 'Skipped: unable to parse AP-ARCH-004 finding details safely.',
        };
    }
    const filesCreated = [];
    for (const moduleFinding of parsed.modules) {
        const moduleRoot = path.join(context.workspaceRoot, ...parsed.modulesRoot.split('/'), moduleFinding.module);
        if (moduleFinding.missing.includes('readme.md')) {
            const readmePath = path.join(moduleRoot, 'README.md');
            const created = await (0, fileHelpers_1.createFileIfMissing)(readmePath, `# ${moduleFinding.module}\n\nModule overview.\n`);
            if (created) {
                filesCreated.push(relative(readmePath, context.workspaceRoot));
            }
        }
        if (moduleFinding.missing.includes('index.ts')) {
            const indexPath = path.join(moduleRoot, 'index.ts');
            const created = await (0, fileHelpers_1.createFileIfMissing)(indexPath, 'export {};\n');
            if (created) {
                filesCreated.push(relative(indexPath, context.workspaceRoot));
            }
        }
    }
    return {
        applied: filesCreated.length > 0,
        filesCreated,
        filesModified: [],
        description: filesCreated.length > 0 ? 'Created missing module baseline files.' : 'No missing module baseline files remained.',
    };
}
async function previewDep006(context) {
    const parsed = parseDep006Message(context.finding.message);
    if (!parsed) {
        return {
            filesCreated: [],
            filesModified: [],
            diffPreview: '(no changes)',
            description: 'Unable to parse dependency removal from finding.',
        };
    }
    const contractPath = `.archpilot/contracts/${parsed.sourceModule}.contract.json`;
    return {
        filesCreated: [],
        filesModified: [contractPath],
        diffPreview: `- remove "${parsed.dependency}" from dependsOn in ${contractPath}`,
        description: 'Remove unused declared dependency from module contract.',
    };
}
async function applyDep006(context) {
    const parsed = parseDep006Message(context.finding.message);
    if (!parsed) {
        return {
            applied: false,
            filesCreated: [],
            filesModified: [],
            description: 'Skipped: unable to parse dependency removal from finding.',
        };
    }
    const contractPath = path.join(context.workspaceRoot, '.archpilot', 'contracts', `${parsed.sourceModule}.contract.json`);
    if (!(await (0, fileHelpers_1.pathExists)(contractPath))) {
        return {
            applied: false,
            filesCreated: [],
            filesModified: [],
            description: 'Skipped: source module contract file is missing.',
        };
    }
    const content = await node_fs_1.promises.readFile(contractPath, {
        encoding: 'utf8',
    });
    const parsedJson = JSON.parse(content);
    if (!Array.isArray(parsedJson.dependsOn)) {
        return {
            applied: false,
            filesCreated: [],
            filesModified: [],
            description: 'Skipped: contract dependsOn is not an array.',
        };
    }
    const original = parsedJson.dependsOn.filter((entry) => typeof entry === 'string');
    const next = original.filter((entry) => entry !== parsed.dependency);
    if (next.length === original.length) {
        return {
            applied: false,
            filesCreated: [],
            filesModified: [],
            description: 'No change: declared dependency was already absent.',
        };
    }
    parsedJson.dependsOn = next;
    await node_fs_1.promises.writeFile(contractPath, `${JSON.stringify(parsedJson, null, 2)}\n`, { encoding: 'utf8' });
    return {
        applied: true,
        filesCreated: [],
        filesModified: [relative(contractPath, context.workspaceRoot)],
        description: `Removed unused dependency '${parsed.dependency}' from module contract.`,
    };
}
const handlers = [
    {
        ruleId: 'AP-ARCH-001',
        fixId: 'create-modules-root',
        title: 'Create required modular structure folders',
        description: 'Creates the baseline modular folder structure under src/modules.',
        isSafeFix: true,
        previewFix: previewArch001,
        applyFix: applyArch001,
    },
    {
        ruleId: 'AP-ARCH-003',
        fixId: 'create-architecture-overview',
        title: 'Create architecture overview document',
        description: 'Creates docs/architecture/overview.md with a starter architecture template.',
        isSafeFix: true,
        previewFix: previewArch003,
        applyFix: applyArch003,
    },
    {
        ruleId: 'AP-ARCH-004',
        fixId: 'create-missing-module-baseline-files',
        title: 'Create missing module baseline files',
        description: 'Creates missing README.md and index.ts files for modules.',
        isSafeFix: true,
        previewFix: previewArch004,
        applyFix: applyArch004,
    },
    {
        ruleId: 'AP-ARCH-005',
        fixId: 'create-missing-module-path',
        title: 'Create missing module path',
        description: 'Creates missing module directory referenced in architecture registry.',
        isSafeFix: true,
        previewFix: previewArch005,
        applyFix: applyArch005,
    },
    {
        ruleId: 'AP-ARCH-006',
        fixId: 'create-missing-module-contract',
        title: 'Create missing module contract file',
        description: 'Creates a minimal module contract JSON file when missing.',
        isSafeFix: true,
        previewFix: previewArch006,
        applyFix: applyArch006,
    },
    {
        ruleId: 'AP-API-001',
        fixId: 'create-openapi-stub',
        title: 'Create missing OpenAPI specification file',
        description: 'Creates contracts/openapi.yaml with a minimal OpenAPI document.',
        isSafeFix: true,
        previewFix: previewApi001,
        applyFix: applyApi001,
    },
    {
        ruleId: 'AP-DOC-001',
        fixId: 'create-tenant-adr-template',
        title: 'Create tenant model ADR template',
        description: 'Creates docs/adrs/adr-001-tenant-model.md if missing.',
        isSafeFix: true,
        previewFix: previewDoc001,
        applyFix: applyDoc001,
    },
    {
        ruleId: 'AP-DOC-002',
        fixId: 'create-rbac-adr-template',
        title: 'Create RBAC ADR template',
        description: 'Creates docs/adrs/adr-rbac-model.md if missing.',
        isSafeFix: true,
        previewFix: previewDoc002,
        applyFix: applyDoc002,
    },
    {
        ruleId: 'AP-DEP-006',
        fixId: 'remove-unused-declared-dependency',
        title: 'Remove unused declared module dependency',
        description: 'Removes an unused dependency from module contract dependsOn array.',
        isSafeFix: false,
        previewFix: previewDep006,
        applyFix: applyDep006,
    },
];
const handlersByRuleId = handlers.reduce((accumulator, handler) => {
    const existing = accumulator.get(handler.ruleId) ?? [];
    existing.push(handler);
    accumulator.set(handler.ruleId, existing);
    return accumulator;
}, new Map());
for (const [ruleId, ruleHandlers] of handlersByRuleId.entries()) {
    handlersByRuleId.set(ruleId, [...ruleHandlers].sort((left, right) => left.fixId.localeCompare(right.fixId)));
}
function listRegisteredFixHandlers() {
    return [...handlers].sort((left, right) => {
        const ruleCompare = left.ruleId.localeCompare(right.ruleId);
        if (ruleCompare !== 0) {
            return ruleCompare;
        }
        return left.fixId.localeCompare(right.fixId);
    });
}
function getFixHandlersForRule(ruleId) {
    return [...(handlersByRuleId.get(ruleId) ?? [])];
}
function hasFixHandlerForRule(ruleId) {
    return getFixHandlersForRule(ruleId).length > 0;
}
//# sourceMappingURL=fixRegistry.js.map