import { DomainError } from "./domain-error";

/**
 * Error de validación de dominio.
 *
 * - Extiende DomainError para aprovechar `code`, `metadata`, `cause` y `toJSON`.
 * - Estructura estable para mapeo (Problem+JSON / telemetría).
 */
export class DomainValidationError extends DomainError {
  /** Discriminante para routing/telemetría */
  public readonly kind = "VALIDATION" as const;

  /** Regla/identificador de error de validación (ej. INVALID_EMAIL) */
  public readonly code: string;

  /** Campo afectado (path) */
  public readonly field: string;

  /** Mensaje legible de negocio */
  public readonly detail: string;

  constructor(
    code: string,
    field: string,
    detail: string,
    options?: ErrorOptions & { metadata?: Record<string, unknown> }
  ) {
    // Mensaje humano compacto y útil para logs
    super(`[${field}] ${detail}`, code, {
      ...options,
      // Aseguramos metadatos ricos y estables
      metadata: {
        ...(options?.metadata ?? {}),
        field,
        detail,
        kind: "VALIDATION",
      },
    });

    this.name = "DomainValidationError";
    this.code = code;
    this.field = field;
    this.detail = detail;

    Object.setPrototypeOf(this, new.target.prototype);
    Object.freeze(this);
  }

  /** Atajos de construcción comunes */
  static requiredValue(
    field: string,
    options?: ErrorOptions & { metadata?: Record<string, unknown> }
  ) {
    return new DomainValidationError("REQUIRED_VALUE", field, "cannot be empty", options);
  }

  static invalidFormat(
    field: string,
    detail = "invalid format",
    options?: ErrorOptions & { metadata?: Record<string, unknown> }
  ) {
    return new DomainValidationError("INVALID_FORMAT", field, detail, options);
  }

  static invalidValue(
    field: string,
    value: unknown,
    detail = "invalid value",
    options?: ErrorOptions & { metadata?: Record<string, unknown> }
  ) {
    // `cause` preserva el valor problemático para inspección sin exponerlo a cliente
    return new DomainValidationError("INVALID_VALUE", field, detail, { ...options, cause: value });
  }

  /** Proyección mínima para Problem+JSON / colecciones */
  toDetail() {
    return { path: this.field, message: this.detail, rule: this.code };
  }

  /** Incluye proyección validación en la serialización segura */
  override toJSON() {
    const base = super.toJSON();
    return {
      ...base,
      kind: this.kind,
      field: this.field,
      detail: this.detail,
    };
  }
}

/** Type guard */
export const isDomainValidationError = (e: unknown): e is DomainValidationError =>
  e instanceof DomainValidationError;
