import { describe, expect, it } from "vitest";
import {
  WeekdayAvailabilitySchema,
  SkillEntrySchema,
  CreateResourceSchema,
  UpdateResourceSchema,
} from "../schemas/resource.schema.js";
import { CreateClientSchema, UpdateClientSchema } from "../schemas/client.schema.js";
import { CreateRoleSchema, UpdateRoleSchema, ResourceRoleSchema } from "../schemas/role.schema.js";
import { ResourceType } from "../types/enums.js";

// ─── WeekdayAvailabilitySchema ───────────────────────────────────────────────

describe("WeekdayAvailabilitySchema", () => {
  it("accepts valid weekday-only input", () => {
    const result = WeekdayAvailabilitySchema.parse({
      monday: 8,
      tuesday: 8,
      wednesday: 8,
      thursday: 8,
      friday: 8,
    });
    expect(result.monday).toBe(8);
    expect(result.saturday).toBeUndefined();
    expect(result.sunday).toBeUndefined();
  });

  it("accepts full input including weekend hours", () => {
    const result = WeekdayAvailabilitySchema.parse({
      monday: 6,
      tuesday: 6,
      wednesday: 6,
      thursday: 6,
      friday: 6,
      saturday: 4,
      sunday: 0,
    });
    expect(result.saturday).toBe(4);
    expect(result.sunday).toBe(0);
  });

  it("accepts boundary values (0 and 24)", () => {
    const result = WeekdayAvailabilitySchema.parse({
      monday: 0,
      tuesday: 24,
      wednesday: 0,
      thursday: 24,
      friday: 0,
    });
    expect(result.monday).toBe(0);
    expect(result.tuesday).toBe(24);
  });

  it("rejects hours above 24", () => {
    expect(() =>
      WeekdayAvailabilitySchema.parse({
        monday: 25,
        tuesday: 8,
        wednesday: 8,
        thursday: 8,
        friday: 8,
      }),
    ).toThrow();
  });

  it("rejects hours below 0", () => {
    expect(() =>
      WeekdayAvailabilitySchema.parse({
        monday: -1,
        tuesday: 8,
        wednesday: 8,
        thursday: 8,
        friday: 8,
      }),
    ).toThrow();
  });
});

// ─── SkillEntrySchema ────────────────────────────────────────────────────────

describe("SkillEntrySchema", () => {
  it("accepts minimal valid skill entry", () => {
    const result = SkillEntrySchema.parse({ skill: "Houdini", proficiency: 3 });
    expect(result.skill).toBe("Houdini");
    expect(result.proficiency).toBe(3);
    expect(result.category).toBeUndefined();
    expect(result.certified).toBeUndefined();
  });

  it("accepts full skill entry with all optional fields", () => {
    const result = SkillEntrySchema.parse({
      skill: "Maya",
      category: "3D Modeling",
      proficiency: 5,
      yearsExperience: 7,
      certified: true,
      isMainSkill: true,
    });
    expect(result.category).toBe("3D Modeling");
    expect(result.yearsExperience).toBe(7);
    expect(result.certified).toBe(true);
    expect(result.isMainSkill).toBe(true);
  });

  it("accepts all valid proficiency literals (1–5)", () => {
    for (const level of [1, 2, 3, 4, 5] as const) {
      const result = SkillEntrySchema.parse({ skill: "Test", proficiency: level });
      expect(result.proficiency).toBe(level);
    }
  });

  it("rejects proficiency outside the 1–5 range", () => {
    expect(() => SkillEntrySchema.parse({ skill: "Test", proficiency: 0 })).toThrow();
    expect(() => SkillEntrySchema.parse({ skill: "Test", proficiency: 6 })).toThrow();
  });

  it("rejects empty skill name", () => {
    expect(() => SkillEntrySchema.parse({ skill: "", proficiency: 3 })).toThrow();
  });

  it("rejects yearsExperience above 50", () => {
    expect(() =>
      SkillEntrySchema.parse({ skill: "Nuke", proficiency: 2, yearsExperience: 51 }),
    ).toThrow();
  });
});

// ─── CreateResourceSchema ────────────────────────────────────────────────────

describe("CreateResourceSchema", () => {
  const minimalResource = {
    eid: "EMP-001",
    displayName: "Jane Smith",
    email: "jane.smith@example.com",
    lcrCents: 6000,
    ucrCents: 4000,
  };

  it("accepts minimal valid resource and applies defaults", () => {
    const result = CreateResourceSchema.parse(minimalResource);
    expect(result.eid).toBe("EMP-001");
    expect(result.currency).toBe("EUR");
    expect(result.chargeabilityTarget).toBe(80);
    expect(result.availability).toEqual({
      monday: 8,
      tuesday: 8,
      wednesday: 8,
      thursday: 8,
      friday: 8,
    });
    expect(result.skills).toEqual([]);
    expect(result.dynamicFields).toEqual({});
  });

  it("accepts full resource with all optional fields populated", () => {
    const result = CreateResourceSchema.parse({
      ...minimalResource,
      chapter: "3D Animation",
      currency: "USD",
      chargeabilityTarget: 75,
      availability: { monday: 6, tuesday: 6, wednesday: 6, thursday: 6, friday: 6 },
      skills: [{ skill: "Maya", proficiency: 4 }],
      dynamicFields: { department: "VFX" },
      blueprintId: "bp-1",
      portfolioUrl: "https://portfolio.example.com",
      roleId: "role-42",
      postalCode: "80331",
      federalState: "BY",
      countryId: "country-de",
      metroCityId: "metro-muc",
      orgUnitId: "ou-1",
      managementLevelGroupId: "mlg-1",
      managementLevelId: "ml-2",
      resourceType: ResourceType.EMPLOYEE,
      chgResponsibility: false,
      rolledOff: false,
      departed: false,
      enterpriseId: "ENT-999",
      clientUnitId: "cu-5",
      fte: 1,
    });
    expect(result.chapter).toBe("3D Animation");
    expect(result.currency).toBe("USD");
    expect(result.resourceType).toBe(ResourceType.EMPLOYEE);
    expect(result.skills).toHaveLength(1);
    expect(result.fte).toBe(1);
  });

  it("applies default availability when omitted", () => {
    const result = CreateResourceSchema.parse(minimalResource);
    const days = ["monday", "tuesday", "wednesday", "thursday", "friday"] as const;
    for (const day of days) {
      expect(result.availability[day]).toBe(8);
    }
  });

  it("rejects invalid email address", () => {
    expect(() =>
      CreateResourceSchema.parse({ ...minimalResource, email: "not-an-email" }),
    ).toThrow();
  });

  it("rejects eid longer than 50 characters", () => {
    expect(() => CreateResourceSchema.parse({ ...minimalResource, eid: "X".repeat(51) })).toThrow();
  });

  it("rejects negative lcrCents", () => {
    expect(() => CreateResourceSchema.parse({ ...minimalResource, lcrCents: -1 })).toThrow();
  });

  it("rejects non-integer rate values", () => {
    expect(() => CreateResourceSchema.parse({ ...minimalResource, lcrCents: 60.5 })).toThrow();
  });

  it("rejects chargeabilityTarget outside 0–100", () => {
    expect(() =>
      CreateResourceSchema.parse({ ...minimalResource, chargeabilityTarget: 101 }),
    ).toThrow();
    expect(() =>
      CreateResourceSchema.parse({ ...minimalResource, chargeabilityTarget: -1 }),
    ).toThrow();
  });

  it("rejects currency not exactly 3 characters", () => {
    expect(() => CreateResourceSchema.parse({ ...minimalResource, currency: "EURO" })).toThrow();
    expect(() => CreateResourceSchema.parse({ ...minimalResource, currency: "EU" })).toThrow();
  });

  it("rejects invalid resourceType enum value", () => {
    expect(() =>
      CreateResourceSchema.parse({ ...minimalResource, resourceType: "CONSULTANT" }),
    ).toThrow();
  });

  it("rejects fte below 0.01 or above 1", () => {
    expect(() => CreateResourceSchema.parse({ ...minimalResource, fte: 0 })).toThrow();
    expect(() => CreateResourceSchema.parse({ ...minimalResource, fte: 1.01 })).toThrow();
  });

  it("accepts empty string as portfolioUrl", () => {
    const result = CreateResourceSchema.parse({
      ...minimalResource,
      portfolioUrl: "",
    });
    expect(result.portfolioUrl).toBe("");
  });

  it("rejects portfolioUrl that is neither a valid URL nor empty string", () => {
    expect(() =>
      CreateResourceSchema.parse({ ...minimalResource, portfolioUrl: "not-a-url" }),
    ).toThrow();
  });

  it("accepts all ResourceType enum values", () => {
    const types = [
      ResourceType.EMPLOYEE,
      ResourceType.FREELANCER,
      ResourceType.APPRENTICE,
      ResourceType.INTERN,
      ResourceType.STUDENT,
    ];
    for (const resourceType of types) {
      const result = CreateResourceSchema.parse({ ...minimalResource, resourceType });
      expect(result.resourceType).toBe(resourceType);
    }
  });
});

// ─── UpdateResourceSchema ────────────────────────────────────────────────────

describe("UpdateResourceSchema", () => {
  it("accepts empty object (all fields optional)", () => {
    const result = UpdateResourceSchema.parse({});
    expect(result.eid).toBeUndefined();
    expect(result.displayName).toBeUndefined();
    expect(result.isActive).toBeUndefined();
  });

  it("accepts partial update with only displayName", () => {
    const result = UpdateResourceSchema.parse({ displayName: "Updated Name" });
    expect(result.displayName).toBe("Updated Name");
    expect(result.email).toBeUndefined();
  });

  it("accepts isActive flag not present in CreateResourceSchema", () => {
    const result = UpdateResourceSchema.parse({ isActive: false });
    expect(result.isActive).toBe(false);
  });

  it("still validates constrained fields when provided", () => {
    expect(() => UpdateResourceSchema.parse({ email: "bad-email" })).toThrow();
    expect(() => UpdateResourceSchema.parse({ chargeabilityTarget: 200 })).toThrow();
  });
});

// ─── CreateClientSchema ──────────────────────────────────────────────────────

describe("CreateClientSchema", () => {
  it("accepts minimal valid client and applies defaults", () => {
    const result = CreateClientSchema.parse({ name: "Acme Corp" });
    expect(result.name).toBe("Acme Corp");
    expect(result.sortOrder).toBe(0);
    expect(result.code).toBeUndefined();
    expect(result.parentId).toBeUndefined();
    expect(result.tags).toBeUndefined();
  });

  it("accepts full client input with all optional fields", () => {
    const result = CreateClientSchema.parse({
      name: "Global Media GmbH",
      code: "GM-001",
      parentId: "parent-42",
      sortOrder: 5,
      tags: ["vip", "premium"],
    });
    expect(result.code).toBe("GM-001");
    expect(result.parentId).toBe("parent-42");
    expect(result.sortOrder).toBe(5);
    expect(result.tags).toEqual(["vip", "premium"]);
  });

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

  it("rejects name longer than 300 characters", () => {
    expect(() => CreateClientSchema.parse({ name: "A".repeat(301) })).toThrow();
  });

  it("rejects tag exceeding 50 characters", () => {
    expect(() => CreateClientSchema.parse({ name: "Client", tags: ["X".repeat(51)] })).toThrow();
  });

  it("rejects non-integer sortOrder", () => {
    expect(() => CreateClientSchema.parse({ name: "Client", sortOrder: 1.5 })).toThrow();
  });

  it("rejects code longer than 50 characters", () => {
    expect(() => CreateClientSchema.parse({ name: "Client", code: "C".repeat(51) })).toThrow();
  });
});

// ─── UpdateClientSchema ──────────────────────────────────────────────────────

describe("UpdateClientSchema", () => {
  it("accepts empty update object (all fields optional)", () => {
    const result = UpdateClientSchema.parse({});
    expect(result.name).toBeUndefined();
    expect(result.isActive).toBeUndefined();
  });

  it("accepts nullable code (clearing the code)", () => {
    const result = UpdateClientSchema.parse({ code: null });
    expect(result.code).toBeNull();
  });

  it("accepts nullable parentId (detaching from parent)", () => {
    const result = UpdateClientSchema.parse({ parentId: null });
    expect(result.parentId).toBeNull();
  });

  it("accepts isActive flag", () => {
    const result = UpdateClientSchema.parse({ isActive: false });
    expect(result.isActive).toBe(false);
  });

  it("still validates name length when provided", () => {
    expect(() => UpdateClientSchema.parse({ name: "" })).toThrow();
    expect(() => UpdateClientSchema.parse({ name: "N".repeat(301) })).toThrow();
  });
});

// ─── CreateRoleSchema ────────────────────────────────────────────────────────

describe("CreateRoleSchema", () => {
  it("accepts minimal valid role (name only)", () => {
    const result = CreateRoleSchema.parse({ name: "3D Artist" });
    expect(result.name).toBe("3D Artist");
    expect(result.description).toBeUndefined();
    expect(result.color).toBeUndefined();
  });

  it("accepts full role with description and color", () => {
    const result = CreateRoleSchema.parse({
      name: "Lead Animator",
      description: "Responsible for animation direction and team leadership.",
      color: "#FF5733",
    });
    expect(result.description).toBe("Responsible for animation direction and team leadership.");
    expect(result.color).toBe("#FF5733");
  });

  it("accepts lowercase hex color", () => {
    const result = CreateRoleSchema.parse({ name: "VFX Artist", color: "#a1b2c3" });
    expect(result.color).toBe("#a1b2c3");
  });

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

  it("rejects name longer than 100 characters", () => {
    expect(() => CreateRoleSchema.parse({ name: "R".repeat(101) })).toThrow();
  });

  it("rejects description longer than 500 characters", () => {
    expect(() => CreateRoleSchema.parse({ name: "Role", description: "D".repeat(501) })).toThrow();
  });

  it("rejects invalid hex color (missing hash)", () => {
    expect(() => CreateRoleSchema.parse({ name: "Role", color: "FF5733" })).toThrow();
  });

  it("rejects invalid hex color (wrong length)", () => {
    expect(() => CreateRoleSchema.parse({ name: "Role", color: "#FFF" })).toThrow();
    expect(() => CreateRoleSchema.parse({ name: "Role", color: "#FF57330" })).toThrow();
  });
});

// ─── UpdateRoleSchema ────────────────────────────────────────────────────────

describe("UpdateRoleSchema", () => {
  it("accepts empty update object (all fields optional)", () => {
    const result = UpdateRoleSchema.parse({});
    expect(result.name).toBeUndefined();
    expect(result.color).toBeUndefined();
    expect(result.isActive).toBeUndefined();
  });

  it("accepts partial update with only name", () => {
    const result = UpdateRoleSchema.parse({ name: "Senior 3D Artist" });
    expect(result.name).toBe("Senior 3D Artist");
  });

  it("accepts isActive flag not present in CreateRoleSchema", () => {
    const result = UpdateRoleSchema.parse({ isActive: true });
    expect(result.isActive).toBe(true);
  });

  it("still validates color format when provided", () => {
    expect(() => UpdateRoleSchema.parse({ color: "invalid-color" })).toThrow();
  });
});

// ─── ResourceRoleSchema ──────────────────────────────────────────────────────

describe("ResourceRoleSchema", () => {
  it("accepts minimal input and applies isPrimary default", () => {
    const result = ResourceRoleSchema.parse({ roleId: "role-1" });
    expect(result.roleId).toBe("role-1");
    expect(result.isPrimary).toBe(false);
  });

  it("accepts explicit isPrimary true", () => {
    const result = ResourceRoleSchema.parse({ roleId: "role-99", isPrimary: true });
    expect(result.isPrimary).toBe(true);
  });

  it("rejects missing roleId", () => {
    expect(() => ResourceRoleSchema.parse({})).toThrow();
    expect(() => ResourceRoleSchema.parse({ isPrimary: true })).toThrow();
  });

  it("rejects non-boolean isPrimary", () => {
    expect(() => ResourceRoleSchema.parse({ roleId: "role-1", isPrimary: "yes" })).toThrow();
  });
});
