/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
export const PHASE_REGISTRY = [
    {
        id: 'pm',
        profile: 'pm',
        profiles: ['pm'],
        label: 'Product Management',
        order: 10,
        alwaysIncluded: false,
        triggeredByProfiles: ['pm'],
        gatesAfter: [],
        deliversTo: [],
        validatesWith: [],
        playbooks: ['sprint-plan.md', 'status-report.md', 'risk-register.md', 'delivery-metrics.md'],
    },
    {
        id: 'ba',
        profile: 'ba',
        profiles: ['ba'],
        label: 'Business Analysis',
        order: 20,
        alwaysIncluded: false,
        triggeredByProfiles: ['ba'],
        gatesAfter: ['architect'],
        deliversTo: [],
        validatesWith: [],
        playbooks: ['refine-stories.md', 'groom-backlog.md', 'qa-plan.md', 'update-jira-ticket.md'],
    },
    {
        id: 'architect',
        profile: 'ta',
        profiles: ['sa', 'ta'],
        label: 'Architecture',
        order: 30,
        alwaysIncluded: false,
        triggeredByProfiles: ['ta', 'sa'],
        gatesAfter: [],
        deliversTo: [],
        validatesWith: [],
        playbooks: [
            'scope-analysis.md',
            'solution-design.md',
            'adr.md',
            'architecture-review.md',
            'data-model-design.md',
            'update-jira-ticket.md',
        ],
    },
    {
        id: 'ux',
        profile: 'ux',
        profiles: ['ux'],
        label: 'UX Design',
        order: 35,
        alwaysIncluded: false,
        triggeredByProfiles: ['ux'],
        gatesAfter: [],
        deliversTo: ['ba', 'architect'],
        validatesWith: ['qa'],
        playbooks: ['design-review.md', 'wireframe.md', 'a11y-audit.md'],
    },
    {
        id: 'developer',
        profile: 'developer',
        profiles: ['developer'],
        label: 'Development',
        order: 50,
        alwaysIncluded: true,
        triggeredByProfiles: [],
        gatesAfter: [],
        deliversTo: [],
        validatesWith: [],
        playbooks: [
            'create-apex-class.md',
            'create-trigger.md',
            'create-lwc.md',
            'declare-story-points.md',
            'update-jira-ticket.md',
        ],
    },
    {
        id: 'security-review',
        profile: 'security',
        profiles: ['security'],
        label: 'Security Review',
        order: 60,
        alwaysIncluded: false,
        triggeredByProfiles: ['security'],
        gatesAfter: [],
        deliversTo: [],
        validatesWith: [],
        playbooks: ['security-review.md', 'threat-model.md', 'env-audit.md'],
    },
    {
        id: 'qa',
        profile: 'qa',
        profiles: ['qa'],
        label: 'Quality Assurance',
        order: 70,
        alwaysIncluded: true,
        triggeredByProfiles: [],
        gatesAfter: ['release'],
        deliversTo: [],
        validatesWith: [],
        playbooks: [
            'qa-checklist.md',
            'observable-assertions.md',
            'regression.md',
            'smoke-test.md',
            'run-playwright.md',
            'generate-test-report.md',
            'update-jira-ticket.md',
        ],
    },
    {
        id: 'release',
        profile: 'devops',
        profiles: ['devops', 'release-manager', 'sa', 'ta', 'security'],
        label: 'Release Management',
        order: 80,
        alwaysIncluded: true,
        triggeredByProfiles: [],
        gatesAfter: [],
        deliversTo: [],
        validatesWith: [],
        playbooks: ['release.md', 'candidate-release.md', 'validate.md', 'update-jira-ticket.md', 'doc-review.md'],
    },
];
const registryByPhase = new Map(PHASE_REGISTRY.map((p) => [p.id, p]));
export function getPhaseDefinition(phase) {
    const def = registryByPhase.get(phase);
    if (!def)
        throw new Error(`Unknown phase: ${phase}`);
    return def;
}
export function phaseToProfile(phase) {
    return getPhaseDefinition(phase).profile;
}
export function allPhaseIds() {
    return PHASE_REGISTRY.map((p) => p.id);
}
export function phasePlaybooks(phase) {
    return getPhaseDefinition(phase).playbooks;
}
export function resolvePhaseSequenceFromRoles(taskRoles, activeProfiles) {
    const included = new Set();
    for (const def of PHASE_REGISTRY) {
        if (def.alwaysIncluded) {
            included.add(def.id);
            continue;
        }
        if (isPhaseIncludedByRole(def, taskRoles)) {
            included.add(def.id);
        }
        else if (isPhaseTriggeredByProfile(def, activeProfiles)) {
            included.add(def.id);
        }
    }
    return PHASE_REGISTRY.filter((p) => included.has(p.id)).map((p) => p.id);
}
function isPhaseIncludedByRole(def, taskRoles) {
    if (!taskRoles)
        return false;
    if (taskRoles.required.includes(def.profile) || taskRoles.required.includes(def.id))
        return true;
    if (taskRoles.optional.includes(def.profile) || taskRoles.optional.includes(def.id))
        return true;
    if (def.profiles.some((p) => taskRoles.required.includes(p) || taskRoles.optional.includes(p)))
        return true;
    return false;
}
function isPhaseTriggeredByProfile(def, activeProfiles) {
    if (!activeProfiles)
        return false;
    return def.triggeredByProfiles.some((trigger) => activeProfiles.includes(trigger));
}
function resolveBaselineInclusion(taskRoles, activeProfiles) {
    const sources = new Map();
    const included = new Set();
    for (const def of PHASE_REGISTRY) {
        const source = classifyPhaseSource(def, taskRoles, activeProfiles);
        if (source) {
            included.add(def.id);
            sources.set(def.id, source);
        }
    }
    return { included, sources };
}
function classifyPhaseSource(def, taskRoles, activeProfiles) {
    if (def.alwaysIncluded)
        return 'always';
    if (taskRoles) {
        if (taskRoles.required.includes(def.profile) || taskRoles.required.includes(def.id))
            return 'role-required';
        if (def.profiles.some((p) => taskRoles.required.includes(p)))
            return 'role-required';
        if (taskRoles.optional.includes(def.profile) || taskRoles.optional.includes(def.id))
            return 'role-optional';
        if (def.profiles.some((p) => taskRoles.optional.includes(p)))
            return 'role-optional';
    }
    if (activeProfiles && isPhaseTriggeredByProfile(def, activeProfiles))
        return 'profile-trigger';
    return null;
}
function applyOverrides(included, sources, overrides) {
    const skipped = [];
    const gateOverrides = new Map();
    for (const override of overrides) {
        switch (override.action) {
            case 'add':
                included.add(override.phase);
                sources.set(override.phase, 'override-add');
                break;
            case 'skip':
                if (included.has(override.phase)) {
                    included.delete(override.phase);
                    skipped.push({ phase: override.phase, reason: override.reason ?? 'Skipped by phase override' });
                }
                break;
            case 'require-gate':
                gateOverrides.set(override.phase, 'require-gate');
                break;
            case 'remove-gate':
                gateOverrides.set(override.phase, 'remove-gate');
                break;
        }
    }
    return { skipped, gateOverrides };
}
function resolveGates(orderedPhases, gateOverrides) {
    const phaseSet = new Set(orderedPhases.map((p) => p.id));
    const gates = collectNaturalGates(orderedPhases, phaseSet);
    applyGateOverrides(gates, orderedPhases, gateOverrides);
    return gates;
}
function collectNaturalGates(orderedPhases, phaseSet) {
    const gates = new Set();
    for (const def of orderedPhases) {
        for (const target of def.gatesAfter) {
            if (phaseSet.has(target))
                gates.add(`${def.id}→${target}`);
        }
    }
    return gates;
}
function applyGateOverrides(gates, orderedPhases, gateOverrides) {
    for (const [phase, action] of gateOverrides) {
        if (action === 'require-gate') {
            const idx = orderedPhases.findIndex((p) => p.id === phase);
            if (idx >= 0 && idx < orderedPhases.length - 1) {
                gates.add(`${phase}→${orderedPhases[idx + 1].id}`);
            }
        }
        else {
            for (const g of gates) {
                if (g.startsWith(`${phase}→`))
                    gates.delete(g);
            }
        }
    }
}
export function buildPhasePlan(input) {
    const { taskRoles, activeProfiles, overrides = [], declaredPhases } = input;
    const { included, sources } = resolveBaselineInclusion(taskRoles, activeProfiles);
    if (declaredPhases && declaredPhases.length > 0) {
        for (const phase of declaredPhases) {
            if (!sources.has(phase)) {
                included.add(phase);
                sources.set(phase, 'declared');
            }
        }
    }
    const { skipped, gateOverrides } = applyOverrides(included, sources, overrides);
    const orderedPhases = PHASE_REGISTRY.filter((p) => included.has(p.id));
    const gates = resolveGates(orderedPhases, gateOverrides);
    const plan = orderedPhases.map((def) => ({
        phase: def.id,
        profile: def.profile,
        source: sources.get(def.id) ?? 'always',
        gateAfter: [...gates].some((g) => g.startsWith(`${def.id}→`)),
    }));
    return { included: plan, skipped, gates: [...gates] };
}
export function derivePhaseGates(phases) {
    const gates = new Set();
    const phaseSet = new Set(phases);
    for (const phase of phases) {
        const def = getPhaseDefinition(phase);
        for (const target of def.gatesAfter) {
            if (phaseSet.has(target)) {
                gates.add(`${phase}→${target}`);
            }
        }
        for (const target of def.deliversTo) {
            if (phaseSet.has(target)) {
                gates.add(`${phase}→${target}`);
            }
        }
    }
    return gates;
}
export function phaseDeliversTo(phase) {
    return getPhaseDefinition(phase).deliversTo;
}
export function phaseValidatesWith(phase) {
    return getPhaseDefinition(phase).validatesWith;
}
//# sourceMappingURL=registry.js.map