import { describe, expect, it } from "vitest";
import { canGoNext } from "./useProjectWizardForm.js";
import { makeDefaultState, makeReq } from "./types.js";
import type { WizardState } from "./types.js";

function stateWith(overrides: Partial<WizardState>): WizardState {
  return { ...makeDefaultState(), ...overrides };
}

describe("canGoNext", () => {
  // ─── Step 0: Blueprint & Identity ────────────────────────────────────────────

  describe("step 0", () => {
    it("returns true when shortCode and name are valid", () => {
      const state = stateWith({ shortCode: "PRJ-01", name: "My Project" });
      expect(canGoNext(0, state)).toBe(true);
    });

    it("returns false when shortCode is empty", () => {
      const state = stateWith({ shortCode: "", name: "My Project" });
      expect(canGoNext(0, state)).toBe(false);
    });

    it("returns false when shortCode contains lowercase letters", () => {
      const state = stateWith({ shortCode: "prj01", name: "My Project" });
      expect(canGoNext(0, state)).toBe(false);
    });

    it("returns false when name is empty", () => {
      const state = stateWith({ shortCode: "PRJ01", name: "" });
      expect(canGoNext(0, state)).toBe(false);
    });

    it("returns false when name is only whitespace", () => {
      const state = stateWith({ shortCode: "PRJ01", name: "   " });
      expect(canGoNext(0, state)).toBe(false);
    });

    it("returns false when a required blueprint field is missing", () => {
      const state = stateWith({
        shortCode: "PRJ01",
        name: "Test",
        blueprintFieldDefs: [
          {
            id: "f1",
            key: "field1",
            type: "TEXT" as never,
            label: "Field 1",
            required: true,
            order: 0,
          },
        ],
        dynamicFields: {},
      });
      expect(canGoNext(0, state)).toBe(false);
    });

    it("returns true when required blueprint field is filled", () => {
      const state = stateWith({
        shortCode: "PRJ01",
        name: "Test",
        blueprintFieldDefs: [
          {
            id: "f1",
            key: "field1",
            type: "TEXT" as never,
            label: "Field 1",
            required: true,
            order: 0,
          },
        ],
        dynamicFields: { field1: "value" },
      });
      expect(canGoNext(0, state)).toBe(true);
    });

    it("returns false when required field is an empty array", () => {
      const state = stateWith({
        shortCode: "PRJ01",
        name: "Test",
        blueprintFieldDefs: [
          {
            id: "f2",
            key: "tags",
            type: "TAG_LIST" as never,
            label: "Tags",
            required: true,
            order: 0,
          },
        ],
        dynamicFields: { tags: [] },
      });
      expect(canGoNext(0, state)).toBe(false);
    });

    it("ignores non-required fields", () => {
      const state = stateWith({
        shortCode: "PRJ01",
        name: "Test",
        blueprintFieldDefs: [
          {
            id: "f3",
            key: "optional",
            type: "TEXT" as never,
            label: "Optional",
            required: false,
            order: 0,
          },
        ],
        dynamicFields: {},
      });
      expect(canGoNext(0, state)).toBe(true);
    });
  });

  // ─── Step 1: Timeline & Budget ─────────────────────────────────────────────

  describe("step 1", () => {
    it("returns true with valid dates and no budget", () => {
      const state = stateWith({
        startDate: "2026-01-01",
        endDate: "2026-12-31",
        budgetEur: "",
      });
      expect(canGoNext(1, state)).toBe(true);
    });

    it("returns true with valid dates and positive budget", () => {
      const state = stateWith({
        startDate: "2026-01-01",
        endDate: "2026-12-31",
        budgetEur: "50000",
      });
      expect(canGoNext(1, state)).toBe(true);
    });

    it("returns false when endDate is before startDate", () => {
      const state = stateWith({
        startDate: "2026-12-31",
        endDate: "2026-01-01",
      });
      expect(canGoNext(1, state)).toBe(false);
    });

    it("returns false when startDate is empty", () => {
      const state = stateWith({ startDate: "", endDate: "2026-12-31" });
      expect(canGoNext(1, state)).toBe(false);
    });

    it("returns false when budget is negative", () => {
      const state = stateWith({
        startDate: "2026-01-01",
        endDate: "2026-12-31",
        budgetEur: "-100",
      });
      expect(canGoNext(1, state)).toBe(false);
    });

    it("returns true when budget is zero", () => {
      const state = stateWith({
        startDate: "2026-01-01",
        endDate: "2026-12-31",
        budgetEur: "0",
      });
      expect(canGoNext(1, state)).toBe(true);
    });
  });

  // ─── Step 2: Staffing Demand ─────────────────────────────────────────────────

  describe("step 2", () => {
    it("returns true with no staffing requirements", () => {
      const state = stateWith({ staffingReqs: [] });
      expect(canGoNext(2, state)).toBe(true);
    });

    it("returns true with a valid requirement", () => {
      const req = { ...makeReq(), role: "Developer", hoursPerDay: 8, headcount: 1 };
      const state = stateWith({ staffingReqs: [req] });
      expect(canGoNext(2, state)).toBe(true);
    });

    it("returns false when a requirement has empty role and no roleId", () => {
      const req = { ...makeReq(), role: "", hoursPerDay: 8, headcount: 1 };
      const state = stateWith({ staffingReqs: [req] });
      expect(canGoNext(2, state)).toBe(false);
    });

    it("returns true when a requirement has roleId but empty role string", () => {
      const req = { ...makeReq(), role: "", roleId: "role_1", hoursPerDay: 8, headcount: 1 };
      const state = stateWith({ staffingReqs: [req] });
      expect(canGoNext(2, state)).toBe(true);
    });

    it("returns false when hoursPerDay is zero", () => {
      const req = { ...makeReq(), role: "Dev", hoursPerDay: 0, headcount: 1 };
      const state = stateWith({ staffingReqs: [req] });
      expect(canGoNext(2, state)).toBe(false);
    });

    it("returns false when headcount is less than 1", () => {
      const req = { ...makeReq(), role: "Dev", hoursPerDay: 8, headcount: 0 };
      const state = stateWith({ staffingReqs: [req] });
      expect(canGoNext(2, state)).toBe(false);
    });
  });

  // ─── Steps 3+ ────────────────────────────────────────────────────────────────

  describe("steps 3 and 4", () => {
    it("always returns true for step 3", () => {
      expect(canGoNext(3, makeDefaultState())).toBe(true);
    });

    it("always returns true for step 4", () => {
      expect(canGoNext(4, makeDefaultState())).toBe(true);
    });
  });
});

describe("makeDefaultState", () => {
  it("returns a state with empty shortCode and name", () => {
    const state = makeDefaultState();
    expect(state.shortCode).toBe("");
    expect(state.name).toBe("");
  });

  it("defaults to CHARGEABLE order type", () => {
    expect(makeDefaultState().orderType).toBe("CHARGEABLE");
  });

  it("defaults to 100% win probability", () => {
    expect(makeDefaultState().winProbability).toBe(100);
  });

  it("defaults to saveAsDraft true", () => {
    expect(makeDefaultState().saveAsDraft).toBe(true);
  });

  it("sets startDate and endDate to today", () => {
    const state = makeDefaultState();
    expect(state.startDate).toMatch(/^\d{4}-\d{2}-\d{2}$/);
    expect(state.endDate).toBe(state.startDate);
  });
});

describe("makeReq", () => {
  it("creates a requirement with a unique id", () => {
    const a = makeReq();
    const b = makeReq();
    expect(a.id).not.toBe(b.id);
  });

  it("defaults to 8 hours per day", () => {
    expect(makeReq().hoursPerDay).toBe(8);
  });

  it("defaults to headcount 1", () => {
    expect(makeReq().headcount).toBe(1);
  });

  it("starts with empty skills", () => {
    const req = makeReq();
    expect(req.requiredSkills).toEqual([]);
    expect(req.preferredSkills).toEqual([]);
  });
});
