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

// 📌 Validaciones usando `zod`
const postalCodeSchema = z
  .string()
  .min(4, "Invalid postal code format")
  .max(10, "Invalid postal code format")
  .regex(/^\d{4,10}$/, {
    message: "Invalid postal code format",
  });

const streetSchema = z.string().min(2).max(255);
const street2Schema = z.string().optional();
const citySchema = z.string().min(2).max(50);
const stateSchema = z.string().min(2).max(50);
const countrySchema = z.string().min(2).max(56);

interface IPostalAddressProps {
  street: string;
  street2?: string;
  city: string;
  postalCode: string;
  state: string;
  country: string;
}

export class PostalAddress extends ValueObject<IPostalAddressProps> {
  protected static validate(values: IPostalAddressProps) {
    return z
      .object({
        street: streetSchema,
        street2: street2Schema,
        city: citySchema,
        postalCode: postalCodeSchema,
        state: stateSchema,
        country: countrySchema,
      })
      .safeParse(values);
  }

  static create(values: IPostalAddressProps): Result<PostalAddress, Error> {
    const valueIsValid = PostalAddress.validate(values);

    if (!valueIsValid.success) {
      return Result.fail(new Error(valueIsValid.error.errors[0]?.message));
    }
    return Result.ok(new PostalAddress(values));
  }

  static createNullable(values?: IPostalAddressProps): Result<Maybe<PostalAddress>, Error> {
    if (!values || Object.values(values).every((value) => value.trim() === "")) {
      return Result.ok(Maybe.none<PostalAddress>());
    }

    return PostalAddress.create(values).map((value) => Maybe.some(value));
  }

  static update(
    oldAddress: PostalAddress,
    data: Partial<PostalAddress>
  ): Result<PostalAddress, Error> {
    return PostalAddress.create({
      street: data.street ?? oldAddress.street,
      street2: data.street2?.getOrUndefined() ?? oldAddress.street2.getOrUndefined(),
      city: data.city ?? oldAddress.city,
      postalCode: data.postalCode ?? oldAddress.postalCode,
      state: data.state ?? oldAddress.state,
      country: data.country ?? oldAddress.country,
    }).getOrElse(this);
  }

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

  get street2(): Maybe<string> {
    return Maybe.fromNullable(this.props.street2);
  }

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

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

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

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

  getValue(): IPostalAddressProps {
    return this.props;
  }

  toPrimitive() {
    return this.getValue();
  }

  toString(): string {
    return `${this.props.street}, ${this.props.street2}, ${this.props.city}, ${this.props.postalCode}, ${this.props.state}, ${this.props.country}`;
  }
}
