import {
  ParseError,
  isPossiblePhoneNumber,
  parsePhoneNumber,
  validatePhoneNumberLength,
} from "libphonenumber-js"
import { z } from "zod"

const enum ParseErrorMessage {
  INVALID_COUNTRY = "INVALID_COUNTRY",
  INVALID_LENGTH = "INVALID_LENGTH",
  NOT_A_NUMBER = "NOT_A_NUMBER",
  TOO_LONG = "TOO_LONG",
  TOO_SHORT = "TOO_SHORT",
}

export function phoneValidator(
  msg = "Required field",
  invalidMsg = "Invalid type"
) {
  return z
    .string({ invalid_type_error: invalidMsg, required_error: msg })
    .min(1, { message: msg })
    .superRefine((value, ctx) => {
      if (value) {
        try {
          const phoneNumber = parsePhoneNumber(value)
          if (phoneNumber) {
            if (isPossiblePhoneNumber(value, phoneNumber.country)) {
              return validatePhoneNumberLength(value, phoneNumber.country)
            } else {
              ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: "Please enter a valid phone number",
              })
            }
          }
        } catch (error) {
          if (error instanceof ParseError) {
            /**
             * Only setup for when we need proper validation,
             * should probably move to .superRefine to be able
             * to return different messages depending on error.
             */
            switch (error.message) {
              case ParseErrorMessage.INVALID_COUNTRY:
                ctx.addIssue({
                  code: z.ZodIssueCode.custom,
                  message:
                    "The country selected and country code doesn't match",
                })
                break
              case ParseErrorMessage.INVALID_LENGTH:
                ctx.addIssue({
                  code: z.ZodIssueCode.custom,
                  message: "Please enter a valid phone number",
                })
                break
              case ParseErrorMessage.NOT_A_NUMBER:
                ctx.addIssue({
                  code: z.ZodIssueCode.custom,
                  message: "Please enter a number",
                })
                break
              case ParseErrorMessage.TOO_LONG:
                ctx.addIssue({
                  code: z.ZodIssueCode.custom,
                  message: "The number you have entered is too long",
                })
                break
              case ParseErrorMessage.TOO_SHORT:
                ctx.addIssue({
                  code: z.ZodIssueCode.custom,
                  message: "The number you have entered is too short",
                })
                break
            }
          } else {
            ctx.addIssue({
              code: z.ZodIssueCode.custom,
              message: "The number you have entered is not valid",
            })
          }
        }
      }
    })
}
