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

import { canStartDevTask, hasDevTaskPlan, needsDevTaskClaim, type DevTask } from './dev-task';
import { applyDevTaskTransition, normalizeDevTaskOnCreate } from './dev-task-workflow';

function task(patch: Partial<DevTask> = {}): DevTask {
  return {
    id: 'task-1',
    taskNo: 'DEV-001',
    requirementId: 'req-1',
    title: '实现拖拽排序',
    categoryId: 'cat-frontend-interaction',
    assigneeId: 'Alice',
    priority: 'P2',
    expectedStartAt: '2026-06-25T01:00:00.000Z',
    expectedEndAt: '2026-06-25T02:00:00.000Z',
    status: 'todo',
    isBlocked: false,
    createdBy: 'PM',
    createdAt: '2026-06-25T00:00:00.000Z',
    updatedAt: '2026-06-25T00:00:00.000Z',
    ...patch,
  };
}

test('normalizeDevTaskOnCreate forces todo and clears actual timestamps for AI drafts', () => {
  const normalized = normalizeDevTaskOnCreate(task({
    status: 'in_progress',
    actualStartAt: '2026-06-25T01:00:00.000Z',
    actualEndAt: '2026-06-25T02:00:00.000Z',
    aiDraft: true,
  }));

  assert.equal(normalized.status, 'todo');
  assert.equal(normalized.actualStartAt, undefined);
  assert.equal(normalized.actualEndAt, undefined);
});

test('todo to in_progress writes actualStartAt from manual click time', () => {
  const result = applyDevTaskTransition(task(), 'in_progress', {
    now: new Date('2026-06-25T03:30:00.000Z'),
  });

  assert.equal(result.ok, true);
  assert.equal(result.patch?.status, 'in_progress');
  assert.equal(result.patch?.actualStartAt, '2026-06-25T03:30:00.000Z');
});

test('AI dev task without an assignee must be claimed with a plan before starting', () => {
  const draft = task({
    assigneeId: '',
    expectedStartAt: '',
    expectedEndAt: '',
    aiDraft: true,
  });

  assert.equal(needsDevTaskClaim(draft), true);
  assert.equal(hasDevTaskPlan(draft), false);
  assert.equal(canStartDevTask(draft), false);

  const result = applyDevTaskTransition(draft, 'in_progress', {
    now: new Date('2026-06-25T03:30:00.000Z'),
  });

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

test('recommended assignee dev task still needs a plan before starting', () => {
  const draft = task({
    assigneeId: 'Alice',
    expectedStartAt: '',
    expectedEndAt: '',
    aiDraft: true,
  });

  assert.equal(needsDevTaskClaim(draft), false);
  assert.equal(hasDevTaskPlan(draft), false);
  assert.equal(canStartDevTask(draft), false);

  const result = applyDevTaskTransition(draft, 'in_progress', {
    now: new Date('2026-06-25T03:30:00.000Z'),
  });

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

test('in_progress to testing does not write actualEndAt', () => {
  const result = applyDevTaskTransition(task({
    status: 'in_progress',
    actualStartAt: '2026-06-25T03:30:00.000Z',
  }), 'testing', {
    now: new Date('2026-06-25T04:00:00.000Z'),
  });

  assert.equal(result.ok, true);
  assert.equal(result.patch?.actualEndAt, undefined);
});

test('testing to submitted writes actualEndAt', () => {
  const result = applyDevTaskTransition(task({
    status: 'testing',
    actualStartAt: '2026-06-25T03:30:00.000Z',
  }), 'submitted', {
    now: new Date('2026-06-25T05:00:00.000Z'),
  });

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

test('blocked task cannot be submitted to test until unblocked', () => {
  const result = applyDevTaskTransition(task({
    status: 'testing',
    actualStartAt: '2026-06-25T03:30:00.000Z',
    isBlocked: true,
    blockReason: '等待接口联调',
  }), 'submitted', {
    now: new Date('2026-06-25T05:00:00.000Z'),
  });

  assert.equal(result.ok, false);
  assert.equal(result.patch, undefined);
  assert.match(result.message || '', /\u963b\u585e/);
});

test('invalid transition is rejected', () => {
  const result = applyDevTaskTransition(task(), 'submitted', {
    now: new Date('2026-06-25T05:00:00.000Z'),
  });

  assert.equal(result.ok, false);
  assert.match(result.message || '', /不允许/);
});
