import type { Bug } from './bug';
import type { DevTask } from './dev-task';
import { STATUS_PROGRESS, getEstimateHours } from './dev-task';
import type { TestCase } from './test-case';
import { getTestCaseEstimateHours } from './test-case';
import type { XiaobaoRiskInsight } from './xiaobao-risk-cache';
import type { VersionDailyEvidence } from './xiaobao-risk-evidence';
import { summarizeRiskTrendWithCurrent, type XiaobaoRiskSnapshot } from './xiaobao-risk-trend';
import { WORK_HOURS, addWorkHours } from './work-hours';

export type XiaobaoRiskLevel = 'on_track' | 'attention' | 'at_risk' | 'likely_delayed' | 'blocked';
export type XiaobaoConfidenceLevel = 'high' | 'medium' | 'low';

export interface XiaobaoVersionRef {
  id: string;
  name: string;
  status?: string;
  productId?: string;
  productName?: string;
  projectId?: string;
  projectName?: string;
  expectedReleaseDate?: string | null;
  members?: Array<{ id?: string; name: string; role?: string }>;
}

export interface RiskReason {
  key: string;
  title: string;
  detail: string;
  severity: 'info' | 'warning' | 'danger';
  count?: number;
}

export interface SilentRisk {
  key: string;
  title: string;
  detail: string;
  itemId?: string;
  itemType?: 'dev_task' | 'test_case' | 'bug' | 'version';
}

export interface XiaobaoRiskSignals {
  unfinishedCount: number;
  openBugCount: number;
  criticalBugCount: number;
  failedTestCount: number;
  blockedCount: number;
  silentRiskCount: number;
  daysToExpectedRelease?: number;
}

export interface XiaobaoVersionRisk {
  versionId: string;
  versionName: string;
  productId?: string;
  productName?: string;
  projectId?: string;
  projectName?: string;
  riskScore: number;
  riskLevel: XiaobaoRiskLevel;
  expectedReleaseDate: string | null;
  confidence: number;
  confidenceLevel: XiaobaoConfidenceLevel;
  forecastReleaseDate?: string;
  delayDays: number;
  remainingWorkHours: number;
  reasons: RiskReason[];
  silentRisks: SilentRisk[];
  dailyEvidence?: VersionDailyEvidence;
  signals: XiaobaoRiskSignals;
  currentSnapshot: XiaobaoRiskSnapshot;
  aiInsight?: XiaobaoRiskInsight;
  aiInsightUpdating?: boolean;
  aiInsightSource?: 'current_ai' | 'previous_ai' | 'rule';
  trend: {
    direction: 'up' | 'down' | 'flat' | 'unknown';
    delta: number;
    summary: string;
    pattern?: 'continuous_rising' | 'continuous_falling' | 'score_delta' | 'stable' | 'unknown';
  };
}

export interface CalcXiaobaoVersionRiskInput {
  version: XiaobaoVersionRef;
  devTasks?: DevTask[];
  testCases?: TestCase[];
  bugs?: Bug[];
  now?: Date;
  snapshots?: XiaobaoRiskSnapshot[];
  silentRisks?: SilentRisk[];
  dailyEvidence?: VersionDailyEvidence;
  recentActivityCount?: number;
  lastActivityAt?: string;
}

const OPEN_BUG_STATUSES = new Set<Bug['status']>(['open', 'fixing', 'fixed', 'verifying']);
const TEST_DONE_STATUSES = new Set<TestCase['status']>(['passed']);

export function calcXiaobaoVersionRisk(input: CalcXiaobaoVersionRiskInput): XiaobaoVersionRisk {
  const now = input.now ?? new Date();
  const devTasks = input.devTasks ?? [];
  const testCases = input.testCases ?? [];
  const bugs = input.bugs ?? [];
  const silentRisks = input.dailyEvidence?.silentRisks ?? [];

  const remainingDevHours = devTasks.reduce((sum, task) => {
    const estimate = getEstimateHours(task);
    const progress = STATUS_PROGRESS[task.status] ?? 0;
    return sum + estimate * Math.max(0, 100 - progress) / 100;
  }, 0);
  const remainingTestHours = testCases.reduce((sum, tc) => {
    if (TEST_DONE_STATUSES.has(tc.status)) return sum;
    return sum + getTestCaseEstimateHours(tc);
  }, 0);
  const openBugs = bugs.filter((bug) => OPEN_BUG_STATUSES.has(bug.status));
  const remainingBugHours = openBugs.reduce((sum, bug) => sum + getBugEstimateHours(bug), 0);
  const remainingWorkHours = roundHours(remainingDevHours + remainingTestHours + remainingBugHours);

  const forecastReleaseDate = remainingWorkHours > 0 ? addWorkHours(now.toISOString(), remainingWorkHours) : undefined;
  const expectedRelease = parseDate(input.version.expectedReleaseDate);
  const forecast = parseDate(forecastReleaseDate);
  const delayDays = expectedRelease && forecast && forecast.getTime() > expectedRelease.getTime()
    ? roundDays((forecast.getTime() - expectedRelease.getTime()) / 86_400_000)
    : 0;
  const daysToExpectedRelease = expectedRelease
    ? roundDays((expectedRelease.getTime() - now.getTime()) / 86_400_000)
    : undefined;

  const criticalBugCount = openBugs.filter((bug) => bug.severity === 'critical' || bug.priority === 'P1').length;
  const failedTestCount = testCases.filter((tc) => tc.status === 'failed').length;
  const blockedDevCount = devTasks.filter((task) => task.isBlocked).length;
  const blockedTestCount = testCases.filter((tc) => tc.status === 'blocked').length;
  const blockedCount = blockedDevCount + blockedTestCount;
  const unfinishedCount = devTasks.filter((task) => task.status !== 'submitted').length
    + testCases.filter((tc) => tc.status !== 'passed').length
    + openBugs.length;

  const reasons: RiskReason[] = [];
  if (remainingWorkHours > 0) {
    reasons.push({
      key: 'remaining_work',
      title: '剩余工作量',
      detail: `预计还剩 ${remainingWorkHours}h 工作量。`,
      severity: delayDays > 0 ? 'danger' : 'warning',
    });
  }
  if (delayDays > 0) {
    reasons.push({
      key: 'forecast_delay',
      title: '预测延期',
      detail: `预测发布时间晚于计划约 ${delayDays} 天。`,
      severity: 'danger',
    });
  }
  if (criticalBugCount > 0) {
    reasons.push({
      key: 'critical_bug',
      title: '关键缺陷',
      detail: `仍有 ${criticalBugCount} 个 P1 或致命 Bug 未关闭。`,
      severity: 'danger',
      count: criticalBugCount,
    });
  }
  if (blockedCount > 0) {
    reasons.push({
      key: 'blocked_work',
      title: '阻塞工作',
      detail: `仍有 ${blockedCount} 个开发或测试项处于阻塞。`,
      severity: 'danger',
      count: blockedCount,
    });
  }
  if (failedTestCount > 0) {
    reasons.push({
      key: 'failed_test',
      title: '失败用例',
      detail: `仍有 ${failedTestCount} 个测试用例未通过。`,
      severity: 'warning',
      count: failedTestCount,
    });
  }
  if (silentRisks.length > 0) {
    reasons.push({
      key: 'silent_risk',
      title: '静默风险',
      detail: `发现 ${silentRisks.length} 个今日缺少进展或证据的风险项。`,
      severity: 'warning',
      count: silentRisks.length,
    });
  }

  const signals: XiaobaoRiskSignals = {
    unfinishedCount,
    openBugCount: openBugs.length,
    criticalBugCount,
    failedTestCount,
    blockedCount,
    silentRiskCount: silentRisks.length,
    daysToExpectedRelease,
  };

  const riskScore = calcRiskScore({
    delayDays,
    remainingWorkHours,
    criticalBugCount,
    failedTestCount,
    blockedCount,
    silentRiskCount: silentRisks.length,
    daysToExpectedRelease,
  });
  const hasBlockingRisk = criticalBugCount > 0 || blockedCount > 0;
  const riskLevel = getRiskLevel(riskScore, delayDays, hasBlockingRisk);
  const confidence = calcConfidence(input, devTasks, testCases, bugs);
  const currentSnapshot: XiaobaoRiskSnapshot = {
    versionId: input.version.id,
    date: now.toISOString().slice(0, 10),
    riskScore,
    riskLevel,
    forecastReleaseDate,
    openBugCount: signals.openBugCount,
    criticalBugCount: signals.criticalBugCount,
    failedTestCount: signals.failedTestCount,
    blockedCount: signals.blockedCount,
    silentRiskCount: signals.silentRiskCount,
    confidence,
    createdAt: now.toISOString(),
  };
  const trend = summarizeRiskTrendWithCurrent(input.snapshots ?? [], currentSnapshot);

  return {
    versionId: input.version.id,
    versionName: input.version.name,
    productId: input.version.productId,
    productName: input.version.productName,
    projectId: input.version.projectId,
    projectName: input.version.projectName,
    riskScore,
    riskLevel,
    expectedReleaseDate: input.version.expectedReleaseDate ?? null,
    confidence,
    confidenceLevel: getConfidenceLevel(confidence),
    forecastReleaseDate,
    delayDays,
    remainingWorkHours,
    reasons,
    silentRisks,
    dailyEvidence: input.dailyEvidence,
    signals,
    currentSnapshot,
    trend,
  };
}

function getBugEstimateHours(bug: Bug): number {
  if (typeof bug.estimateHours === 'number' && bug.estimateHours > 0) return roundHours(bug.estimateHours);
  if (typeof bug.aiEstimateHours === 'number' && bug.aiEstimateHours > 0) return roundHours(bug.aiEstimateHours);
  if (bug.severity === 'critical') return 16;
  if (bug.severity === 'major') return 8;
  return 4;
}

function calcRiskScore(input: {
  delayDays: number;
  remainingWorkHours: number;
  criticalBugCount: number;
  failedTestCount: number;
  blockedCount: number;
  silentRiskCount: number;
  daysToExpectedRelease?: number;
}): number {
  let score = 0;
  if (input.delayDays > 0) score += 70 + Math.min(15, input.delayDays * 3);
  if (input.daysToExpectedRelease !== undefined && input.daysToExpectedRelease <= 2 && input.remainingWorkHours > 0) {
    score += Math.min(20, input.remainingWorkHours / WORK_HOURS.hoursPerDay * 4);
  }
  score += input.criticalBugCount * 25;
  score += input.blockedCount * 22;
  score += input.failedTestCount * 12;
  score += input.silentRiskCount * 8;
  if (input.remainingWorkHours > 0 && input.delayDays === 0) {
    score += Math.min(35, input.remainingWorkHours / WORK_HOURS.hoursPerDay * 5);
  }
  return clampScore(score);
}

function getRiskLevel(score: number, delayDays: number, hasBlockingRisk: boolean): XiaobaoRiskLevel {
  if (hasBlockingRisk) return 'blocked';
  if (delayDays > 0 || score >= 75) return 'likely_delayed';
  if (score >= 55) return 'at_risk';
  if (score >= 30) return 'attention';
  return 'on_track';
}

function calcConfidence(
  input: CalcXiaobaoVersionRiskInput,
  devTasks: DevTask[],
  testCases: TestCase[],
  bugs: Bug[],
): number {
  let confidence = 100;
  if (!input.version.expectedReleaseDate) confidence -= 20;
  const workItems = [...devTasks, ...testCases, ...bugs];
  const missingEstimateCount = workItems.filter((item) => !hasEstimate(item)).length;
  if (missingEstimateCount > 0) confidence -= Math.min(25, missingEstimateCount * 5);
  if (testCases.length === 0) confidence -= 15;
  if (!input.version.members || input.version.members.length === 0) confidence -= 10;
  if (input.recentActivityCount === 0 || input.lastActivityAt === undefined) confidence -= 10;
  if ((input.snapshots?.length ?? 0) < 2) confidence -= 10;
  return clampScore(confidence);
}

function hasEstimate(item: DevTask | TestCase | Bug): boolean {
  return (typeof item.estimateHours === 'number' && item.estimateHours > 0)
    || (typeof item.aiEstimateHours === 'number' && item.aiEstimateHours > 0);
}


function getConfidenceLevel(confidence: number): XiaobaoConfidenceLevel {
  if (confidence >= 75) return 'high';
  if (confidence >= 50) return 'medium';
  return 'low';
}

function parseDate(iso?: string | null): Date | undefined {
  if (!iso) return undefined;
  const date = new Date(iso);
  return Number.isNaN(date.getTime()) ? undefined : date;
}

function roundHours(hours: number): number {
  return Math.round(hours * 2) / 2;
}

function roundDays(days: number): number {
  return Math.round(days * 10) / 10;
}

function clampScore(score: number): number {
  return Math.max(0, Math.min(100, Math.round(score)));
}
