import { beforeEach, describe, expect, it, vi } from "vitest";
import { PROJECT_BRIEF_SELECT } from "../db/selects.js";
import { createAuditEntry } from "../lib/audit.js";
import {
  createCalculationRule,
  deleteCalculationRule,
  getCalculationRuleById,
  listActiveCalculationRules,
  listCalculationRules,
  updateCalculationRule,
} from "../router/calculation-rule-procedure-support.js";

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

function createContext(db: Record<string, unknown>) {
  return {
    db: db as never,
    dbUser: { id: "user_mgr" },
  };
}

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

  it("lists calculation rules with the expected project include", async () => {
    const findMany = vi.fn().mockResolvedValue([{ id: "calc_1" }]);
    const ctx = createContext({
      calculationRule: { findMany },
    });

    const result = await listCalculationRules(ctx);

    expect(result).toEqual([{ id: "calc_1" }]);
    expect(findMany).toHaveBeenCalledWith({
      orderBy: [{ priority: "desc" }, { name: "asc" }],
      include: { project: { select: PROJECT_BRIEF_SELECT } },
    });
  });

  it("gets a rule by id with the expected project include", async () => {
    const findUnique = vi.fn().mockResolvedValue({ id: "calc_1", name: "Rush Fee" });
    const ctx = createContext({
      calculationRule: { findUnique },
    });

    const result = await getCalculationRuleById(ctx, { id: "calc_1" });

    expect(result).toEqual({ id: "calc_1", name: "Rush Fee" });
    expect(findUnique).toHaveBeenCalledWith({
      where: { id: "calc_1" },
      include: { project: { select: PROJECT_BRIEF_SELECT } },
    });
  });

  it("lists only active rules for engine use", async () => {
    const findMany = vi.fn().mockResolvedValue([{ id: "calc_2", isActive: true }]);
    const ctx = createContext({
      calculationRule: { findMany },
    });

    const result = await listActiveCalculationRules(ctx);

    expect(result).toEqual([{ id: "calc_2", isActive: true }]);
    expect(findMany).toHaveBeenCalledWith({
      where: { isActive: true },
      orderBy: [{ priority: "desc" }],
    });
  });

  it("creates a rule and records an audit entry", async () => {
    const created = {
      id: "calc_3",
      name: "Vacation Discount",
      triggerType: "VACATION",
      costEffect: "REDUCE",
      chargeabilityEffect: "COUNT",
      priority: 10,
      isActive: true,
    };
    const create = vi.fn().mockResolvedValue(created);
    const ctx = createContext({
      calculationRule: { create },
    });

    const result = await createCalculationRule(ctx, {
      name: "Vacation Discount",
      triggerType: "VACATION",
      costEffect: "REDUCE",
      chargeabilityEffect: "COUNT",
      priority: 10,
      isActive: true,
    });

    expect(result).toBe(created);
    expect(create).toHaveBeenCalledWith({
      data: {
        name: "Vacation Discount",
        triggerType: "VACATION",
        costEffect: "REDUCE",
        chargeabilityEffect: "COUNT",
        priority: 10,
        isActive: true,
      },
    });
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      db: ctx.db,
      entityType: "CalculationRule",
      entityId: "calc_3",
      entityName: "Vacation Discount",
      action: "CREATE",
      userId: "user_mgr",
      after: created,
      source: "ui",
    }));
  });

  it("updates a rule and records before/after audit snapshots", async () => {
    const before = {
      id: "calc_4",
      name: "Rush Fee",
      priority: 90,
      isActive: true,
    };
    const after = {
      id: "calc_4",
      name: "Rush Fee",
      priority: 95,
      isActive: false,
    };
    const findUnique = vi.fn().mockResolvedValue(before);
    const update = vi.fn().mockResolvedValue(after);
    const ctx = createContext({
      calculationRule: { findUnique, update },
    });

    const result = await updateCalculationRule(ctx, {
      id: "calc_4",
      priority: 95,
      isActive: false,
    });

    expect(result).toBe(after);
    expect(findUnique).toHaveBeenCalledWith({ where: { id: "calc_4" } });
    expect(update).toHaveBeenCalledWith({
      where: { id: "calc_4" },
      data: {
        priority: 95,
        isActive: false,
      },
    });
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      db: ctx.db,
      entityType: "CalculationRule",
      entityId: "calc_4",
      entityName: "Rush Fee",
      action: "UPDATE",
      userId: "user_mgr",
      before,
      after,
      source: "ui",
    }));
  });

  it("deletes a rule and records a delete audit entry", async () => {
    const rule = { id: "calc_5", name: "Legacy Rule" };
    const findUnique = vi.fn().mockResolvedValue(rule);
    const deleteFn = vi.fn().mockResolvedValue(rule);
    const ctx = createContext({
      calculationRule: { findUnique, delete: deleteFn },
    });

    const result = await deleteCalculationRule(ctx, { id: "calc_5" });

    expect(result).toEqual({ success: true });
    expect(findUnique).toHaveBeenCalledWith({ where: { id: "calc_5" } });
    expect(deleteFn).toHaveBeenCalledWith({ where: { id: "calc_5" } });
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      db: ctx.db,
      entityType: "CalculationRule",
      entityId: "calc_5",
      entityName: "Legacy Rule",
      action: "DELETE",
      userId: "user_mgr",
      before: rule,
      source: "ui",
    }));
  });
});
