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

import type { TestCase } from './test-case';
import { canStartTestCase, hasTestCasePlan, needsTestCaseClaim } from './test-case';
import { applyTestCaseTransition, normalizeTestCaseOnCreate } from './test-case-workflow';

function tc(patch: Partial<TestCase> = {}): TestCase {
  return {
    id: 'tc-1',
    caseNo: 'TC-001',
    versionId: 'version-1',
    title: '拖拽排序正常',
    description: '验证拖拽排序',
    categoryId: 'cat-test-functional',
    priority: 'P2',
    status: 'pending',
    assigneeId: 'QA',
    plannedTestAt: '2026-06-25T01:00:00.000Z',
    plannedEndAt: '2026-06-25T02:00:00.000Z',
    createdBy: 'QA',
    createdAt: '2026-06-25T00:00:00.000Z',
    updatedAt: '2026-06-25T00:00:00.000Z',
    ...patch,
  };
}

test('normalizeTestCaseOnCreate forces pending and clears actual timestamps', () => {
  const result = normalizeTestCaseOnCreate(tc({
    status: 'running',
    startedAt: '2026-06-25T01:00:00.000Z',
    completedAt: '2026-06-25T02:00:00.000Z',
  }));

  assert.equal(result.status, 'pending');
  assert.equal(result.startedAt, undefined);
  assert.equal(result.completedAt, undefined);
});

test('normalizeTestCaseOnCreate defaults missing roundNo to the first test round', () => {
  const result = normalizeTestCaseOnCreate(tc());

  assert.equal(result.roundNo, 1);
});

test('pending to running writes startedAt', () => {
  const result = applyTestCaseTransition(tc(), 'running', {
    now: new Date('2026-06-25T01:00:00.000Z'),
  });

  assert.equal(result.ok, true);
  assert.equal(result.patch?.startedAt, '2026-06-25T01:00:00.000Z');
});

test('AI test case without an assignee must be claimed with a plan before running', () => {
  const draft = tc({
    assigneeId: undefined,
    plannedTestAt: undefined,
    plannedEndAt: undefined,
    aiDraft: true,
  });

  assert.equal(needsTestCaseClaim(draft), true);
  assert.equal(hasTestCasePlan(draft), false);
  assert.equal(canStartTestCase(draft), false);

  const result = applyTestCaseTransition(draft, 'running', {
    now: new Date('2026-06-25T01:00:00.000Z'),
  });

  assert.equal(result.ok, false);
});

test('recommended assignee test case still needs a plan before running', () => {
  const draft = tc({
    assigneeId: 'QA',
    plannedTestAt: undefined,
    plannedEndAt: undefined,
    aiDraft: true,
  });

  assert.equal(needsTestCaseClaim(draft), false);
  assert.equal(hasTestCasePlan(draft), false);
  assert.equal(canStartTestCase(draft), false);

  const result = applyTestCaseTransition(draft, 'running', {
    now: new Date('2026-06-25T01:00:00.000Z'),
  });

  assert.equal(result.ok, false);
});

test('running to passed writes completedAt', () => {
  const result = applyTestCaseTransition(tc({
    status: 'running',
    startedAt: '2026-06-25T01:00:00.000Z',
  }), 'passed', {
    now: new Date('2026-06-25T02:00:00.000Z'),
  });

  assert.equal(result.ok, true);
  assert.equal(result.patch?.completedAt, '2026-06-25T02:00:00.000Z');
});

test('failed back to running clears failure and completion reason', () => {
  const result = applyTestCaseTransition(tc({
    status: 'failed',
    startedAt: '2026-06-25T01:00:00.000Z',
    completedAt: '2026-06-25T02:00:00.000Z',
    failReason: '排序未保存',
  }), 'running', {
    now: new Date('2026-06-25T03:00:00.000Z'),
  });

  assert.equal(result.ok, true);
  assert.equal(result.patch?.startedAt, undefined);
  assert.equal(result.patch?.completedAt, undefined);
  assert.equal(result.patch?.failReason, undefined);
});

test('invalid transition is rejected', () => {
  const result = applyTestCaseTransition(tc(), 'passed', {
    now: new Date('2026-06-25T02:00:00.000Z'),
  });

  assert.equal(result.ok, false);
});
