import { beforeEach, describe, expect, it, vi } from "vitest";
import { PermissionKey, SystemRole } from "@capakraken/shared";

vi.mock("@capakraken/application", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@capakraken/application")>();
  return {
    ...actual,
    approveEstimateVersion: vi.fn(),
    cloneEstimate: vi.fn(),
    createEstimateExport: vi.fn(),
    createEstimatePlanningHandoff: vi.fn(),
    createEstimateRevision: vi.fn(),
    getDashboardBudgetForecast: vi.fn().mockResolvedValue([]),
    getDashboardPeakTimes: vi.fn().mockResolvedValue([]),
    getEstimateById: vi.fn(),
    listAssignmentBookings: vi.fn().mockResolvedValue([]),
    submitEstimateVersion: vi.fn(),
    updateEstimateDraft: vi.fn(),
  };
});

import { executeTool } from "../router/assistant-tools.js";
import {
  createToolContext,
  resetEstimateToolMocks,
} from "./assistant-tools-estimate-test-helpers.js";

describe("assistant estimate creation race tools", () => {
  beforeEach(() => {
    resetEstimateToolMocks();
  });

  it("returns stable assistant errors for estimate creation races", async () => {
    const missingProjectCtx = createToolContext(
      {
        project: {
          findUnique: vi.fn()
            .mockResolvedValueOnce({ id: "project_1" })
            .mockResolvedValueOnce({
              id: "project_1",
              shortCode: "PROJ-1",
              name: "Project One",
              status: "DRAFT",
              startDate: new Date("2026-01-01T00:00:00.000Z"),
              endDate: new Date("2026-12-31T00:00:00.000Z"),
              orderType: "TIME_MATERIAL",
              allocationType: "INT",
              winProbability: 100,
              budgetCents: 100000,
              responsiblePerson: "Peter Parker",
            }),
        },
        estimate: {
          create: vi.fn().mockRejectedValue({
            code: "P2003",
            message: "Foreign key constraint failed",
            meta: { field_name: "Estimate_projectId_fkey" },
          }),
        },
      },
      {
        userRole: SystemRole.MANAGER,
        permissions: [PermissionKey.MANAGE_PROJECTS],
      },
    );

    const missingProjectResult = await executeTool(
      "create_estimate",
      JSON.stringify({ name: "Delivery Estimate", projectId: "project_1" }),
      missingProjectCtx,
    );

    expect(JSON.parse(missingProjectResult.content)).toEqual({
      error: "Project not found with the given criteria.",
    });

    const missingRoleCtx = createToolContext(
      {
        estimate: {
          create: vi.fn().mockRejectedValue({
            code: "P2003",
            message: "Foreign key constraint failed",
            meta: { field_name: "EstimateDemandLine_roleId_fkey" },
          }),
        },
        rateCardLine: {
          findMany: vi.fn().mockResolvedValue([]),
        },
      },
      {
        userRole: SystemRole.MANAGER,
        permissions: [PermissionKey.MANAGE_PROJECTS],
      },
    );

    const missingRoleResult = await executeTool(
      "create_estimate",
      JSON.stringify({
        name: "Delivery Estimate",
        demandLines: [
          {
            roleId: "role_1",
            lineType: "LABOR",
            name: "Design",
            hours: 40,
            costRateCents: 0,
            billRateCents: 0,
            currency: "EUR",
            costTotalCents: 0,
            priceTotalCents: 0,
            monthlySpread: {},
            staffingAttributes: {},
            metadata: {},
          },
        ],
      }),
      missingRoleCtx,
    );

    expect(JSON.parse(missingRoleResult.content)).toEqual({
      error: "Role not found with the given criteria.",
    });

    const cases = [
      {
        name: "missing resource reference",
        payload: {
          name: "Delivery Estimate",
          demandLines: [
            {
              resourceId: "resource_1",
              lineType: "LABOR",
              name: "Animation",
              hours: 40,
              costRateCents: 0,
              billRateCents: 0,
              currency: "EUR",
              costTotalCents: 0,
              priceTotalCents: 0,
              monthlySpread: {},
              staffingAttributes: {},
              metadata: {},
            },
          ],
        },
        rejection: {
          code: "P2003",
          message: "Foreign key constraint failed",
          meta: { field_name: "EstimateDemandLine_resourceId_fkey" },
        },
        expected: "Resource not found with the given criteria.",
      },
      {
        name: "missing scope item reference",
        payload: {
          name: "Delivery Estimate",
          scopeItems: [
            {
              sequenceNo: 1,
              scopeType: "SHOT",
              name: "Shot 010",
              technicalSpec: {},
              metadata: {},
            },
          ],
          demandLines: [
            {
              scopeItemId: "scope_item_missing",
              lineType: "LABOR",
              name: "Lighting",
              hours: 24,
              costRateCents: 0,
              billRateCents: 0,
              currency: "EUR",
              costTotalCents: 0,
              priceTotalCents: 0,
              monthlySpread: {},
              staffingAttributes: {},
              metadata: {},
            },
          ],
        },
        rejection: {
          code: "P2003",
          message: "Foreign key constraint failed",
          meta: { field_name: "EstimateDemandLine_scopeItemId_fkey" },
        },
        expected: "Estimate scope item not found with the given criteria.",
      },
      {
        name: "generic referenced record race",
        payload: { name: "Delivery Estimate" },
        rejection: {
          code: "P2025",
          message: "Record to create no longer references a valid row",
          meta: { cause: "Dependent record disappeared during nested estimate create" },
        },
        expected: "One of the referenced project, role, resource, or scope items no longer exists.",
      },
    ] as const;

    for (const testCase of cases) {
      const ctx = createToolContext(
        {
          estimate: {
            create: vi.fn().mockRejectedValue(testCase.rejection),
          },
          rateCardLine: {
            findMany: vi.fn().mockResolvedValue([]),
          },
        },
        {
          userRole: SystemRole.MANAGER,
          permissions: [PermissionKey.MANAGE_PROJECTS],
        },
      );

      const result = await executeTool(
        "create_estimate",
        JSON.stringify(testCase.payload),
        ctx,
      );

      expect(JSON.parse(result.content)).toEqual({
        error: testCase.expected,
      });
    }
  });
});
