import Joi from "joi";
import { RuleValidator } from "../../../RuleValidator";
import { Result } from "../../Result";
import { ValueObject } from "../../ValueObject";

export interface IOrderProps {
  type: string;
  field: string;
}

export interface IOrder {
  type: string;
  field: string;

  toString(): string;
  toObject(): Record<string, string>;
}

export class Order extends ValueObject<IOrderProps> implements IOrder {
  public static create(orderProps: IOrderProps) {
    // Validación de props
    const valid = this.validate(orderProps);
    if (valid.isFailure) {
      return Result.fail(valid.error);
    }

    return Result.ok<Order>(
      new Order({
        field: Order.sanitize(orderProps.field),
        type: Order.sanitize(orderProps.type),
      })
    );
  }

  protected static validate(orderProps: IOrderProps) {
    const { type, field } = orderProps;

    // type
    if (
      RuleValidator.validate(RuleValidator.RULE_NOT_NULL_OR_UNDEFINED, type)
        .isSuccess
    ) {
      let typeOrError = RuleValidator.validate(
        RuleValidator.RULE_IS_TYPE_STRING,
        type
      );

      if (typeOrError.isFailure) {
        return typeOrError;
      }

      typeOrError = RuleValidator.validate(Joi.any().valid("+", "-"), type);

      if (typeOrError.isFailure) {
        return typeOrError;
      }
    }

    // field
    if (
      RuleValidator.validate(RuleValidator.RULE_NOT_NULL_OR_UNDEFINED, type)
        .isSuccess
    ) {
      const fieldOrError = RuleValidator.validate(
        RuleValidator.RULE_IS_TYPE_STRING,
        field
      );

      if (fieldOrError.isFailure) {
        return fieldOrError;
      }
    }

    return Result.ok();
  }

  protected static sanitize(value: string): string {
    return String(Joi.string().trim().validate(value).value);
  }

  protected constructor(props: IOrderProps) {
    super(props);
  }

  get field(): string {
    return this.props.field;
  }

  get type(): string {
    return this.props.type;
  }

  public toString(): string {
    return `${this.field} [${this.type}]`;
  }

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

  public toObject(): Record<string, string> {
    return {
      type: this.type,
      field: this.field,
    };
  }

  public toJSON(): string {
    return this.toString();
  }
}
