"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.buildRequiredAdrScaffoldSuggestions = buildRequiredAdrScaffoldSuggestions;
exports.generateRequiredAdrScaffolds = generateRequiredAdrScaffolds;
const path = __importStar(require("node:path"));
const node_fs_1 = require("node:fs");
const architectureInit_1 = require("./architectureInit");
const adrManagement_1 = require("./adrManagement");
function normalizeText(value) {
    return value.trim().toLowerCase();
}
function slugifyTitle(title) {
    const normalized = title
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/^-+|-+$/g, '');
    return normalized.length > 0 ? normalized : 'decision';
}
async function pathExists(targetPath) {
    try {
        await node_fs_1.promises.access(targetPath);
        return true;
    }
    catch {
        return false;
    }
}
async function readValidationStatusLike(workspaceRoot) {
    try {
        const raw = await node_fs_1.promises.readFile(path.join(workspaceRoot, '.archpilot', 'validation-status.json'), {
            encoding: 'utf8',
        });
        const parsed = JSON.parse(raw);
        return parsed && typeof parsed === 'object' ? parsed : undefined;
    }
    catch {
        return undefined;
    }
}
function includesAny(value, candidates) {
    return candidates.some((candidate) => value.includes(candidate));
}
function moduleNameFromIsolatedFinding(message) {
    const match = message.match(/module\s+'([^']+)'/iu);
    return match && match[1] ? match[1].trim() : undefined;
}
function adrCoversKeywords(existing, keywords) {
    return existing.some((record) => {
        const haystack = normalizeText(`${record.title} ${record.fileName}`);
        return keywords.some((keyword) => haystack.includes(normalizeText(keyword)));
    });
}
function uniqueSuggestions(suggestions) {
    const seen = new Set();
    const deduped = [];
    for (const suggestion of suggestions) {
        if (seen.has(suggestion.topicKey)) {
            continue;
        }
        seen.add(suggestion.topicKey);
        deduped.push(suggestion);
    }
    return deduped;
}
function renderAdrScaffoldContent(input) {
    return [
        `# ${input.adrId}: ${input.title}`,
        '',
        'Status: Proposed',
        '',
        '## Context',
        ...input.contextLines.map((line) => `- ${line}`),
        '',
        '## Decision',
        ...input.decisionLines.map((line) => `- ${line}`),
        '',
        '## Consequences',
        ...input.consequenceLines.map((line) => `- ${line}`),
        '',
    ].join('\n');
}
async function buildScaffoldContent(workspaceRoot, suggestion, index) {
    const contract = await (0, architectureInit_1.loadArchitectureContract)(workspaceRoot).catch(() => undefined);
    const dependencyRulesPresent = await pathExists(path.join(workspaceRoot, '.archpilot', 'dependency-rules.json'));
    const layerRulesPresent = await pathExists(path.join(workspaceRoot, '.archpilot', 'layer-rules.json'));
    const adrId = `ADR-${String(index).padStart(3, '0')}`;
    const contextLines = [
        'ArchPilot validation indicates ADR coverage is needed for this architecture decision.',
        suggestion.reason,
    ];
    const decisionLines = [];
    const consequenceLines = [
        'This ADR will give future validation reviews and pull requests an explicit architectural rationale to reference.',
        'Teams can use it as the baseline for deciding whether future changes preserve or intentionally update this architecture decision.',
    ];
    if (contract?.architectureStyle) {
        contextLines.push(`Current architecture style is '${contract.architectureStyle}'.`);
    }
    if (contract?.apiStyle && contract.apiStyle !== 'none') {
        contextLines.push(`Current API style is '${contract.apiStyle}'.`);
    }
    if (contract?.tenantModel) {
        contextLines.push(`Current tenant model is '${contract.tenantModel}'.`);
    }
    if (dependencyRulesPresent) {
        contextLines.push('Dependency governance rules are already present in .archpilot/dependency-rules.json.');
    }
    if (layerRulesPresent) {
        contextLines.push('Layer rules are already present in .archpilot/layer-rules.json.');
    }
    switch (suggestion.topicKey) {
        case 'architecture-style':
            decisionLines.push(`Document why '${contract?.architectureStyle ?? 'the selected architecture style'}' is the intended system shape.`);
            decisionLines.push('Record the deployment, module boundary, and operational assumptions that follow from that choice.');
            consequenceLines.push('Architecture reviews can distinguish intentional modular-monolith decisions from accidental service sprawl.');
            break;
        case 'api-style':
            decisionLines.push(`Document why '${contract?.apiStyle ?? 'the selected API style'}' is the chosen public contract style for this repository.`);
            decisionLines.push('Capture how API contracts should be documented, versioned, and reviewed.');
            consequenceLines.push('API-related changes can be evaluated against one explicit contract strategy.');
            break;
        case 'tenant-model':
            decisionLines.push(`Document why '${contract?.tenantModel ?? 'the selected tenant model'}' is the intended tenancy strategy.`);
            decisionLines.push('Capture the isolation, operational, and migration trade-offs that come with the tenancy approach.');
            consequenceLines.push('Multi-tenant changes can be reviewed against a clear data-isolation decision.');
            break;
        case 'dependency-governance':
            decisionLines.push('Document the intended dependency governance model between modules and shared packages.');
            decisionLines.push('Clarify how dependency rules, layer rules, and module contracts should be kept in sync.');
            consequenceLines.push('Cross-module dependency findings become easier to resolve consistently.');
            break;
        case 'public-entrypoints':
            decisionLines.push('Document how modules should expose public entrypoints and how internal files remain private implementation details.');
            decisionLines.push('Clarify when imports must go through public surfaces versus internal paths.');
            consequenceLines.push('Public surface discipline becomes reviewable across module contract and import changes.');
            break;
        case 'shared-infrastructure':
            decisionLines.push('Document the role of shared/platform infrastructure modules and the boundaries they should preserve.');
            decisionLines.push('Clarify which responsibilities belong in shared infrastructure versus feature modules.');
            consequenceLines.push('Shared modules are less likely to become accidental god modules.');
            break;
        default:
            if (suggestion.topicKey.startsWith('isolated-module:')) {
                const moduleName = suggestion.topicKey.slice('isolated-module:'.length);
                decisionLines.push(`Document whether module '${moduleName}' is intentionally isolated or should be integrated into broader module dependencies.`);
                decisionLines.push('Clarify the reasons for keeping the module detached, or the expected dependencies if that isolation is temporary.');
                consequenceLines.push(`Future reviews can tell whether isolation of '${moduleName}' is intentional governance or an incomplete integration.`);
            }
            else {
                decisionLines.push('Document the architectural decision and the reasons it is needed in the current repository context.');
            }
            break;
    }
    return renderAdrScaffoldContent({
        adrId,
        title: suggestion.title,
        contextLines,
        decisionLines,
        consequenceLines,
    });
}
async function buildRequiredAdrScaffoldSuggestions(workspaceRoot, options) {
    const [validationStatus, contract, existingAdrs, dependencyRulesPresent, layerRulesPresent] = await Promise.all([
        options?.validationStatus ?? readValidationStatusLike(workspaceRoot),
        (0, architectureInit_1.loadArchitectureContract)(workspaceRoot).catch(() => undefined),
        (0, adrManagement_1.listAdrs)(workspaceRoot),
        pathExists(path.join(workspaceRoot, '.archpilot', 'dependency-rules.json')),
        pathExists(path.join(workspaceRoot, '.archpilot', 'layer-rules.json')),
    ]);
    const failedFindings = (validationStatus?.results ?? []).filter((result) => result?.passed === false);
    const failedIds = new Set(failedFindings
        .map((finding) => (typeof finding.id === 'string' ? finding.id : ''))
        .filter((id) => id.length > 0));
    const hasAdrCoverageFinding = failedIds.has('AP-ADR-004') ||
        failedIds.has('AP-SAFE-001') ||
        failedIds.has('AP-ADR-001') ||
        failedIds.has('AP-ADR-002') ||
        failedIds.has('AP-ADR-003');
    const suggestions = [];
    const maybeAdd = (suggestion) => {
        if (!adrCoversKeywords(existingAdrs, suggestion.keywords)) {
            suggestions.push(suggestion);
        }
    };
    if (contract?.architectureStyle && hasAdrCoverageFinding) {
        maybeAdd({
            topicKey: 'architecture-style',
            title: contract.architectureStyle === 'microservices'
                ? 'Document microservices architecture decision'
                : 'Document modular monolith architecture decision',
            reason: `Current architecture configuration selects '${contract.architectureStyle}'.`,
            keywords: ['architecture decision', 'modular monolith', 'microservices', 'architecture style'],
        });
    }
    if (contract?.apiStyle && contract.apiStyle !== 'none' && hasAdrCoverageFinding) {
        maybeAdd({
            topicKey: 'api-style',
            title: `Document ${contract.apiStyle.toUpperCase()} API style decision`,
            reason: `Current architecture configuration selects '${contract.apiStyle}' as the API style.`,
            keywords: ['api style', 'rest api', 'graphql api', 'api contract'],
        });
    }
    if (contract?.tenantModel && (contract.tenantModel !== 'single_tenant' || hasAdrCoverageFinding)) {
        maybeAdd({
            topicKey: 'tenant-model',
            title: 'Document tenant model decision',
            reason: `Current architecture configuration selects '${contract.tenantModel}' as the tenant model.`,
            keywords: ['tenant model', 'multi tenant', 'schema per tenant', 'tenant isolation'],
        });
    }
    if (dependencyRulesPresent || layerRulesPresent || [...failedIds].some((id) => id.startsWith('AP-DEP-')) || hasAdrCoverageFinding) {
        maybeAdd({
            topicKey: 'dependency-governance',
            title: 'Document dependency governance and module boundary enforcement',
            reason: 'Current validation findings and config indicate module dependency governance is an active architecture concern.',
            keywords: ['dependency governance', 'module boundary', 'module boundaries', 'dependency rules', 'layer rules'],
        });
    }
    if (failedIds.has('AP-ARCH-004') ||
        failedIds.has('AP-ARCH-008') ||
        failedIds.has('AP-DEP-003') ||
        failedIds.has('AP-DEP-005')) {
        maybeAdd({
            topicKey: 'public-entrypoints',
            title: 'Document module public entrypoint discipline',
            reason: 'Validation findings indicate that public module surfaces and entrypoints need explicit governance.',
            keywords: ['public entrypoint', 'public surface', 'module public surface', 'entrypoint discipline'],
        });
    }
    if ([...(contract ? Object.keys(contract.modules ?? {}) : [])].some((moduleName) => includesAny(moduleName.toLowerCase(), ['shared', 'platform', 'infra', 'config'])) ||
        failedIds.has('AP-DEP-009')) {
        maybeAdd({
            topicKey: 'shared-infrastructure',
            title: 'Document shared platform and infrastructure module boundaries',
            reason: 'Current repository structure suggests shared/platform modules that need explicit responsibility boundaries.',
            keywords: ['shared platform', 'shared infrastructure', 'platform boundaries', 'infrastructure module'],
        });
    }
    for (const finding of failedFindings) {
        if (finding.id !== 'AP-DEP-007' || typeof finding.message !== 'string') {
            continue;
        }
        const isolatedModuleName = moduleNameFromIsolatedFinding(finding.message);
        if (!isolatedModuleName) {
            continue;
        }
        maybeAdd({
            topicKey: `isolated-module:${isolatedModuleName}`,
            title: `Document intentional isolation of module ${isolatedModuleName}`,
            reason: `Validation flagged module '${isolatedModuleName}' as isolated.`,
            keywords: ['isolated module', isolatedModuleName],
        });
    }
    return uniqueSuggestions(suggestions);
}
async function generateRequiredAdrScaffolds(workspaceRoot, suggestions) {
    const existingAdrs = await (0, adrManagement_1.listAdrs)(workspaceRoot);
    const adrDirectory = await (0, adrManagement_1.getAdrDirectoryPath)(workspaceRoot);
    await node_fs_1.promises.mkdir(adrDirectory, { recursive: true });
    const created = [];
    const skippedExisting = [];
    let nextId = (existingAdrs[existingAdrs.length - 1]?.id ?? 0) + 1;
    const simulatedExisting = [...existingAdrs];
    for (const suggestion of suggestions) {
        if (adrCoversKeywords(simulatedExisting, suggestion.keywords)) {
            skippedExisting.push(suggestion.title);
            continue;
        }
        const fileName = `adr-${String(nextId).padStart(3, '0')}-${slugifyTitle(suggestion.title)}.md`;
        const filePath = path.join(adrDirectory, fileName);
        const content = await buildScaffoldContent(workspaceRoot, suggestion, nextId);
        await node_fs_1.promises.writeFile(filePath, content, { encoding: 'utf8' });
        created.push({
            title: suggestion.title,
            fileName,
            filePath,
        });
        simulatedExisting.push({
            id: nextId,
            idLabel: `ADR-${String(nextId).padStart(3, '0')}`,
            fileName,
            filePath,
            title: suggestion.title,
        });
        nextId += 1;
    }
    return {
        created,
        skippedExisting,
    };
}
//# sourceMappingURL=adrScaffoldGeneration.js.map