import { PermissionKey } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { describe, expect, it, vi } from "vitest";
import {
  exportProjectsCsv,
  exportResourcesCsv,
  importCsv,
} from "../router/import-export-procedure-support.js";

function createContext(db: Record<string, unknown>, permissions?: PermissionKey[]) {
  return {
    db: db as never,
    permissions: new Set(permissions ?? []),
  };
}

describe("import-export procedure support", () => {
  it("exports active resources with visible custom fields as CSV", async () => {
    const csv = await exportResourcesCsv(
      createContext({
        resource: {
          findMany: vi.fn().mockResolvedValue([
            {
              eid: "E-001",
              displayName: "Ada Lovelace",
              email: "ada@example.com",
              chapter: "Consulting",
              lcrCents: 10000,
              ucrCents: 20000,
              currency: "EUR",
              chargeabilityTarget: 0.8,
              dynamicFields: { favoriteTool: "Excel, Sheets" },
            },
          ]),
        },
        blueprint: {
          findMany: vi.fn().mockResolvedValue([
            {
              fieldDefs: [
                { key: "favoriteTool", label: "Favorite Tool", showInList: true },
                { key: "privateNote", label: "Private Note", showInList: false },
              ],
            },
          ]),
        },
      }),
    );

    expect(csv).toBe(
      "eid,displayName,email,chapter,lcrCents,ucrCents,currency,chargeabilityTarget,Favorite Tool\n"
      + 'E-001,Ada Lovelace,ada@example.com,Consulting,10000,20000,EUR,0.8,"Excel, Sheets"',
    );
  });

  it("exports projects with formatted dates and custom columns", async () => {
    const csv = await exportProjectsCsv(
      createContext({
        project: {
          findMany: vi.fn().mockResolvedValue([
            {
              shortCode: "APO",
              name: "Apollo",
              orderType: "TIME_AND_MATERIAL",
              status: "ACTIVE",
              budgetCents: 300000,
              startDate: new Date("2026-03-01T00:00:00.000Z"),
              endDate: new Date("2026-06-30T00:00:00.000Z"),
              winProbability: 80,
              dynamicFields: { sponsor: "Finance" },
            },
          ]),
        },
        blueprint: {
          findMany: vi.fn().mockResolvedValue([
            { fieldDefs: [{ key: "sponsor", label: "Sponsor", showInList: true }] },
          ]),
        },
      }),
    );

    expect(csv).toBe(
      "shortCode,name,orderType,status,budgetCents,startDate,endDate,winProbability,Sponsor\n"
      + "APO,Apollo,TIME_AND_MATERIAL,ACTIVE,300000,2026-03-01,2026-06-30,80,Finance",
    );
  });

  it("runs dry-run imports without touching the database", async () => {
    const resourceFindFirst = vi.fn();
    const auditCreate = vi.fn();

    const result = await importCsv(
      createContext(
        {
          resource: {
            findFirst: resourceFindFirst,
            update: vi.fn(),
          },
          auditLog: {
            create: auditCreate,
          },
        },
        [PermissionKey.IMPORT_DATA],
      ),
      {
        entityType: "resources",
        rows: [{ eid: "E-001", displayName: "Ada Lovelace" }],
        dryRun: true,
      },
    );

    expect(result).toEqual({
      total: 1,
      created: 0,
      updated: 0,
      errors: [],
      dryRun: true,
      message: "Dry run: 1 rows validated",
    });
    expect(resourceFindFirst).not.toHaveBeenCalled();
    expect(auditCreate).not.toHaveBeenCalled();
  });

  it("updates matching resources and records import errors", async () => {
    const resourceFindFirst = vi
      .fn()
      .mockResolvedValueOnce({
        id: "res_1",
        displayName: "Ada",
        email: "ada-old@example.com",
        chapter: "Old",
        lcrCents: 9000,
      })
      .mockResolvedValueOnce(null);
    const resourceUpdate = vi.fn().mockResolvedValue({ id: "res_1" });
    const auditCreate = vi.fn().mockResolvedValue({ id: "audit_1" });

    const result = await importCsv(
      createContext(
        {
          resource: {
            findFirst: resourceFindFirst,
            update: resourceUpdate,
          },
          auditLog: {
            create: auditCreate,
          },
        },
        [PermissionKey.IMPORT_DATA],
      ),
      {
        entityType: "resources",
        rows: [
          {
            eid: "E-001",
            displayName: "Ada Lovelace",
            email: "ada@example.com",
            chapter: "Consulting",
            lcrCents: "12000",
          },
          {
            eid: "E-404",
            displayName: "Missing User",
          },
        ],
        dryRun: false,
      },
    );

    expect(resourceUpdate).toHaveBeenCalledWith({
      where: { id: "res_1" },
      data: {
        displayName: "Ada Lovelace",
        email: "ada@example.com",
        chapter: "Consulting",
        lcrCents: 12000,
      },
    });
    expect(auditCreate).toHaveBeenCalledWith({
      data: {
        entityType: "resources",
        entityId: "bulk-import",
        action: "IMPORT",
        changes: {
          summary: {
            total: 2,
            created: 0,
            updated: 1,
            errors: [{ row: 2, message: "New resource creation via import requires full data" }],
            dryRun: false,
          },
        },
      },
    });
    expect(result).toEqual({
      total: 2,
      created: 0,
      updated: 1,
      errors: [{ row: 2, message: "New resource creation via import requires full data" }],
      dryRun: false,
    });
  });

  it("rejects imports without the explicit import permission", async () => {
    await expect(
      importCsv(
        createContext({
          resource: {
            findFirst: vi.fn(),
            update: vi.fn(),
          },
          auditLog: {
            create: vi.fn(),
          },
        }),
        {
          entityType: "resources",
          rows: [],
          dryRun: true,
        },
      ),
    ).rejects.toEqual(
      expect.objectContaining<Partial<TRPCError>>({
        code: "FORBIDDEN",
        message: `Permission required: ${PermissionKey.IMPORT_DATA}`,
      }),
    );
  });
});
