import { beforeEach, describe, expect, it, vi } from 'vitest';
import { BillingType, QaResult, TaskStage } from '@prisma/client';

vi.mock('../db', () => ({
  prisma: {
    task: {
      findMany: vi.fn().mockResolvedValue([]),
      findUnique: vi.fn().mockResolvedValue(null),
    },
    bug: {
      findMany: vi.fn().mockResolvedValue([]),
    },
  },
}));

import { validateGateA, validateGateB, validateGateC } from './workflow';

function baseTask(overrides: Record<string, unknown> = {}) {
  return {
    id: 't1',
    code: 'ZPT-999',
    title: 'Test',
    projectId: 'p1',
    description: 'Desc',
    acceptanceCriteria: 'AC',
    stage: TaskStage.Backlog,
    assigneeId: 'e1',
    allocatedHours: 8,
    baAnalysisComplete: true,
    developmentComplete: false,
    unitTestEvidence: null,
    developerCompletionLog: null,
    codeReviewApproved: false,
    qaResult: null,
    qaEvidence: null,
    acceptanceSatisfied: false,
    testerApproval: false,
    parentId: null,
    dependencies: [],
    assignments: [{ employeeId: 'e1', allocatedHours: 8 }],
    attachments: [{ kind: 'requirement' }],
    internalStatusId: 'st-ready',
    project: {
      id: 'p1',
      requireAssigneeHours: true,
      requireBaAnalysis: true,
      requireDescriptionAc: true,
      requireRequirementDoc: true,
      requireReadyForDevelopment: true,
      enforceRequiredSubtasks: true,
      requireDevelopmentComplete: true,
      requireUnitTestEvidence: true,
      allowTypedEvidence: false,
      enforceDependencies: true,
      requireCodeReview: true,
      requireQaPassed: true,
      requireQaEvidence: true,
      blockDoneOnBlockingBugs: true,
      requireAcceptance: true,
      requireTesterApproval: true,
      requireBugRetestEvidence: true,
      internalStatuses: [
        { id: 'st-ready', text: 'Ready for Development' },
        { id: 'st-created', text: 'Created' },
      ],
    },
    ...overrides,
  } as any;
}

describe('workflow gates', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('Gate A fails without assignee / hours / BA / AC / ready status', async () => {
    const errors = await validateGateA(
      baseTask({
        assigneeId: null,
        assignments: [],
        allocatedHours: 0,
        baAnalysisComplete: false,
        description: '',
        acceptanceCriteria: '',
        attachments: [],
        internalStatusId: 'st-created',
        project: {
          id: 'p1',
          requireAssigneeHours: true,
          requireBaAnalysis: true,
          requireDescriptionAc: true,
          requireRequirementDoc: true,
          requireReadyForDevelopment: true,
          enforceRequiredSubtasks: true,
          requireDevelopmentComplete: true,
          requireUnitTestEvidence: true,
          allowTypedEvidence: false,
          enforceDependencies: true,
          requireCodeReview: true,
          requireQaPassed: true,
          requireQaEvidence: true,
          blockDoneOnBlockingBugs: true,
          requireAcceptance: true,
          requireTesterApproval: true,
          requireBugRetestEvidence: true,
          internalStatuses: [
            { id: 'st-ready', text: 'Ready for Development' },
            { id: 'st-created', text: 'Created' },
          ],
        },
      })
    );
    expect(errors.some((e) => /assignee/i.test(e))).toBe(true);
    expect(errors.some((e) => /Allocated hours/i.test(e))).toBe(true);
    expect(errors.some((e) => /BA/i.test(e))).toBe(true);
    expect(errors.some((e) => /description/i.test(e))).toBe(true);
    expect(errors.some((e) => /Acceptance/i.test(e))).toBe(true);
    expect(errors.some((e) => /Ready for Development/i.test(e))).toBe(true);
  });

  it('Gate A passes for ready backlog task', async () => {
    const errors = await validateGateA(baseTask());
    expect(errors).toEqual([]);
  });

  it('Gate B requires development complete and evidence', async () => {
    const errors = await validateGateB(
      baseTask({
        stage: TaskStage.InProgress,
        developmentComplete: false,
        unitTestEvidence: null,
        developerCompletionLog: '',
      })
    );
    expect(errors.some((e) => /Development/i.test(e))).toBe(true);
    expect(errors.some((e) => /Unit-test evidence/i.test(e))).toBe(true);
    expect(errors.some((e) => /completion log/i.test(e))).toBe(false);
  });

  it('Gate B rejects typed done when project disallows typed evidence', async () => {
    const errors = await validateGateB(
      baseTask({
        developmentComplete: true,
        developerCompletionLog: 'Done work',
        unitTestEvidence: { type: 'typed', note: 'done' },
        project: {
          id: 'p1',
          requireDevelopmentComplete: true,
          requireUnitTestEvidence: true,
          allowTypedEvidence: false,
          enforceRequiredSubtasks: true,
          enforceDependencies: true,
        },
      })
    );
    expect(errors.some((e) => /Typed evidence is not allowed/i.test(e))).toBe(true);
  });

  it('Gate A skips assignee check when project rule is off', async () => {
    const errors = await validateGateA(
      baseTask({
        assigneeId: null,
        assignments: [],
        allocatedHours: 0,
        project: {
          ...baseTask().project,
          requireAssigneeHours: false,
        },
      })
    );
    expect(errors.some((e) => /assignee/i.test(e))).toBe(false);
    expect(errors.some((e) => /Allocated hours/i.test(e))).toBe(false);
  });

  it('Gate C requires review, QA passed, evidence, acceptance, tester', async () => {
    const errors = await validateGateC(
      baseTask({
        stage: TaskStage.CodeReviewQA,
        codeReviewApproved: false,
        qaResult: QaResult.Failed,
        qaEvidence: null,
        acceptanceSatisfied: false,
        testerApproval: false,
      })
    );
    expect(errors.some((e) => /Code review/i.test(e))).toBe(true);
    expect(errors.some((e) => /QA result must be Passed/i.test(e))).toBe(true);
    expect(errors.some((e) => /QA test evidence/i.test(e))).toBe(true);
    expect(errors.some((e) => /Acceptance/i.test(e))).toBe(true);
    expect(errors.some((e) => /tester/i.test(e))).toBe(true);
  });

  it('Gate C passes when all flags set', async () => {
    const errors = await validateGateC(
      baseTask({
        codeReviewApproved: true,
        qaResult: QaResult.Passed,
        qaEvidence: { type: 'file', note: 'ok' },
        acceptanceSatisfied: true,
        testerApproval: true,
      })
    );
    expect(errors).toEqual([]);
  });
});
