import test from 'node:test';
import assert from 'node:assert/strict';

import {
  calcBugSeverityRanking,
  calcPersonalEffortRanking,
  calcStageEffortMetrics,
  calcVersionOverviewEffortTotals,
  buildVersionTimelineSummary,
  formatVersionOverviewDateTime,
  getVersionCardDefaultExpanded,
  mergeStageProgressWithEffort,
} from './version-overview';
import type { VersionPlan } from './version-plan';
import type { DevTask } from './dev-task';
import type { TestCase } from './test-case';
import type { Bug } from './bug';
import type { OvertimeRecord } from './overtime';

function plan(patch: Partial<VersionPlan>): VersionPlan {
  return {
    id: patch.id || 'plan-1',
    versionId: patch.versionId || 'v-1',
    type: patch.type || 'research',
    title: patch.title || '计划',
    owner: patch.owner || 'Alice',
    startTime: patch.startTime || '2026-06-22T09:00:00',
    endTime: patch.endTime || '2026-06-22T18:00:00',
    status: patch.status || 'completed',
    actualStartAt: patch.actualStartAt,
    completedAt: patch.completedAt,
    createdAt: patch.createdAt || '2026-06-22T08:00:00',
    addedBy: patch.addedBy || 'Alice',
  };
}

function devTask(patch: Partial<DevTask>): DevTask {
  return {
    id: patch.id || 'dev-1',
    taskNo: patch.taskNo || 'DEV-001',
    requirementId: patch.requirementId || 'req-1',
    title: patch.title || '开发任务',
    categoryId: patch.categoryId || 'cat-1',
    assigneeId: patch.assigneeId || 'Alice',
    priority: patch.priority || 'P2',
    expectedStartAt: patch.expectedStartAt || '2026-06-22T09:00:00',
    expectedEndAt: patch.expectedEndAt || '2026-06-22T18:00:00',
    estimateHours: patch.estimateHours,
    aiEstimateHours: patch.aiEstimateHours,
    actualStartAt: patch.actualStartAt,
    actualEndAt: patch.actualEndAt,
    status: patch.status || 'submitted',
    isBlocked: patch.isBlocked ?? false,
    createdBy: patch.createdBy || 'Alice',
    createdAt: patch.createdAt || '2026-06-22T08:00:00',
    updatedAt: patch.updatedAt || '2026-06-22T18:00:00',
  };
}

function testCase(patch: Partial<TestCase>): TestCase {
  return {
    id: patch.id || 'tc-1',
    caseNo: patch.caseNo || 'TC-001',
    versionId: patch.versionId || 'v-1',
    title: patch.title || '测试用例',
    categoryId: patch.categoryId || 'cat-1',
    priority: patch.priority || 'P2',
    assigneeId: patch.assigneeId || 'Bob',
    status: patch.status || 'passed',
    estimateHours: patch.estimateHours,
    aiEstimateHours: patch.aiEstimateHours,
    startedAt: patch.startedAt,
    completedAt: patch.completedAt,
    createdBy: patch.createdBy || 'Bob',
    createdAt: patch.createdAt || '2026-06-22T08:00:00',
    updatedAt: patch.updatedAt || '2026-06-22T18:00:00',
  };
}

function bug(patch: Partial<Bug>): Bug {
  return {
    id: patch.id || 'bug-1',
    bugNo: patch.bugNo || 'BUG-001',
    versionId: patch.versionId || 'v-1',
    testCaseId: patch.testCaseId || 'tc-1',
    title: patch.title || 'Bug',
    description: patch.description || '描述',
    severity: patch.severity || 'major',
    priority: patch.priority || 'P1',
    reportedBy: patch.reportedBy || 'QA',
    assigneeId: patch.assigneeId || 'Bob',
    status: patch.status || 'closed',
    createdAt: patch.createdAt || '2026-06-22T10:00:00',
    updatedAt: patch.updatedAt || '2026-06-22T12:00:00',
    closedAt: patch.closedAt,
    resolvedAt: patch.resolvedAt,
  };
}

test('calcStageEffortMetrics returns actual hours and AI estimates per stage', () => {
  const metrics = calcStageEffortMetrics({
    plans: [
      plan({ type: 'research', actualStartAt: '2026-06-22T09:00:00', completedAt: '2026-06-22T18:00:00' }),
      plan({ type: 'product', actualStartAt: '2026-06-23T09:00:00', completedAt: '2026-06-23T12:00:00' }),
    ],
    devTasks: [
      devTask({
        actualStartAt: '2026-06-24T09:00:00',
        actualEndAt: '2026-06-24T14:00:00',
        estimateHours: 4.5,
        aiEstimateHours: 3.25,
      }),
    ],
    testCases: [
      testCase({
        startedAt: '2026-06-25T09:00:00',
        completedAt: '2026-06-25T11:00:00',
        estimateHours: 2.25,
        aiEstimateHours: 1.5,
      }),
    ],
    bugs: [
      bug({ createdAt: '2026-06-26T10:00:00', closedAt: '2026-06-26T10:10:00' }),
    ],
  });

  assert.equal(metrics.requirement.actualHours, 8);
  assert.equal(metrics.product_design.actualHours, 3);
  assert.equal(metrics.dev.actualHours, 4);
  assert.equal(metrics.dev.estimateHours, 4.5);
  assert.equal(metrics.dev.aiEstimateHours, 3.25);
  assert.equal(metrics.testing.actualHours, 2);
  assert.equal(metrics.testing.estimateHours, 2.25);
  assert.equal(metrics.testing.aiEstimateHours, 1.5);
  assert.equal(metrics.bug.actualHours, 0.5);
});

test('mergeStageProgressWithEffort keeps progress state and adds stage effort fields', () => {
  const metrics = calcStageEffortMetrics({
    plans: [],
    devTasks: [
      devTask({
        actualStartAt: '2026-06-24T09:00:00',
        actualEndAt: '2026-06-24T14:00:00',
        estimateHours: 4.5,
        aiEstimateHours: 3.25,
      }),
    ],
    testCases: [
      testCase({
        startedAt: '2026-06-25T09:00:00',
        completedAt: '2026-06-25T11:00:00',
        estimateHours: 2.25,
        aiEstimateHours: 1.5,
      }),
    ],
    bugs: [],
  });

  const merged = mergeStageProgressWithEffort(
    {
      dev: { percent: 50, status: 'active' },
      testing: { percent: 100, status: 'done' },
    },
    metrics,
  );

  assert.equal(merged.dev.percent, 50);
  assert.equal(merged.dev.status, 'active');
  assert.equal(merged.dev.actualHours, 4);
  assert.equal(merged.dev.estimateHours, 4.5);
  assert.equal(merged.dev.aiEstimateHours, 3.25);
  assert.equal(merged.dev.showEstimates, true);
  assert.equal(merged.testing.percent, 100);
  assert.equal(merged.testing.status, 'done');
  assert.equal(merged.testing.actualHours, 2);
  assert.equal(merged.testing.estimateHours, 2.25);
  assert.equal(merged.testing.aiEstimateHours, 1.5);
  assert.equal(merged.requirement.percent, 0);
  assert.equal(merged.requirement.status, 'idle');
  assert.equal(merged.requirement.actualHours, 0);
});

test('getVersionCardDefaultExpanded expands only in-progress versions by default', () => {
  assert.equal(getVersionCardDefaultExpanded('developing'), true);
  assert.equal(getVersionCardDefaultExpanded('released'), false);
  assert.equal(getVersionCardDefaultExpanded('closed'), false);
  assert.equal(getVersionCardDefaultExpanded('paused'), false);
  assert.equal(getVersionCardDefaultExpanded('planned'), false);
});

test('calcStageEffortMetrics uses updatedAt for terminal test cases missing completedAt', () => {
  const metrics = calcStageEffortMetrics({
    plans: [],
    devTasks: [],
    testCases: [
      testCase({
        status: 'passed',
        startedAt: '2026-06-25T09:00:00',
        completedAt: undefined,
        updatedAt: '2026-06-25T09:20:00',
      }),
    ],
    bugs: [],
    now: new Date('2026-06-26T18:00:00'),
  });

  assert.equal(metrics.testing.actualHours, 0.5);
});

test('calcVersionOverviewEffortTotals sums actual hours and overtime records separately', () => {
  const overtimeRecords: OvertimeRecord[] = [
    {
      id: 'ot-1',
      projectId: 'project-1',
      versionId: 'v-1',
      person: 'Alice',
      startTime: '2026-06-22T19:00:00',
      endTime: '2026-06-22T20:15:00',
      duration: 1.25,
      reasonId: 'reason-3',
      createdAt: '2026-06-22T20:20:00',
    },
    {
      id: 'ot-2',
      projectId: 'project-1',
      versionId: 'v-1',
      person: 'Bob',
      startTime: '2026-06-22T20:00:00',
      endTime: '2026-06-22T22:03:00',
      duration: 2.05,
      reasonId: 'reason-4',
      createdAt: '2026-06-22T22:10:00',
    },
  ];

  const totals = calcVersionOverviewEffortTotals({
    plans: [plan({ actualStartAt: '2026-06-22T09:00:00', completedAt: '2026-06-22T18:00:00' })],
    devTasks: [devTask({ actualStartAt: '2026-06-22T10:00:00', actualEndAt: '2026-06-22T12:00:00' })],
    testCases: [],
    bugs: [],
    overtimeRecords,
  });

  assert.equal(totals.actualHours, 10);
  assert.equal(totals.overtimeHours, 3.3);
});

test('buildVersionTimelineSummary uses the same timing sources as version detail', () => {
  const summary = buildVersionTimelineSummary({
    status: 'closed',
    startDate: '2026-06-20',
    expectedReleaseDate: '2026-06-27',
    releaseDate: '2026-06-30T10:00:00',
    plans: [
      plan({
        type: 'research',
        actualStartAt: '2026-06-22T09:00:00',
        completedAt: '2026-06-22T18:00:00',
      }),
    ],
    devTasks: [
      devTask({
        actualStartAt: '2026-06-23T09:00:00',
        actualEndAt: '2026-06-23T12:00:00',
      }),
    ],
    testCases: [
      testCase({
        startedAt: '2026-06-24T09:00:00',
        completedAt: '2026-06-24T11:00:00',
      }),
    ],
    bugs: [
      bug({
        status: 'closed',
        createdAt: '2026-06-25T10:00:00',
        resolvedAt: '2026-06-25T16:00:00',
        closedAt: undefined,
        updatedAt: '2026-06-25T17:00:00',
      }),
    ],
    now: new Date('2026-06-26T18:00:00'),
  });

  assert.equal(summary.actualStartIso, '2026-06-22T09:00:00');
  assert.equal(summary.expectedReleaseIso, '2026-06-27');
  assert.equal(summary.actualReleaseIso, '2026-06-30T10:00:00');
  assert.equal(summary.actualEndIso, '2026-06-25T16:00:00');
  assert.equal(summary.isTerminalVersion, true);
  assert.equal(summary.actualHours, 30);
  assert.equal(summary.overdueDays, 3);
});

test('formatVersionOverviewDateTime matches the version detail display format', () => {
  assert.equal(formatVersionOverviewDateTime('2026-06-22T09:30:00'), '2026-06-22 09:30');
  assert.equal(formatVersionOverviewDateTime('2026-06-27'), '2026-06-27');
  assert.equal(formatVersionOverviewDateTime(null), '-');
});

test('calcPersonalEffortRanking includes bug work and sorts by total hours', () => {
  const overtimeRecords: OvertimeRecord[] = [
    {
      id: 'ot-1',
      projectId: 'project-1',
      versionId: 'v-1',
      person: 'Alice',
      startTime: '2026-06-23T19:00:00',
      endTime: '2026-06-23T20:30:00',
      duration: 1.5,
      reasonId: 'reason-3',
      createdAt: '2026-06-23T20:35:00',
    },
    {
      id: 'ot-2',
      projectId: 'project-1',
      versionId: 'v-1',
      person: 'Bob',
      startTime: '2026-06-25T19:00:00',
      endTime: '2026-06-25T21:00:00',
      duration: 2,
      reasonId: 'reason-4',
      createdAt: '2026-06-25T21:10:00',
    },
  ];
  const ranking = calcPersonalEffortRanking({
    plans: [plan({ owner: 'Alice', actualStartAt: '2026-06-22T09:00:00', completedAt: '2026-06-22T18:00:00' })],
    devTasks: [devTask({ assigneeId: 'Alice', actualStartAt: '2026-06-23T09:00:00', actualEndAt: '2026-06-23T11:00:00' })],
    testCases: [testCase({ assigneeId: 'Bob', startedAt: '2026-06-24T09:00:00', completedAt: '2026-06-24T11:00:00' })],
    bugs: [bug({ assigneeId: 'Bob', createdAt: '2026-06-25T10:00:00', closedAt: '2026-06-25T12:00:00' })],
    overtimeRecords,
  });

  assert.equal(ranking[0].name, 'Alice');
  assert.equal(ranking[0].actualHours, 10);
  assert.equal(ranking[0].overtimeHours, 1.5);
  assert.equal(ranking[0].total, 11.5);
  assert.equal(ranking[1].name, 'Bob');
  assert.equal(ranking[1].actualHours, 4);
  assert.equal(ranking[1].overtimeHours, 2);
  assert.equal(ranking[1].total, 6);
});

test('calcBugSeverityRanking groups bugs by assignee and severity', () => {
  const ranking = calcBugSeverityRanking([
    bug({ id: 'bug-1', assigneeId: 'Alice', severity: 'critical' }),
    bug({ id: 'bug-2', assigneeId: 'Alice', severity: 'major' }),
    bug({ id: 'bug-3', assigneeId: 'Alice', severity: 'minor' }),
    bug({ id: 'bug-4', assigneeId: 'Bob', severity: 'critical' }),
    bug({ id: 'bug-5', assigneeId: 'Bob', severity: 'trivial' }),
    bug({ id: 'bug-6', assigneeId: 'Alice', severity: 'trivial' }),
  ]);

  assert.deepEqual(ranking, [
    {
      assigneeId: 'Alice',
      critical: 1,
      major: 1,
      minor: 1,
      trivial: 1,
      total: 4,
    },
    {
      assigneeId: 'Bob',
      critical: 1,
      major: 0,
      minor: 0,
      trivial: 1,
      total: 2,
    },
  ]);
});
