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

interface IUtcDateProps {
  value: string;
}

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

  private constructor(props: IUtcDateProps) {
    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 dateString Fecha en formato UTC (con o sin hora YYYY-MM-DD)
   * @returns UtcDate si es válida, Error en caso contrario.
   */
  static create(dateString: string): Result<UtcDate, Error> {
    const dateIsValid = UtcDate.validate(dateString);
    if (!dateIsValid.success) {
      return Result.fail(new Error(`Invalid UTC date format: ${dateString}`));
    }

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

  getValue(): 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] ?? "";
  }

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

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