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

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

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

vi.mock("@capakraken/engine", () => ({
  expandScopeToEffort: vi.fn().mockReturnValue({
    lines: [
      {
        scopeItemName: "Shot_001",
        discipline: "Compositing",
        chapter: null,
        hours: 16,
        unitMode: "per_frame",
        unitCount: 200,
        hoursPerUnit: 0.08,
      },
    ],
    warnings: [],
    unmatchedScopeItems: [],
  }),
  aggregateByDiscipline: vi.fn().mockReturnValue({
    Compositing: { totalHours: 16, lineCount: 1 },
  }),
}));

import {
  applyEffortRules,
  createEffortRuleSet,
  previewEffortRules,
} from "../router/effort-rule-procedure-support.js";

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

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

function sampleRuleSet(overrides: Record<string, unknown> = {}) {
  return {
    id: "ers_1",
    name: "VFX Standard",
    description: null,
    isDefault: true,
    createdAt: new Date(),
    updatedAt: new Date(),
    rules: [
      {
        id: "er_1",
        ruleSetId: "ers_1",
        scopeType: "shot",
        discipline: "Compositing",
        chapter: null,
        unitMode: "per_frame",
        hoursPerUnit: 0.08,
        description: null,
        sortOrder: 0,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ],
    ...overrides,
  };
}

describe("effort rule procedure support", () => {
  it("creates a rule set and audits it", async () => {
    const create = vi.fn().mockResolvedValue(sampleRuleSet());
    const updateMany = vi.fn().mockResolvedValue({ count: 1 });

    const result = await createEffortRuleSet(createManagerContext({
      effortRuleSet: {
        updateMany,
        create,
      },
    }), {
      name: "VFX Standard",
      description: "Default",
      isDefault: true,
      rules: [],
    });

    expect(updateMany).toHaveBeenCalledWith({
      where: { isDefault: true },
      data: { isDefault: false },
    });
    expect(create).toHaveBeenCalledWith(expect.objectContaining({
      data: expect.objectContaining({
        name: "VFX Standard",
        isDefault: true,
      }),
    }));
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      entityType: "EffortRuleSet",
      action: "CREATE",
      entityId: result.id,
    }));
  });

  it("previews expanded effort using the latest estimate version", async () => {
    const result = await previewEffortRules(createControllerContext({
      estimate: {
        findUnique: vi.fn().mockResolvedValue({
          id: "est_1",
          versions: [
            {
              id: "v_1",
              versionNumber: 2,
              scopeItems: [
                {
                  name: "Shot_001",
                  scopeType: "shot",
                  frameCount: 200,
                  itemCount: null,
                  unitMode: "per_frame",
                },
              ],
            },
          ],
        }),
      },
      effortRuleSet: {
        findUnique: vi.fn().mockResolvedValue(sampleRuleSet()),
      },
    }), {
      estimateId: "est_1",
      ruleSetId: "ers_1",
    });

    expect(result.scopeItemCount).toBe(1);
    expect(result.ruleCount).toBe(1);
    expect(result.aggregated).toEqual({
      Compositing: { totalHours: 16, lineCount: 1 },
    });
  });

  it("replaces demand lines and writes an estimate audit when rules are applied", async () => {
    const deleteMany = vi.fn().mockResolvedValue({ count: 1 });
    const createMany = vi.fn().mockResolvedValue({ count: 1 });
    const auditCreate = vi.fn().mockResolvedValue({});

    const result = await applyEffortRules(createManagerContext({
      estimate: {
        findUnique: vi.fn().mockResolvedValue({
          id: "est_1",
          baseCurrency: "EUR",
          versions: [
            {
              id: "v_1",
              versionNumber: 2,
              status: "WORKING",
              scopeItems: [
                {
                  name: "Shot_001",
                  scopeType: "shot",
                  frameCount: 200,
                  itemCount: null,
                  unitMode: "per_frame",
                },
              ],
              demandLines: [{ id: "old_1" }],
            },
          ],
        }),
      },
      effortRuleSet: {
        findUnique: vi.fn().mockResolvedValue(sampleRuleSet()),
      },
      estimateDemandLine: {
        deleteMany,
        createMany,
      },
      auditLog: {
        create: auditCreate,
      },
    }), {
      estimateId: "est_1",
      ruleSetId: "ers_1",
      mode: "replace",
    });

    expect(deleteMany).toHaveBeenCalledWith({
      where: { estimateVersionId: "v_1" },
    });
    expect(createMany).toHaveBeenCalledTimes(1);
    expect(auditCreate).toHaveBeenCalledTimes(1);
    expect(result).toEqual({
      linesGenerated: 1,
      warnings: [],
      unmatchedScopeItems: [],
    });
  });
});
