import Joi from "joi";
import { EmptyOr } from "../../../../utilities";
import { RuleValidator } from "../RuleValidator";
import { DomainError, handleDomainError } from "../errors";
import { Result } from "./Result";
import { IValueObjectOptions, ValueObject } from "./ValueObject";

export interface IDateValueOptions extends IValueObjectOptions {
  dateFormat?: string;
}

export class UTCDateValue extends ValueObject<Date> {
  protected static validate(value: EmptyOr<string>, options: IDateValueOptions) {
    const ruleIsEmpty = RuleValidator.RULE_ALLOW_EMPTY.default(0);
    const rulesIsDate = Joi.date()
      //.format(String(options.dateFormat))
      .label(String(options.label));

    const rules = Joi.alternatives(ruleIsEmpty, rulesIsDate);

    return RuleValidator.validate<string>(rules, value);
  }

  public static createCurrentDate() {
    return Result.ok<UTCDateValue>(new UTCDateValue(new Date()));
  }

  public static create(value: EmptyOr<string>, options: IDateValueOptions = {}) {
    const _options = {
      ...options,
      dateFormat: options.dateFormat ? options.dateFormat : "YYYY-MM-DD",
      label: options.label ? options.label : "date",
    };

    const validationResult = UTCDateValue.validate(value, _options);

    if (validationResult.isFailure) {
      return Result.fail(
        handleDomainError(DomainError.INVALID_INPUT_DATA, validationResult.error.message, _options)
      );
    }

    return Result.ok<UTCDateValue>(new UTCDateValue(new Date(validationResult.object)));
  }

  public isValid = (): boolean => {
    return !isNaN(this.props.valueOf()) && this.props.valueOf() !== 0;
  };

  public isEmpty = (): boolean => {
    return !this.isValid();
  };

  public toISO8601 = (): string => {
    return this.isValid() ? this.props.toISOString() : "";
  };

  public toDateString = (): string => {
    // Tue Jul 09 2024
    return this.isValid() ? this.props.toDateString() : "";
  };

  public toLocaleDateString = (
    locales?: Intl.LocalesArgument,
    options?: Intl.DateTimeFormatOptions
  ): string => {
    // DD/MM/YYYY
    return this.isValid() ? this.props.toLocaleDateString(locales, options) : "";
  };

  public toLocaleTimeString = (): string => {
    return this.isValid() ? this.props.toLocaleTimeString() : "";
  };

  public toString(): string {
    // YYYY-MM-DD
    if (!this.isEmpty()) {
      const year = this.props.getFullYear();
      const month = String(this.props.getMonth() + 1).padStart(2, "0");
      const day = String(this.props.getDate()).padStart(2, "0");

      return String(`${year}-${month}-${day}`);
    }

    return String("");
  }

  public toPrimitive(): string {
    return this.toISO8601();
  }
}
