import type { XiaobaoRiskLevel } from './xiaobao-risk'; export interface XiaobaoRiskSnapshot { versionId: string; date: string; riskScore: number; riskLevel: XiaobaoRiskLevel; forecastReleaseDate?: string; openBugCount: number; failedTestCount: number; blockedCount: number; silentRiskCount: number; confidence: number; createdAt: string; } export interface RiskTrendSummary { direction: 'up' | 'down' | 'flat' | 'unknown'; delta: number; summary: string; pattern: 'continuous_rising' | 'continuous_falling' | 'score_delta' | 'stable' | 'unknown'; } export function summarizeRiskTrend(snapshots: XiaobaoRiskSnapshot[]): RiskTrendSummary { const sorted = [...snapshots].sort((a, b) => getSnapshotTime(a) - getSnapshotTime(b)); if (sorted.length < 2) { return { direction: 'unknown', delta: 0, summary: 'No historical risk snapshots yet.', pattern: 'unknown' }; } const first = sorted[0]; const latest = sorted[sorted.length - 1]; const delta = clampScore(latest.riskScore) - clampScore(first.riskScore); const recentThree = sorted.slice(-3); if (recentThree.length === 3) { const [a, b, c] = recentThree.map((snapshot) => clampScore(snapshot.riskScore)); if (a < b && b < c) { return { direction: 'up', delta, summary: `Risk rose continuously from ${a} to ${c}.`, pattern: 'continuous_rising', }; } if (a > b && b > c) { return { direction: 'down', delta, summary: `Risk fell continuously from ${a} to ${c}.`, pattern: 'continuous_falling', }; } } if (delta >= 10) { return { direction: 'up', delta, summary: `Risk rose by ${delta} points.`, pattern: 'score_delta' }; } if (delta <= -10) { return { direction: 'down', delta, summary: `Risk fell by ${Math.abs(delta)} points.`, pattern: 'score_delta' }; } return { direction: 'flat', delta, summary: 'Risk is stable.', pattern: 'stable' }; } export function summarizeRiskTrendWithCurrent( snapshots: XiaobaoRiskSnapshot[] = [], current: XiaobaoRiskSnapshot, ): RiskTrendSummary { return summarizeRiskTrend([...snapshots, current]); } export function buildRiskSignature(snapshot: XiaobaoRiskSnapshot): string { return [ snapshot.versionId, snapshot.date, clampScore(snapshot.riskScore), snapshot.riskLevel, snapshot.forecastReleaseDate ?? '', snapshot.openBugCount, snapshot.failedTestCount, snapshot.blockedCount, snapshot.silentRiskCount, clampScore(snapshot.confidence), ].join('|'); } function getSnapshotTime(snapshot: XiaobaoRiskSnapshot): number { const date = new Date(snapshot.createdAt || snapshot.date).getTime(); return Number.isFinite(date) ? date : 0; } function clampScore(score: number): number { return Math.max(0, Math.min(100, Math.round(score))); }