import { Result } from "@repo/rdx-utils";
import { z } from "zod/v4";
import { translateZodValidationError } from "../helpers";
import { ValueObject } from "./value-object";

interface UtcDateProps {
  value: string;
}

export class UtcDate extends ValueObject<UtcDateProps> {
  private readonly date!: Date;

  private constructor(props: UtcDateProps) {
    super(props);
    const { value: dateString } = props;
    this.date = Object.freeze(new Date(dateString));
  }

  static validate(dateString: string) {
    const dateStr = z.union([
      z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/, "Invalid ISO 8601 format"),
      z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Invalid YYYY-MM-DD format"),
    ]);

    const dateStrToDate = dateStr.pipe(z.coerce.date());

    return dateStrToDate.safeParse(dateString);
  }

  /**
   * Crea una instancia de UtcDate a partir de un string en formato UTC ISO 8601.
   * @param isoDateString Fecha en formato UTC (con o sin hora YYYY-MM-DD)
   * @returns UtcDate si es válida, Error en caso contrario.
   */
  static createFromISO(isoDateString: string): Result<UtcDate, Error> {
    const dateIsValid = UtcDate.validate(isoDateString);

    if (!dateIsValid.success) {
      return Result.fail(translateZodValidationError("UtcDate creation failed", dateIsValid.error));
    }

    return Result.ok(new UtcDate({ value: isoDateString }));
  }

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

  /**
   * Devuelve la fecha completa en formato UTC con hora (ISO 8601). Ejemplo: 2025-12-31T23:59:59Z.
   */
  toPrimitive() {
    return this.toISOString();
  }

  /**
   * Devuelve la fecha en formato UTC sin hora (YYYY-MM-DD).
   */
  toDateString(): string {
    return this.date.toISOString().split("T")[0];
  }

  toString() {
    return this.toDateString();
  }

  /**
   * Devuelve la fecha en formato UTC con hora (ISO 8601). Ejemplo: 2025-12-31T23:59:59Z.
   */
  toISOString(): string {
    return this.date.toISOString();
  }

  /**
   * Devuelve la fecha en formato dd/mm/yyyy (formato europeo).
   */
  toEuropeanString(): string {
    const day = String(this.date.getUTCDate()).padStart(2, "0");
    const month = String(this.date.getUTCMonth() + 1).padStart(2, "0"); // Los meses en JS empiezan en 0
    const year = this.date.getUTCFullYear();

    return `${day}/${month}/${year}`;
  }

  /**
   * Compara si dos instancias de UtcDate son iguales.
   */
  equals(other: UtcDate): boolean {
    return this.toISOString() === other.toISOString();
  }
}
