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

const { createAuditEntry } = vi.hoisted(() => ({
  createAuditEntry: vi.fn(),
}));

vi.mock("../lib/audit.js", () => ({
  createAuditEntry,
}));

import {
  addRateCardLine,
  createRateCard,
  replaceRateCardLines,
} from "../router/rate-card-procedure-support.js";

function createManagerContext(db: Record<string, unknown>) {
  return {
    db: db as never,
    dbUser: {
      id: "manager_1",
      systemRole: SystemRole.MANAGER,
      permissionOverrides: null,
    },
  };
}

describe("rate-card procedure support", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("creates a rate card and writes a summarized audit entry", async () => {
    const create = vi.fn().mockResolvedValue({
      id: "rc_1",
      name: "Standard 2026",
      lines: [{ id: "line_1" }],
    });

    const result = await createRateCard(createManagerContext({
      rateCard: { create },
    }), {
      name: "Standard 2026",
      currency: "EUR",
      lines: [{
        costRateCents: 9_500,
        billRateCents: 14_000,
        chapter: "Delivery",
        attributes: {},
      }],
    });

    expect(result).toEqual({
      id: "rc_1",
      name: "Standard 2026",
      lines: [{ id: "line_1" }],
    });
    expect(create).toHaveBeenCalledWith(expect.objectContaining({
      data: expect.objectContaining({
        name: "Standard 2026",
        currency: "EUR",
      }),
    }));
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      entityType: "RateCard",
      entityId: "rc_1",
      action: "CREATE",
      after: {
        name: "Standard 2026",
        currency: "EUR",
        lineCount: 1,
      },
    }));
  });

  it("adds a line and uses the rate card name in audit metadata", async () => {
    const lineCreate = vi.fn().mockResolvedValue({
      id: "line_1",
      rateCardId: "rc_1",
      chapter: "Delivery",
      costRateCents: 9_500,
      billRateCents: 14_000,
    });

    const result = await addRateCardLine(createManagerContext({
      rateCard: {
        findUnique: vi.fn().mockResolvedValue({ id: "rc_1", name: "Standard 2026" }),
      },
      rateCardLine: {
        create: lineCreate,
      },
    }), {
      rateCardId: "rc_1",
      line: {
        costRateCents: 9_500,
        billRateCents: 14_000,
        chapter: "Delivery",
        attributes: {},
      },
    });

    expect(result.id).toBe("line_1");
    expect(lineCreate).toHaveBeenCalledWith(expect.objectContaining({
      data: expect.objectContaining({
        rateCardId: "rc_1",
        chapter: "Delivery",
      }),
    }));
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      entityType: "RateCardLine",
      entityId: "line_1",
      entityName: "Standard 2026 - Delivery",
    }));
  });

  it("replaces lines inside a transaction and audits the replacement count", async () => {
    const deleteMany = vi.fn().mockResolvedValue({ count: 2 });
    const create = vi.fn()
      .mockResolvedValueOnce({ id: "line_1", rateCardId: "rc_1", costRateCents: 8_000 })
      .mockResolvedValueOnce({ id: "line_2", rateCardId: "rc_1", costRateCents: 9_500 });

    const db = {
      rateCard: {
        findUnique: vi.fn().mockResolvedValue({ id: "rc_1", name: "Standard 2026" }),
      },
      rateCardLine: {},
      $transaction: vi.fn(async (callback: (tx: { rateCardLine: { deleteMany: typeof deleteMany; create: typeof create } }) => unknown) => callback({
        rateCardLine: { deleteMany, create },
      })),
    };

    const result = await replaceRateCardLines(createManagerContext(db), {
      rateCardId: "rc_1",
      lines: [
        { costRateCents: 8_000, chapter: "Delivery", attributes: {} },
        { costRateCents: 9_500, chapter: "Pipeline", attributes: {} },
      ],
    });

    expect(deleteMany).toHaveBeenCalledWith({ where: { rateCardId: "rc_1" } });
    expect(create).toHaveBeenCalledTimes(2);
    expect(result).toHaveLength(2);
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      entityType: "RateCard",
      entityId: "rc_1",
      summary: "Replaced all lines with 2 new lines",
      after: { replacedLineCount: 2 },
    }));
  });
});
