import { describe, expect, it } from "vitest";
import {
  CreateRateCardSchema,
  CreateRateCardLineSchema,
  UpdateRateCardSchema,
} from "../schemas/rate-card.schema.js";
import {
  CreateAllocationBaseSchema,
  CreateDemandRequirementBaseSchema,
  CreateAssignmentBaseSchema,
  CreateAllocationSchema,
  CreateDemandRequirementSchema,
  CreateAssignmentSchema,
  FillDemandRequirementSchema,
  ShiftProjectSchema,
  UpdateAllocationHoursSchema,
} from "../schemas/allocation.schema.js";
import {
  CreateEstimateSchema,
  EstimateDemandLineSchema,
  ScopeItemSchema,
  EstimateAssumptionSchema,
  ResourceCostSnapshotSchema,
  EstimateMetricSchema,
  EstimateExportSummarySchema,
} from "../schemas/estimate.schema.js";
import { AllocationStatus, EstimateStatus, EstimateVersionStatus, EstimateExportFormat } from "../types/enums.js";

// ─── Rate Card Schemas ──────────────────────────────────────────────────────

describe("CreateRateCardSchema", () => {
  it("accepts valid input with defaults", () => {
    const result = CreateRateCardSchema.parse({ name: "Standard Rates" });
    expect(result.name).toBe("Standard Rates");
    expect(result.currency).toBe("EUR");
    expect(result.lines).toEqual([]);
  });

  it("accepts full input with lines and clientId", () => {
    const result = CreateRateCardSchema.parse({
      name: "Client Rate Card",
      currency: "USD",
      clientId: "client-1",
      effectiveFrom: "2026-01-01",
      effectiveTo: "2026-12-31",
      source: "Import",
      lines: [{ costRateCents: 5000 }],
    });
    expect(result.clientId).toBe("client-1");
    expect(result.lines).toHaveLength(1);
    expect(result.effectiveFrom).toBeInstanceOf(Date);
  });

  it("rejects empty name", () => {
    expect(() => CreateRateCardSchema.parse({ name: "" })).toThrow();
  });

  it("rejects invalid currency length", () => {
    expect(() =>
      CreateRateCardSchema.parse({ name: "Test", currency: "EURO" }),
    ).toThrow();
  });
});

describe("CreateRateCardLineSchema", () => {
  it("accepts minimal input", () => {
    const result = CreateRateCardLineSchema.parse({ costRateCents: 8000 });
    expect(result.costRateCents).toBe(8000);
    expect(result.attributes).toEqual({});
  });

  it("accepts full input with all optional fields", () => {
    const result = CreateRateCardLineSchema.parse({
      roleId: "role-1",
      chapter: "3D",
      location: "Munich",
      seniority: "Senior",
      workType: "Production",
      serviceGroup: "CGI",
      costRateCents: 8000,
      billRateCents: 12000,
      machineRateCents: 500,
      attributes: { tier: "A" },
    });
    expect(result.chapter).toBe("3D");
    expect(result.billRateCents).toBe(12000);
  });

  it("rejects negative cost rate", () => {
    expect(() => CreateRateCardLineSchema.parse({ costRateCents: -1 })).toThrow();
  });

  it("rejects non-integer cost rate", () => {
    expect(() => CreateRateCardLineSchema.parse({ costRateCents: 100.5 })).toThrow();
  });
});

describe("UpdateRateCardSchema", () => {
  it("accepts partial updates", () => {
    const result = UpdateRateCardSchema.parse({ name: "Updated" });
    expect(result.name).toBe("Updated");
    expect(result.currency).toBeUndefined();
  });

  it("accepts nullable clientId", () => {
    const result = UpdateRateCardSchema.parse({ clientId: null });
    expect(result.clientId).toBeNull();
  });

  it("accepts nullable effectiveFrom", () => {
    const result = UpdateRateCardSchema.parse({ effectiveFrom: null });
    expect(result.effectiveFrom).toBeNull();
  });
});

// ─── Allocation Schemas ─────────────────────────────────────────────────────

describe("CreateDemandRequirementSchema", () => {
  const validDemand = {
    projectId: "proj-1",
    startDate: "2026-03-01",
    endDate: "2026-03-31",
    hoursPerDay: 8,
    percentage: 100,
  };

  it("accepts valid demand requirement", () => {
    const result = CreateDemandRequirementSchema.parse(validDemand);
    expect(result.projectId).toBe("proj-1");
    expect(result.headcount).toBe(1);
    expect(result.status).toBe(AllocationStatus.PROPOSED);
    expect(result.metadata).toEqual({});
  });

  it("rejects end date before start date", () => {
    expect(() =>
      CreateDemandRequirementSchema.parse({
        ...validDemand,
        startDate: "2026-03-31",
        endDate: "2026-03-01",
      }),
    ).toThrow("End date must be after start date");
  });

  it("rejects hoursPerDay > 24", () => {
    expect(() =>
      CreateDemandRequirementBaseSchema.parse({ ...validDemand, hoursPerDay: 25 }),
    ).toThrow();
  });

  it("rejects percentage > 100", () => {
    expect(() =>
      CreateDemandRequirementBaseSchema.parse({ ...validDemand, percentage: 101 }),
    ).toThrow();
  });
});

describe("CreateAssignmentSchema", () => {
  const validAssignment = {
    resourceId: "res-1",
    projectId: "proj-1",
    startDate: "2026-03-01",
    endDate: "2026-03-31",
    hoursPerDay: 8,
    percentage: 100,
  };

  it("accepts valid assignment", () => {
    const result = CreateAssignmentSchema.parse(validAssignment);
    expect(result.resourceId).toBe("res-1");
    expect(result.status).toBe(AllocationStatus.PROPOSED);
  });

  it("requires resourceId", () => {
    const { resourceId: _, ...withoutResource } = validAssignment;
    expect(() => CreateAssignmentBaseSchema.parse(withoutResource)).toThrow();
  });

  it("rejects end date before start date", () => {
    expect(() =>
      CreateAssignmentSchema.parse({
        ...validAssignment,
        startDate: "2026-03-31",
        endDate: "2026-03-01",
      }),
    ).toThrow("End date must be after start date");
  });
});

describe("FillDemandRequirementSchema", () => {
  it("accepts valid fill input", () => {
    const result = FillDemandRequirementSchema.parse({
      demandRequirementId: "demand-1",
      resourceId: "res-1",
    });
    expect(result.demandRequirementId).toBe("demand-1");
    expect(result.hoursPerDay).toBeUndefined();
  });

  it("rejects hoursPerDay below 0.5", () => {
    expect(() =>
      FillDemandRequirementSchema.parse({
        demandRequirementId: "d-1",
        resourceId: "r-1",
        hoursPerDay: 0.3,
      }),
    ).toThrow();
  });
});

describe("ShiftProjectSchema", () => {
  it("accepts valid shift", () => {
    const result = ShiftProjectSchema.parse({
      projectId: "proj-1",
      newStartDate: "2026-04-01",
      newEndDate: "2026-06-30",
    });
    expect(result.projectId).toBe("proj-1");
    expect(result.newStartDate).toBeInstanceOf(Date);
  });

  it("rejects end before start", () => {
    expect(() =>
      ShiftProjectSchema.parse({
        projectId: "proj-1",
        newStartDate: "2026-06-30",
        newEndDate: "2026-04-01",
      }),
    ).toThrow("New end date must be after new start date");
  });
});

// ─── Estimate Schemas ───────────────────────────────────────────────────────

describe("CreateEstimateSchema", () => {
  it("accepts minimal input", () => {
    const result = CreateEstimateSchema.parse({ name: "Q2 Estimate" });
    expect(result.name).toBe("Q2 Estimate");
    expect(result.baseCurrency).toBe("EUR");
    expect(result.status).toBe(EstimateStatus.DRAFT);
    expect(result.assumptions).toEqual([]);
    expect(result.scopeItems).toEqual([]);
    expect(result.demandLines).toEqual([]);
    expect(result.resourceSnapshots).toEqual([]);
    expect(result.metrics).toEqual([]);
  });

  it("rejects empty name", () => {
    expect(() => CreateEstimateSchema.parse({ name: "" })).toThrow();
  });
});

describe("EstimateDemandLineSchema", () => {
  it("accepts minimal demand line", () => {
    const result = EstimateDemandLineSchema.parse({
      name: "3D Artist",
      hours: 160,
    });
    expect(result.name).toBe("3D Artist");
    expect(result.hours).toBe(160);
    expect(result.lineType).toBe("LABOR");
    expect(result.currency).toBe("EUR");
    expect(result.costRateCents).toBe(0);
    expect(result.monthlySpread).toEqual({});
    expect(result.metadata).toEqual({});
  });

  it("accepts full demand line with metadata", () => {
    const result = EstimateDemandLineSchema.parse({
      name: "Senior 3D Artist",
      hours: 320,
      days: 40,
      fte: 1,
      roleId: "role-1",
      resourceId: "res-1",
      chapter: "3D Visualization",
      costRateCents: 8000,
      billRateCents: 12000,
      costTotalCents: 256_000_0,
      priceTotalCents: 384_000_0,
      monthlySpread: { "2026-03": 80, "2026-04": 80 },
      metadata: {
        calculation: {
          costRateMode: "resource",
          billRateMode: "manual",
        },
      },
    });
    expect(result.chapter).toBe("3D Visualization");
    expect(result.monthlySpread["2026-03"]).toBe(80);
    expect(result.metadata.calculation?.costRateMode).toBe("resource");
  });

  it("rejects negative hours", () => {
    expect(() =>
      EstimateDemandLineSchema.parse({ name: "Test", hours: -1 }),
    ).toThrow();
  });
});

describe("ScopeItemSchema", () => {
  it("accepts valid scope item", () => {
    const result = ScopeItemSchema.parse({
      sequenceNo: 1,
      scopeType: "shot",
      name: "Hero shot exterior",
    });
    expect(result.sequenceNo).toBe(1);
    expect(result.technicalSpec).toEqual({});
    expect(result.metadata).toEqual({});
  });

  it("rejects negative sequenceNo", () => {
    expect(() =>
      ScopeItemSchema.parse({ sequenceNo: -1, scopeType: "shot", name: "Test" }),
    ).toThrow();
  });
});

describe("ResourceCostSnapshotSchema", () => {
  it("accepts valid snapshot", () => {
    const result = ResourceCostSnapshotSchema.parse({
      displayName: "John Doe",
      lcrCents: 6000,
      ucrCents: 4000,
    });
    expect(result.displayName).toBe("John Doe");
    expect(result.currency).toBe("EUR");
  });

  it("rejects negative LCR", () => {
    expect(() =>
      ResourceCostSnapshotSchema.parse({
        displayName: "Test",
        lcrCents: -100,
        ucrCents: 0,
      }),
    ).toThrow();
  });
});

describe("EstimateExportSummarySchema", () => {
  it("accepts valid export summary", () => {
    const result = EstimateExportSummarySchema.parse({
      estimateId: "est-1",
      estimateName: "Test Estimate",
      versionId: "ver-1",
      versionNumber: 1,
      versionStatus: EstimateVersionStatus.APPROVED,
      baseCurrency: "EUR",
      assumptionCount: 5,
      scopeItemCount: 10,
      demandLineCount: 8,
      resourceSnapshotCount: 3,
      totalHours: 1200,
      totalCostCents: 600_000_00,
      totalPriceCents: 900_000_00,
      marginCents: 300_000_00,
      marginPercent: 33.33,
    });
    expect(result.marginPercent).toBeCloseTo(33.33);
  });
});
