import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
import { join, dirname } from 'node:path';
/**
 * Get the stats file path for a project
 */
function getStatsPath(projectPath) {
    return join(projectPath, '.claude', 'reviews', 'stats.json');
}
function hasProperty(object, key) {
    return key in object;
}
function isProjectStatsShape(value) {
    if (typeof value !== 'object' || value === null)
        return false;
    return (hasProperty(value, 'totalReviews') && typeof value.totalReviews === 'number' &&
        hasProperty(value, 'totalDuration') && typeof value.totalDuration === 'number' &&
        hasProperty(value, 'lastUpdated') && typeof value.lastUpdated === 'string');
}
/**
 * Load project statistics
 */
export function loadProjectStats(projectPath) {
    const statsPath = getStatsPath(projectPath);
    if (!existsSync(statsPath)) {
        return createEmptyStats();
    }
    try {
        const content = readFileSync(statsPath, 'utf-8');
        const parsed = JSON.parse(content);
        if (!isProjectStatsShape(parsed)) {
            return createEmptyStats();
        }
        if (!Array.isArray(parsed.reviews)) {
            parsed.reviews = [];
        }
        return parsed;
    }
    catch {
        return createEmptyStats();
    }
}
/**
 * Save project statistics
 */
export function saveProjectStats(projectPath, stats) {
    const statsPath = getStatsPath(projectPath);
    // Ensure directory exists
    const dir = dirname(statsPath);
    if (!existsSync(dir)) {
        mkdirSync(dir, { recursive: true });
    }
    stats.lastUpdated = new Date().toISOString();
    writeFileSync(statsPath, JSON.stringify(stats, null, 2), 'utf-8');
}
/**
 * Create empty stats object
 */
function createEmptyStats() {
    return {
        totalReviews: 0,
        totalDuration: 0,
        averageScore: null,
        averageDuration: 0,
        totalBlocking: 0,
        totalWarnings: 0,
        totalAdditions: 0,
        totalDeletions: 0,
        averageAdditions: null,
        averageDeletions: null,
        reviews: [],
        lastUpdated: new Date().toISOString(),
    };
}
/**
 * Parse review output to extract statistics
 *
 * Supports two formats:
 * 1. Summary format (from skill output):
 *    Score global : X/10
 *    Bloquants : X
 *    Importants : X
 *
 * 2. Structured stats line:
 *    [REVIEW_STATS:blocking=X:warnings=X:suggestions=X:score=X]
 *
 * 3. Inline markers (fallback):
 *    [BLOQUANT], [IMPORTANT], [SUGGESTION]
 */
export function parseReviewOutput(stdout) {
    let score = null;
    let blocking = 0;
    let warnings = 0;
    let suggestions = 0;
    // Method 1: Parse structured stats line (most reliable)
    // Format: [REVIEW_STATS:blocking=1:warnings=2:suggestions=3:score=7.5]
    const statsLineMatch = stdout.match(/\[REVIEW_STATS:([^\]]+)\]/i);
    if (statsLineMatch) {
        const statsStr = statsLineMatch[1];
        const blockingMatch = statsStr.match(/blocking=(\d+)/);
        const warningsMatch = statsStr.match(/warnings=(\d+)/);
        const suggestionsMatch = statsStr.match(/suggestions=(\d+)/);
        const scoreMatch = statsStr.match(/score=(\d+(?:\.\d+)?)/);
        if (blockingMatch)
            blocking = Number.parseInt(blockingMatch[1], 10);
        if (warningsMatch)
            warnings = Number.parseInt(warningsMatch[1], 10);
        if (suggestionsMatch)
            suggestions = Number.parseInt(suggestionsMatch[1], 10);
        if (scoreMatch)
            score = Number.parseFloat(scoreMatch[1]);
        return { score, blocking, warnings, suggestions };
    }
    // Method 2: Parse summary format (skill output)
    const scoreMatch = stdout.match(/Score\s+[Gg]lobal\s*:\s*(\d+(?:\.\d+)?)\s*\/\s*10/i);
    if (scoreMatch) {
        score = Number.parseFloat(scoreMatch[1]);
    }
    const blockingSummary = stdout.match(/🚨\s*Bloquants?\s*:\s*(\d+)/i);
    if (blockingSummary) {
        blocking = Number.parseInt(blockingSummary[1], 10);
    }
    const warningsSummary = stdout.match(/⚠️\s*Importants?\s*:\s*(\d+)/i);
    if (warningsSummary) {
        warnings = Number.parseInt(warningsSummary[1], 10);
    }
    const suggestionsSummary = stdout.match(/(?:📝|💡)\s*(?:Améliorations?|Suggestions?)[^:]*:\s*(\d+)/i);
    if (suggestionsSummary) {
        suggestions = Number.parseInt(suggestionsSummary[1], 10);
    }
    // If summary format worked, return
    if (blockingSummary || warningsSummary || suggestionsSummary) {
        return { score, blocking, warnings, suggestions };
    }
    // Method 3: Fallback - count inline markers
    const blockingMatches = stdout.match(/🚨\s*\*?\*?\[BLOQUANT\]/gi);
    if (blockingMatches) {
        blocking = blockingMatches.length;
    }
    const blockingSection = stdout.match(/##\s+Corrections?\s+Bloquantes?[\s\S]*?(?=##\s|$)/i);
    if (blockingSection) {
        const blockingHeaders = blockingSection[0].match(/^###\s+\d+\./gm);
        if (blockingHeaders && blockingHeaders.length > blocking) {
            blocking = blockingHeaders.length;
        }
    }
    const warningMatches = stdout.match(/⚠️\s*\*?\*?\[IMPORTANT\]/gi);
    if (warningMatches) {
        warnings = warningMatches.length;
    }
    const warningSection = stdout.match(/##\s+Corrections?\s+Importantes?[\s\S]*?(?=##\s|$)/i);
    if (warningSection) {
        const warningHeaders = warningSection[0].match(/^###\s+\d+\./gm);
        if (warningHeaders && warningHeaders.length > warnings) {
            warnings = warningHeaders.length;
        }
    }
    const suggestionMatches = stdout.match(/💡\s*\*?\*?\[SUGGESTION\]/gi);
    if (suggestionMatches) {
        suggestions = suggestionMatches.length;
    }
    const suggestionSection = stdout.match(/##\s+Suggestions?[\s\S]*?(?=##\s|$)/i);
    if (suggestionSection) {
        const suggestionHeaders = suggestionSection[0].match(/^###\s+\d+\./gm);
        if (suggestionHeaders && suggestionHeaders.length > suggestions) {
            suggestions = suggestionHeaders.length;
        }
    }
    return { score, blocking, warnings, suggestions };
}
/**
 * Add a review to project statistics
 */
export function addReviewStats(projectPath, mrNumber, duration, stdout, assignedBy, diffStats) {
    const stats = loadProjectStats(projectPath);
    const parsed = parseReviewOutput(stdout);
    const now = new Date();
    const reviewStats = {
        id: `${now.getTime()}-${mrNumber}`,
        timestamp: now.toISOString(),
        mrNumber,
        duration,
        score: parsed.score,
        blocking: parsed.blocking,
        warnings: parsed.warnings,
        suggestions: parsed.suggestions,
        assignedBy,
        diffStats: diffStats ?? null,
    };
    initializeCumulativeCounters(stats);
    stats.reviews.push(reviewStats);
    updateAggregatesForNewReview(stats, reviewStats);
    if (stats.reviews.length > 100) {
        stats.reviews = stats.reviews.slice(-100);
    }
    saveProjectStats(projectPath, stats);
    return reviewStats;
}
function initializeCumulativeCounters(stats) {
    if (stats.totalScoreSum !== undefined)
        return;
    const reviewsWithScore = stats.reviews.filter((r) => r.score !== null);
    stats.totalScoreSum = reviewsWithScore.reduce((sum, r) => sum + (r.score ?? 0), 0);
    stats.scoredReviewCount = reviewsWithScore.length;
    const reviewsWithDiffStats = stats.reviews.filter((r) => r.diffStats != null);
    stats.diffStatsReviewCount = reviewsWithDiffStats.length;
}
function updateAggregatesForNewReview(stats, review) {
    stats.totalReviews += 1;
    stats.totalDuration += review.duration;
    stats.averageDuration = stats.totalDuration / stats.totalReviews;
    stats.totalBlocking += review.blocking;
    stats.totalWarnings += review.warnings;
    if (review.score !== null) {
        stats.totalScoreSum = (stats.totalScoreSum ?? 0) + review.score;
        stats.scoredReviewCount = (stats.scoredReviewCount ?? 0) + 1;
    }
    if (stats.scoredReviewCount && stats.scoredReviewCount > 0) {
        stats.averageScore = (stats.totalScoreSum ?? 0) / stats.scoredReviewCount;
    }
    if (review.diffStats) {
        stats.totalAdditions += review.diffStats.additions;
        stats.totalDeletions += review.diffStats.deletions;
        stats.diffStatsReviewCount = (stats.diffStatsReviewCount ?? 0) + 1;
    }
    if (stats.diffStatsReviewCount && stats.diffStatsReviewCount > 0) {
        stats.averageAdditions = stats.totalAdditions / stats.diffStatsReviewCount;
        stats.averageDeletions = stats.totalDeletions / stats.diffStatsReviewCount;
    }
}
/**
 * Get statistics summary for display
 */
export function getStatsSummary(stats) {
    const formatDuration = (ms) => {
        const hours = Math.floor(ms / 3600000);
        const minutes = Math.floor((ms % 3600000) / 60000);
        if (hours > 0)
            return `${hours}h ${minutes}m`;
        return `${minutes}m`;
    };
    // Calculate trend based on last 5 vs previous 5 reviews
    const recent = stats.reviews.slice(-5);
    const previous = stats.reviews.slice(-10, -5);
    let scoreTrend = 'stable';
    let blockingTrend = 'stable';
    if (recent.length >= 3 && previous.length >= 3) {
        const recentScores = recent.filter((r) => r.score !== null);
        const prevScores = previous.filter((r) => r.score !== null);
        if (recentScores.length > 0 && prevScores.length > 0) {
            const avgRecent = recentScores.reduce((s, r) => s + (r.score ?? 0), 0) / recentScores.length;
            const avgPrev = prevScores.reduce((s, r) => s + (r.score ?? 0), 0) / prevScores.length;
            if (avgRecent > avgPrev + 0.5)
                scoreTrend = 'up';
            else if (avgRecent < avgPrev - 0.5)
                scoreTrend = 'down';
        }
        const avgBlockingRecent = recent.reduce((s, r) => s + r.blocking, 0) / recent.length;
        const avgBlockingPrev = previous.reduce((s, r) => s + r.blocking, 0) / previous.length;
        if (avgBlockingRecent < avgBlockingPrev - 0.5)
            blockingTrend = 'up'; // fewer blocking = good = up
        else if (avgBlockingRecent > avgBlockingPrev + 0.5)
            blockingTrend = 'down';
    }
    return {
        totalReviews: stats.totalReviews,
        totalTime: formatDuration(stats.totalDuration),
        averageTime: formatDuration(stats.averageDuration),
        averageScore: stats.averageScore !== null ? stats.averageScore.toFixed(1) : '-',
        totalBlocking: stats.totalBlocking,
        totalWarnings: stats.totalWarnings,
        totalAdditions: stats.totalAdditions,
        totalDeletions: stats.totalDeletions,
        averageAdditions: stats.averageAdditions !== null ? stats.averageAdditions.toFixed(1) : '-',
        averageDeletions: stats.averageDeletions !== null ? stats.averageDeletions.toFixed(1) : '-',
        totalLinesReviewed: stats.totalAdditions + stats.totalDeletions,
        trend: { score: scoreTrend, blocking: blockingTrend },
    };
}
//# sourceMappingURL=statsService.js.map