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

import {
  TEST_CASE_STATUS_LABEL,
  aggregateTestCaseHours,
  calcTestProgress,
  canStartNextTestRound,
  copyTestCaseToRound,
  getNextTestRoundNo,
  getRequirementDeliveryStatus,
  getTestCaseEstimateHours,
  getTestCaseRoundNo,
  normalizeTestCase,
} from './test-case';
import { DEFAULT_TEST_CATEGORY_ID } from './task-category';

test('normalizeTestCase backfills missing categoryId', () => {
  const tc = normalizeTestCase({
    id: 'tc-1',
    caseNo: 'TC-001',
    versionId: 'v1',
    title: '登录正常',
    priority: 'P2',
    status: 'pending',
    createdBy: 'tester',
    createdAt: '2026-06-25',
    updatedAt: '2026-06-25',
  } as any);

  assert.equal(tc.categoryId, DEFAULT_TEST_CATEGORY_ID);
});

test('normalizeTestCase keeps existing categoryId', () => {
  const tc = normalizeTestCase({
    id: 'tc-1',
    caseNo: 'TC-001',
    versionId: 'v1',
    title: '登录正常',
    priority: 'P2',
    status: 'pending',
    categoryId: 'cat-test-api',
    createdBy: 'tester',
    createdAt: '2026-06-25',
    updatedAt: '2026-06-25',
  } as any);

  assert.equal(tc.categoryId, 'cat-test-api');
});

test('normalizeTestCase preserves planned test time', () => {
  const tc = normalizeTestCase({
    id: 'tc-1',
    caseNo: 'TC-001',
    versionId: 'v1',
    title: 'scheduled test case',
    priority: 'P2',
    status: 'pending',
    categoryId: 'cat-test-api',
    plannedTestAt: '2026-07-01T09:30:00.000Z',
    createdBy: 'tester',
    createdAt: '2026-06-25',
    updatedAt: '2026-06-25',
  } as any);

  assert.equal(tc.plannedTestAt, '2026-07-01T09:30:00.000Z');
});

test('normalizeTestCase backfills missing roundNo to the first test round', () => {
  const tc = normalizeTestCase({
    id: 'tc-1',
    caseNo: 'TC-001',
    versionId: 'v1',
    title: '第一轮用例',
    priority: 'P2',
    status: 'pending',
    categoryId: 'cat-test-api',
    createdBy: 'tester',
    createdAt: '2026-06-25',
    updatedAt: '2026-06-25',
  } as any);

  assert.equal(tc.roundNo, 1);
  assert.equal(getTestCaseRoundNo(tc), 1);
});

test('getNextTestRoundNo returns one more than the current max round', () => {
  const cases = [
    normalizeTestCase({ id: 'tc-1', caseNo: 'TC-001', versionId: 'v1', title: 'R1', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
    normalizeTestCase({ id: 'tc-2', caseNo: 'TC-002', versionId: 'v1', title: 'R3', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 3, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
  ];

  assert.equal(getNextTestRoundNo(cases), 4);
});

test('canStartNextTestRound requires latest round cases all tested', () => {
  assert.equal(canStartNextTestRound([]), false);
  assert.equal(canStartNextTestRound([
    normalizeTestCase({ id: 'tc-1', caseNo: 'TC-001', versionId: 'v1', title: '通过', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
    normalizeTestCase({ id: 'tc-2', caseNo: 'TC-002', versionId: 'v1', title: '待测', priority: 'P2', status: 'pending', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
  ]), false);
  assert.equal(canStartNextTestRound([
    normalizeTestCase({ id: 'tc-1', caseNo: 'TC-001', versionId: 'v1', title: '通过', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
    normalizeTestCase({ id: 'tc-2', caseNo: 'TC-002', versionId: 'v1', title: '不通过', priority: 'P2', status: 'failed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
  ]), true);
  assert.equal(canStartNextTestRound([
    normalizeTestCase({ id: 'tc-1', caseNo: 'TC-001', versionId: 'v1', title: '首轮通过', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
    normalizeTestCase({ id: 'tc-2', caseNo: 'TC-002', versionId: 'v1', title: '二轮待测', priority: 'P2', status: 'running', categoryId: 'cat-test-api', roundNo: 2, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
  ]), false);
});

test('getRequirementDeliveryStatus marks a requirement submitted only when all its dev tasks are submitted', () => {
  const tasks = [
    { requirementId: 'req-1', status: 'submitted' },
    { requirementId: 'req-1', status: 'submitted' },
    { requirementId: 'req-2', status: 'testing' },
  ] as any;

  assert.equal(getRequirementDeliveryStatus('req-1', tasks), 'submitted');
  assert.equal(getRequirementDeliveryStatus('req-2', tasks), 'pending');
  assert.equal(getRequirementDeliveryStatus('req-empty', tasks), 'pending');
});

test('copyTestCaseToRound preserves estimates and references but clears execution state', () => {
  const source = normalizeTestCase({
    id: 'tc-source',
    caseNo: 'TC-001',
    versionId: 'v1',
    requirementId: 'req-1',
    title: '登录正常',
    description: '步骤',
    priority: 'P1',
    status: 'passed',
    categoryId: 'cat-test-functional',
    estimateHours: 0.75,
    aiEstimateHours: 0.25,
    plannedTestAt: '2026-07-01T09:30:00.000Z',
    assigneeId: 'QA',
    startedAt: '2026-06-25T01:00:00.000Z',
    completedAt: '2026-06-25T02:00:00.000Z',
    executedAt: '2026-06-25T02:00:00.000Z',
    failReason: 'old fail',
    blockReason: 'old block',
    references: [{ type: 'requirement', id: 'req-1', label: 'REQ-001 登录' }],
    aiDraft: true,
    aiDraftAt: '2026-06-25T00:00:00.000Z',
    roundNo: 1,
    createdBy: 'AI',
    createdAt: '2026-06-25',
    updatedAt: '2026-06-25',
  } as any);

  const copied = copyTestCaseToRound(source, 2, 'tester');

  assert.equal(copied.roundNo, 2);
  assert.equal(copied.sourceCaseId, 'tc-source');
  assert.equal(copied.aiEstimateHours, 0.25);
  assert.equal(copied.estimateHours, 0.75);
  assert.equal(copied.plannedTestAt, '2026-07-01T09:30:00.000Z');
  assert.deepEqual(copied.references, source.references);
  assert.equal(copied.startedAt, undefined);
  assert.equal(copied.completedAt, undefined);
  assert.equal(copied.executedAt, undefined);
  assert.equal(copied.failReason, undefined);
  assert.equal(copied.blockReason, undefined);
  assert.equal(copied.createdBy, 'tester');
});

test('normalizeTestCase keeps missing executor estimate empty', () => {
  const tc = normalizeTestCase({
    id: 'tc-1',
    caseNo: 'TC-001',
    versionId: 'v1',
    title: '登录正常',
    priority: 'P2',
    status: 'pending',
    categoryId: 'cat-test-api',
    createdBy: 'tester',
    createdAt: '2026-06-25',
    updatedAt: '2026-06-25',
  } as any);

  assert.equal(tc.estimateHours, undefined);
  assert.equal(getTestCaseEstimateHours(tc), 0);
});

test('getTestCaseEstimateHours prefers executor estimate over AI estimate', () => {
  const tc = normalizeTestCase({
    id: 'tc-1',
    caseNo: 'TC-001',
    versionId: 'v1',
    title: '登录正常',
    priority: 'P2',
    status: 'pending',
    categoryId: 'cat-test-api',
    estimateHours: 0.7,
    aiEstimateHours: 0.2,
    createdBy: 'tester',
    createdAt: '2026-06-25',
    updatedAt: '2026-06-25',
  } as any);

  assert.equal(getTestCaseEstimateHours(tc), 0.7);
});

test('getTestCaseEstimateHours falls back to AI estimate', () => {
  const tc = normalizeTestCase({
    id: 'tc-1',
    caseNo: 'TC-001',
    versionId: 'v1',
    title: '登录正常',
    priority: 'P2',
    status: 'pending',
    categoryId: 'cat-test-api',
    aiEstimateHours: 0.2,
    createdBy: 'tester',
    createdAt: '2026-06-25',
    updatedAt: '2026-06-25',
  } as any);

  assert.equal(getTestCaseEstimateHours(tc), 0.2);
});

test('test case status labels use waiting and testing wording', () => {
  assert.equal(TEST_CASE_STATUS_LABEL.pending, '待测试');
  assert.equal(TEST_CASE_STATUS_LABEL.running, '测试中');
  assert.equal(TEST_CASE_STATUS_LABEL.failed, '不通过');
});

test('calcTestProgress uses estimate-weighted completion', () => {
  const progress = calcTestProgress([
    normalizeTestCase({
      id: 'tc-1',
      caseNo: 'TC-001',
      versionId: 'v1',
      title: '待测试',
      priority: 'P2',
      status: 'pending',
      categoryId: 'cat-test-functional',
      estimateHours: 1,
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
    normalizeTestCase({
      id: 'tc-2',
      caseNo: 'TC-002',
      versionId: 'v1',
      title: '已通过',
      priority: 'P2',
      status: 'passed',
      categoryId: 'cat-test-functional',
      estimateHours: 3,
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
  ]);

  assert.equal(progress.completionRate, 75);
});

test('calcTestProgress calculates pass rate from passed cases that never had effective bugs', () => {
  const progress = calcTestProgress([
    normalizeTestCase({
      id: 'tc-clean',
      caseNo: 'TC-001',
      versionId: 'v1',
      title: '无 Bug 通过',
      priority: 'P2',
      status: 'passed',
      categoryId: 'cat-test-functional',
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
    normalizeTestCase({
      id: 'tc-bug-fixed',
      caseNo: 'TC-002',
      versionId: 'v1',
      title: '提过 Bug 后通过',
      priority: 'P2',
      status: 'passed',
      categoryId: 'cat-test-functional',
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
    normalizeTestCase({
      id: 'tc-failed',
      caseNo: 'TC-003',
      versionId: 'v1',
      title: '不通过',
      priority: 'P2',
      status: 'failed',
      categoryId: 'cat-test-functional',
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
    normalizeTestCase({
      id: 'tc-running-with-bug',
      caseNo: 'TC-004',
      versionId: 'v1',
      title: '测试中已提 Bug',
      priority: 'P2',
      status: 'running',
      categoryId: 'cat-test-functional',
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
    normalizeTestCase({
      id: 'tc-rejected-only',
      caseNo: 'TC-005',
      versionId: 'v1',
      title: '仅有驳回 Bug',
      priority: 'P2',
      status: 'passed',
      categoryId: 'cat-test-functional',
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
    normalizeTestCase({
      id: 'tc-pending',
      caseNo: 'TC-006',
      versionId: 'v1',
      title: '未执行',
      priority: 'P2',
      status: 'pending',
      categoryId: 'cat-test-functional',
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
  ], [
    { testCaseId: 'tc-bug-fixed', status: 'closed' },
    { testCaseId: 'tc-running-with-bug', status: 'open' },
    { testCaseId: 'tc-rejected-only', status: 'rejected' },
  ]);

  assert.equal(progress.executed, 5);
  assert.equal(progress.passed, 3);
  assert.equal(progress.passRate, 40);
});

test('aggregateTestCaseHours returns estimate and actual totals', () => {
  const hours = aggregateTestCaseHours([
    normalizeTestCase({
      id: 'tc-1',
      caseNo: 'TC-001',
      versionId: 'v1',
      title: '测试中',
      priority: 'P2',
      status: 'running',
      categoryId: 'cat-test-functional',
      estimateHours: 1,
      startedAt: '2026-06-25T01:00:00.000Z',
      createdBy: 'tester',
      createdAt: '2026-06-25',
      updatedAt: '2026-06-25',
    } as any),
  ], new Date('2026-06-25T02:00:00.000Z'));

  assert.equal(hours.estimate, 1);
  assert.equal(hours.actual, 1);
});
