"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TRACKABLE_DAY_SHIFT_SHARED_ASSET_PATHS = exports.CANONICAL_DAY_SHIFT_LOCAL_IGNORE_RULES = exports.OPERATIONAL_DAY_SHIFT_IGNORE_RULES = exports.IGNORE_POLICY_VERSION = void 0;
exports.parseIgnoreFileRules = parseIgnoreFileRules;
exports.createIgnorePolicy = createIgnorePolicy;
exports.normalizeWorkspaceRelativePath = normalizeWorkspaceRelativePath;
exports.explainIgnorePolicy = explainIgnorePolicy;
exports.getCanonicalDayShiftLocalIgnoreEntries = getCanonicalDayShiftLocalIgnoreEntries;
exports.renderCanonicalIgnoreInstallContractText = renderCanonicalIgnoreInstallContractText;
exports.createIgnoreInstallPreview = createIgnoreInstallPreview;
exports.applyIgnoreInstallPreview = applyIgnoreInstallPreview;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const preview_normalization_1 = require("./preview-normalization");
__exportStar(require("./integration-adapters"), exports);
exports.IGNORE_POLICY_VERSION = '1';
exports.OPERATIONAL_DAY_SHIFT_IGNORE_RULES = Object.freeze([
    {
        path: '.day-shift/state/write.lock',
        rationale: 'The repository mutation lock is volatile operational state.',
    },
    {
        path: '.day-shift/state/transactions/',
        rationale: 'Transaction manifests, status, and staged content are volatile operational state.',
    },
]);
const DEFAULT_SCAN_RULES = [
    ...[
        '.git/',
        'node_modules/',
        'dist/',
        'build/',
        'coverage/',
        '.cache/',
        '.turbo/',
        '.next/',
        '.day-shift/runs/',
        '.day-shift/rehearsals/',
        'tmp/',
    ].map((pattern) => ({
        pattern,
        decision: 'exclude',
        rationale: 'Safe default excludes dependency, generated, cache, test-output, or disposable workspace trees.',
    })),
    ...exports.OPERATIONAL_DAY_SHIFT_IGNORE_RULES.map((rule) => ({
        pattern: rule.path,
        decision: 'exclude',
        rationale: rule.rationale,
    })),
];
function parseIgnoreFileRules(content) {
    return content
        .split(/\r?\n/)
        .map((line) => line.trim())
        .filter((line) => line && !line.startsWith('#'))
        .map((line) => ({
        pattern: line.startsWith('!') ? line.slice(1) : line,
        decision: line.startsWith('!') ? 'include' : 'exclude',
        rationale: 'Rule supplied by supported ignore-file evidence.',
    }));
}
function createIgnorePolicy(options) {
    const workspaceRoot = node_path_1.default.resolve(options.workspaceRoot);
    const sources = [
        { id: 'product-defaults', kind: 'default', rules: DEFAULT_SCAN_RULES },
        { id: 'workspace-configuration', kind: 'configuration', rules: options.configuredRules ?? [] },
        { id: 'ignore-file-evidence', kind: 'ignore-file', rules: options.ignoreFileRules ?? [] },
        { id: 'invocation-overrides', kind: 'invocation', rules: options.invocationRules ?? [] },
    ];
    return { version: exports.IGNORE_POLICY_VERSION, id: `ignore-policy-v${exports.IGNORE_POLICY_VERSION}`, workspaceRoot, sources };
}
function normalizeWorkspaceRelativePath(workspaceRoot, requestedPath) {
    if (!requestedPath.trim() || node_path_1.default.isAbsolute(requestedPath))
        throw new Error('Path must be a non-empty workspace-relative path.');
    const normalized = node_path_1.default.posix.normalize(requestedPath.replace(/\\/g, '/')).replace(/^\.\//, '');
    if (normalized === '..' || normalized.startsWith('../') || normalized.includes('/../'))
        throw new Error('Path escapes the effective workspace.');
    const resolved = node_path_1.default.resolve(workspaceRoot, normalized);
    const relative = node_path_1.default.relative(node_path_1.default.resolve(workspaceRoot), resolved);
    if (relative === '..' || relative.startsWith(`..${node_path_1.default.sep}`) || node_path_1.default.isAbsolute(relative))
        throw new Error('Path escapes the effective workspace.');
    return normalized;
}
function ruleMatches(pattern, candidate) {
    const clean = pattern.replace(/^\//, '').replace(/\\/g, '/');
    if (clean.endsWith('/')) {
        const segment = clean.slice(0, -1);
        return candidate === segment || candidate.startsWith(`${segment}/`) || candidate.includes(`/${segment}/`);
    }
    const escaped = clean
        .replace(/[.+^${}()|[\]\\]/g, '\\$&')
        .replace(/\*\*/g, '.*')
        .replace(/\*/g, '[^/]*')
        .replace(/\?/g, '[^/]');
    return new RegExp(`^(?:.*/)?${escaped}(?:/.*)?$`).test(candidate);
}
function explainIgnorePolicy(policy, requestedPath) {
    const base = {
        policyVersion: policy.version,
        policyId: policy.id,
        workspaceRoot: policy.workspaceRoot,
        requestedPath,
        contributingSources: policy.sources.map(({ id, kind, rules }) => ({ id, kind, ruleCount: rules.length })),
    };
    let normalizedPath;
    try {
        normalizedPath = normalizeWorkspaceRelativePath(policy.workspaceRoot, requestedPath);
    }
    catch (error) {
        return { ...base, decision: 'invalid', containmentFailure: error instanceof Error ? error.message : String(error) };
    }
    let winningRule;
    for (const source of policy.sources)
        for (const rule of source.rules)
            if (ruleMatches(rule.pattern, normalizedPath))
                winningRule = { ...rule, sourceId: source.id, sourceKind: source.kind };
    return { ...base, normalizedPath, decision: winningRule?.decision ?? 'include', winningRule };
}
exports.CANONICAL_DAY_SHIFT_LOCAL_IGNORE_RULES = Object.freeze([
    {
        path: '.day-shift/local.toml',
        rationale: 'local.toml stores local machine/worktree overrides.',
    },
    {
        path: '.day-shift/runs/',
        rationale: 'Managed lifecycle runs are local-only disposable evidence.',
    },
    {
        path: '.day-shift/rehearsals/',
        rationale: 'Managed repository rehearsals are local-only disposable workspaces.',
    },
    ...exports.OPERATIONAL_DAY_SHIFT_IGNORE_RULES,
]);
exports.TRACKABLE_DAY_SHIFT_SHARED_ASSET_PATHS = Object.freeze([
    '.day-shift/agents.md',
    '.day-shift/config.toml',
    '.day-shift/prompts/',
    '.day-shift/workflows/',
    '.day-shift/templates/',
    '.day-shift/specs/',
]);
function getCanonicalDayShiftLocalIgnoreEntries() {
    return exports.CANONICAL_DAY_SHIFT_LOCAL_IGNORE_RULES.map((rule) => rule.path);
}
function renderCanonicalIgnoreInstallContractText() {
    const lines = [
        'Canonical Day Shift ignore-install contract (v1)',
        '',
        'Local-only ignore entries:',
        ...exports.CANONICAL_DAY_SHIFT_LOCAL_IGNORE_RULES.map((rule) => `- ${rule.path} (${rule.rationale})`),
        '',
        'Shared Day Shift assets that remain trackable:',
        ...exports.TRACKABLE_DAY_SHIFT_SHARED_ASSET_PATHS.map((assetPath) => `- ${assetPath}`),
    ];
    return lines.join('\n');
}
function createIgnoreInstallPreview(repositoryRoot) {
    const gitignorePath = node_path_1.default.resolve(repositoryRoot, '.gitignore');
    const gitignoreExists = node_fs_1.default.existsSync(gitignorePath);
    const existingContent = gitignoreExists ? node_fs_1.default.readFileSync(gitignorePath, 'utf8') : '';
    const existingLines = existingContent.split(/\r?\n/).map((line) => line.replace(/\r$/, ''));
    const normalizedExistingLines = (0, preview_normalization_1.collectNormalizedIgnoreEntries)(existingLines);
    const canonicalEntries = getCanonicalDayShiftLocalIgnoreEntries();
    const existingCanonicalEntries = canonicalEntries.filter((entry) => normalizedExistingLines.has((0, preview_normalization_1.normalizeCanonicalEntryForComparison)(entry)));
    const proposedAdditions = canonicalEntries.filter((entry) => !normalizedExistingLines.has((0, preview_normalization_1.normalizeCanonicalEntryForComparison)(entry)));
    let outcome = 'already-installed';
    if (proposedAdditions.length > 0) {
        outcome = gitignoreExists ? 'append' : 'create-file';
    }
    return {
        repositoryRoot,
        gitignorePath,
        gitignoreExists,
        canonicalEntries,
        existingCanonicalEntries,
        proposedAdditions,
        outcome,
    };
}
function applyIgnoreInstallPreview(preview) {
    if (preview.proposedAdditions.length === 0) {
        return {
            changed: false,
            action: 'none',
            writtenEntries: [],
        };
    }
    if (!preview.gitignoreExists) {
        node_fs_1.default.writeFileSync(preview.gitignorePath, `${preview.proposedAdditions.join('\n')}\n`, 'utf8');
        return {
            changed: true,
            action: 'create-file',
            writtenEntries: preview.proposedAdditions,
        };
    }
    const existingContent = node_fs_1.default.readFileSync(preview.gitignorePath, 'utf8');
    const separator = existingContent.length > 0 && !existingContent.endsWith('\n') ? '\n' : '';
    const nextContent = `${existingContent}${separator}${preview.proposedAdditions.join('\n')}\n`;
    node_fs_1.default.writeFileSync(preview.gitignorePath, nextContent, 'utf8');
    return {
        changed: true,
        action: 'append',
        writtenEntries: preview.proposedAdditions,
    };
}
