import { ValueObject, translateZodValidationError } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { z } from "zod/v4";

// 🔹 Conjunto canónico de tipos admitidos en todo el ERP
export const DOCUMENT_TYPE_CODES = [
  "INVOICE",
  "QUOTATION",
  "DELIVERY_NOTE",
  "PAYMENT",
  "CREDIT_NOTE",
  "RECEIPT",
  "PURCHASE_ORDER",
] as const;

export type DocTypeCode = (typeof DOCUMENT_TYPE_CODES)[number];

// 🔹 Alias comunes (entrada libre) → código canónico
const ALIASES: Record<string, DocTypeCode> = {
  // facturas
  INVOICE: "INVOICE",
  FACTURA: "INVOICE",
  "FACTURA-RECTIFICATIVA": "CREDIT_NOTE",
  FACTURA_RECTIFICATIVA: "CREDIT_NOTE",
  "CREDIT-NOTE": "CREDIT_NOTE",
  CREDIT_NOTE: "CREDIT_NOTE",
  ABONO: "CREDIT_NOTE",

  // presupuestos
  QUOTATION: "QUOTATION",
  QUOTE: "QUOTATION",
  PRESUPUESTO: "QUOTATION",

  // albaranes
  "DELIVERY-NOTE": "DELIVERY_NOTE",
  DELIVERY_NOTE: "DELIVERY_NOTE",
  ALBARAN: "DELIVERY_NOTE",
  ALBARÁN: "DELIVERY_NOTE",

  // pagos / cobros
  PAYMENT: "PAYMENT",
  PAGO: "PAYMENT",
  RECEIPT: "RECEIPT",
  RECIBO: "RECEIPT",

  // pedidos
  "PURCHASE-ORDER": "PURCHASE_ORDER",
  PURCHASE_ORDER: "PURCHASE_ORDER",
  "ORDEN-DE-COMPRA": "PURCHASE_ORDER",
  ORDEN_DE_COMPRA: "PURCHASE_ORDER",
};

// 🔹 Normaliza texto a forma comparable: mayúsculas, sin acentos, separadores como ""
function normalizeToken(input: string): string {
  return (
    input
      .trim()
      .toUpperCase()
      // eliminar diacríticos (tildes)
      .normalize("NFD")
      .replace(/\p{Diacritic}/gu, "")
      // espacios/guiones → guion bajo
      .replace(/[\s-]+/g, "")
  );
}

interface DocTypeProps {
  value: DocTypeCode;
}

export class DocType extends ValueObject<DocTypeProps> {
  // Validación de código canónico con zod
  protected static validateCanonical(value: string) {
    const schema = z.enum(DOCUMENT_TYPE_CODES);
    return schema.safeParse(value);
  }

  // Intenta mapear la entrada libre a un código canónico
  protected static canonicalize(input: string): Result<DocTypeCode, Error> {
    const token = normalizeToken(input);
    const fromAlias = ALIASES[token];
    const candidate = (fromAlias ?? token) as string;

    const parsed = DocType.validateCanonical(candidate);
    if (!parsed.success) {
      return Result.fail(translateZodValidationError("DocType creation failed", parsed.error));
    }
    return Result.ok(parsed.data);
  }

  /**
   * Factoría desde entrada libre (admite alias).
   * Devuelve Result con el VO o el error de validación.
   */
  static create(input: string) {
    const canon = DocType.canonicalize(input);
    if (canon.isFailure) {
      return Result.fail(canon.error);
    }
    return Result.ok(new DocType({ value: canon.data }));
  }

  /**
   * Factoría directa para código canónico ya validado.
   * Útil en mappers desde persistencia.
   */
  static from(code: DocTypeCode): DocType {
    return new DocType({ value: code });
  }

  /**
   * Lista de tipos canónicos soportados (para UI/validadores externos).
   */
  static list(): readonly DocTypeCode[] {
    return DOCUMENT_TYPE_CODES;
  }

  /**
   * ¿Pertenece a alguno de los tipos indicados?
   */
  isOneOf(...types: DocTypeCode[]): boolean {
    return types.includes(this.props.value);
  }

  getProps(): DocTypeCode {
    return this.props.value;
  }

  toPrimitive(): DocTypeCode {
    return this.getProps();
  }

  toString(): string {
    return this.getProps();
  }
}
