// engine/team-analytics-calculator.ts — Pure team analytics calculator (SPEC-297)
// Computes per-member metrics and workload distribution from Spec metadata.
// No storage writes — reads only.
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function getAssignee(spec) {
    const extended = spec;
    return typeof extended.assignee === 'string' && extended.assignee.trim().length > 0
        ? extended.assignee.trim()
        : 'unassigned';
}
function daysBetween(from, to) {
    const diff = new Date(to).getTime() - new Date(from).getTime();
    return Math.max(0, diff / (1000 * 60 * 60 * 24));
}
function buildWindowStart(weeks) {
    const d = new Date();
    d.setUTCDate(d.getUTCDate() - weeks * 7);
    return d;
}
// ---------------------------------------------------------------------------
// computeTeamAnalytics
// ---------------------------------------------------------------------------
/**
 * Computes per-member performance metrics over the last N weeks.
 * Pure function — no I/O.
 */
export function computeTeamAnalytics(specs, weeks) {
    const clampedWeeks = Math.min(Math.max(weeks, 1), 52);
    const windowStart = buildWindowStart(clampedWeeks);
    // Group specs by assignee
    const byMember = new Map();
    for (const spec of specs) {
        const name = getAssignee(spec);
        const group = byMember.get(name) ?? [];
        group.push(spec);
        byMember.set(name, group);
    }
    const members = [];
    for (const [name, memberSpecs] of byMember) {
        const doneInWindow = memberSpecs.filter((s) => s.status === 'done' && new Date(s.updatedAt) >= windowStart);
        const specsCompleted = doneInWindow.length;
        const avgLeadTimeDays = specsCompleted > 0
            ? doneInWindow.reduce((acc, s) => acc + daysBetween(s.createdAt, s.updatedAt), 0) /
                specsCompleted
            : 0;
        const currentWip = memberSpecs.filter((s) => s.status === 'implementing' || s.status === 'review').length;
        const totalAssigned = memberSpecs.length;
        const totalDoneAllTime = memberSpecs.filter((s) => s.status === 'done').length;
        const completionRate = totalAssigned > 0 ? totalDoneAllTime / totalAssigned : 0;
        const totalEstimatedHours = memberSpecs.reduce((acc, s) => acc + s.estimation.devHours, 0);
        members.push({
            name,
            specsCompleted,
            avgLeadTimeDays,
            currentWip,
            completionRate,
            totalEstimatedHours,
        });
    }
    const totalSpecs = specs.length;
    const totalCompleted = members.reduce((acc, m) => acc + m.specsCompleted, 0);
    const avgVelocity = clampedWeeks > 0 ? totalCompleted / clampedWeeks : 0;
    const topContributor = members.length > 0
        ? members.reduce((best, m) => (m.specsCompleted > best.specsCompleted ? m : best)).name
        : 'unassigned';
    const teamSummary = { totalSpecs, avgVelocity, topContributor };
    return { members, teamSummary, weeks: clampedWeeks };
}
// ---------------------------------------------------------------------------
// computeWorkloadDistribution
// ---------------------------------------------------------------------------
const WIP_STATUSES = new Set(['draft', 'review', 'implementing']);
/**
 * Computes current workload distribution across team members.
 * Flags imbalances when one member has WIP > 2× the team average.
 * Pure function — no I/O.
 */
export function computeWorkloadDistribution(specs) {
    // Group specs by assignee
    const byMember = new Map();
    for (const spec of specs) {
        const name = getAssignee(spec);
        const group = byMember.get(name) ?? [];
        group.push(spec);
        byMember.set(name, group);
    }
    const members = [];
    for (const [name, memberSpecs] of byMember) {
        const wip = memberSpecs.filter((s) => WIP_STATUSES.has(s.status)).length;
        const backlog = memberSpecs.filter((s) => s.status === 'approved').length;
        const estimatedHours = memberSpecs.reduce((acc, s) => acc + s.estimation.devHours, 0);
        members.push({ name, wip, backlog, estimatedHours });
    }
    // Detect imbalances: anyone with wip > 2 * avgWip is flagged
    const imbalanceWarnings = [];
    if (members.length > 0) {
        const totalWip = members.reduce((acc, m) => acc + m.wip, 0);
        const avgWip = totalWip / members.length;
        if (avgWip > 0) {
            for (const m of members) {
                if (m.wip > 2 * avgWip) {
                    imbalanceWarnings.push({
                        affectedMember: m.name,
                        message: `${m.name} has ${String(m.wip)} WIP items (avg: ${avgWip.toFixed(1)}) — consider redistributing work`,
                    });
                }
            }
        }
    }
    return { members, imbalanceWarnings };
}
//# sourceMappingURL=team-analytics-calculator.js.map