import { Tax } from "../tax";

describe("Tax Value Object", () => {
  describe("Creación", () => {
    test("debe crear una tasa de impuesto válida", () => {
      const result = Tax.create({
        value: 2100, // 21.00%
        scale: 2,
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isSuccess).toBe(true);
      if (result.isSuccess) {
        const tax = result.value;
        expect(tax.value).toBe(2100);
        expect(tax.scale).toBe(2);
        expect(tax.name).toBe("IVA General");
        expect(tax.code).toBe("IVA_GENERAL");
        expect(tax.toNumber()).toBe(21.0);
        expect(tax.toString()).toBe("21.00%");
      }
    });

    test("debe usar escala por defecto cuando no se proporciona", () => {
      const result = Tax.create({
        value: 21, // 21%
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isSuccess).toBe(true);
      if (result.isSuccess) {
        const tax = result.value;
        expect(tax.scale).toBe(Tax.DEFAULT_SCALE);
        expect(tax.toNumber()).toBe(21.0);
      }
    });

    test("debe fallar con valor negativo", () => {
      const result = Tax.create({
        value: -100,
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isFailure).toBe(true);
      if (result.isFailure) {
        expect(result.error.message).toContain("no puede ser negativa");
      }
    });

    test("debe fallar con tasa mayor a 100%", () => {
      const result = Tax.create({
        value: 10100, // 101.00%
        scale: 2,
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isFailure).toBe(true);
      if (result.isFailure) {
        expect(result.error.message).toContain("no puede ser mayor a 100%");
      }
    });

    test("debe fallar con nombre vacío", () => {
      const result = Tax.create({
        value: 2100,
        name: "",
        code: "IVA_GENERAL",
      });

      expect(result.isFailure).toBe(true);
      if (result.isFailure) {
        expect(result.error.message).toContain("El nombre del impuesto es obligatorio");
      }
    });

    test("debe fallar con código vacío", () => {
      const result = Tax.create({
        value: 2100,
        name: "IVA General",
        code: "",
      });

      expect(result.isFailure).toBe(true);
      if (result.isFailure) {
        expect(result.error.message).toContain("El código del impuesto es obligatorio");
      }
    });

    test("debe fallar con código con caracteres inválidos", () => {
      const result = Tax.create({
        value: 2100,
        name: "IVA General",
        code: "iva-general", // minúsculas no permitidas
      });

      expect(result.isFailure).toBe(true);
      if (result.isFailure) {
        expect(result.error.message).toContain("solo letras mayúsculas");
      }
    });

    test("debe fallar con escala fuera de rango", () => {
      const result = Tax.create({
        value: 2100,
        scale: 5, // Máximo permitido es 4
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isFailure).toBe(true);
      if (result.isFailure) {
        expect(result.error.message).toContain("La escala debe estar entre");
      }
    });
  });

  describe("Métodos de cálculo", () => {
    let tax: Tax;

    beforeEach(() => {
      const result = Tax.create({
        value: 2100, // 21.00%
        scale: 2,
        name: "IVA General",
        code: "IVA_GENERAL",
      });
      expect(result.isSuccess).toBe(true);
      if (result.isSuccess) {
        tax = result.value;
      }
    });

    test("debe calcular correctamente el importe del impuesto", () => {
      const baseAmount = 1000;
      const taxAmount = tax.calculateAmount(baseAmount);
      expect(taxAmount).toBe(210); // 21% de 1000
    });

    test("debe calcular correctamente con decimales", () => {
      const baseAmount = 123.45;
      const taxAmount = tax.calculateAmount(baseAmount);
      expect(taxAmount).toBeCloseTo(25.9245, 4); // 21% de 123.45
    });
  });

  describe("Métodos de comparación", () => {
    let tax1: Tax;
    let tax2: Tax;
    let tax3: Tax;

    beforeEach(() => {
      const result1 = Tax.create({
        value: 2100, // 21.00%
        scale: 2,
        name: "IVA General",
        code: "IVA_GENERAL",
      });
      const result2 = Tax.create({
        value: 1000, // 10.00%
        scale: 2,
        name: "IVA Reducido",
        code: "IVA_REDUCIDO",
      });
      const result3 = Tax.create({
        value: 2100, // 21.00%
        scale: 2,
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result1.isSuccess && result2.isSuccess && result3.isSuccess).toBe(true);
      if (result1.isSuccess && result2.isSuccess && result3.isSuccess) {
        tax1 = result1.value;
        tax2 = result2.value;
        tax3 = result3.value;
      }
    });

    test("debe considerar dos tasas con los mismos valores como iguales", () => {
      expect(tax1.equalsTo(tax3)).toBe(true);
    });

    test("debe considerar dos tasas con valores diferentes como distintas", () => {
      expect(tax1.equalsTo(tax2)).toBe(false);
    });

    test("debe comparar correctamente tasas mayores", () => {
      expect(tax1.greaterThan(tax2)).toBe(true);
      expect(tax2.greaterThan(tax1)).toBe(false);
    });

    test("debe comparar correctamente tasas menores", () => {
      expect(tax2.lessThan(tax1)).toBe(true);
      expect(tax1.lessThan(tax2)).toBe(false);
    });
  });

  describe("Métodos de verificación", () => {
    test("debe identificar correctamente una tasa cero", () => {
      const result = Tax.create({
        value: 0,
        name: "Sin IVA",
        code: "SIN_IVA",
      });

      expect(result.isSuccess).toBe(true);
      if (result.isSuccess) {
        const tax = result.value;
        expect(tax.isZero()).toBe(true);
        expect(tax.isPositive()).toBe(false);
      }
    });

    test("debe identificar correctamente una tasa positiva", () => {
      const result = Tax.create({
        value: 2100,
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isSuccess).toBe(true);
      if (result.isSuccess) {
        const tax = result.value;
        expect(tax.isZero()).toBe(false);
        expect(tax.isPositive()).toBe(true);
      }
    });
  });

  describe("Representación", () => {
    test("debe devolver la representación JSON correcta", () => {
      const result = Tax.create({
        value: 2100,
        scale: 2,
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isSuccess).toBe(true);
      if (result.isSuccess) {
        const tax = result.value;
        const json = tax.toJSON();
        
        expect(json).toEqual({
          value: 2100,
          scale: 2,
          name: "IVA General",
          code: "IVA_GENERAL",
          percentage: 21.0,
          formatted: "21.00%",
        });
      }
    });

    test("debe formatear correctamente diferentes escalas", () => {
      const result = Tax.create({
        value: 21000, // 21.000%
        scale: 3,
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isSuccess).toBe(true);
      if (result.isSuccess) {
        const tax = result.value;
        expect(tax.toString()).toBe("21.000%");
        expect(tax.toNumber()).toBe(21.0);
      }
    });
  });

  describe("Inmutabilidad", () => {
    test("debe ser inmutable", () => {
      const result = Tax.create({
        value: 2100,
        name: "IVA General",
        code: "IVA_GENERAL",
      });

      expect(result.isSuccess).toBe(true);
      if (result.isSuccess) {
        const tax = result.value;
        const props = tax.getProps();
        
        // Intentar modificar las propiedades debe fallar
        expect(() => {
          (props as any).value = 3000;
        }).toThrow();
      }
    });
  });
});
