import { eng, ita } from "~/i18n/FormErrorsTranslation";

import * as z from "zod";

const Capitalize = (s: string) => {
  if (typeof s !== "string") return "";
  return s.charAt(0).toUpperCase() + s.slice(1);
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ErrorType = Record<string, any>;
function FormErrorTranslation(template: string, obj: ErrorType): string {
  return template.replace(/{{[^}]*}}/g, (match) =>
    typeof obj[match.replace(/{|}/g, "")] === "string"
      ? Capitalize(String(obj[match.replace(/{|}/g, "")]))
      : String(obj[match.replace(/{|}/g, "")]),
  );
}

export const customErrorMapWrapper = (locale: string) => {
  const data = locale === "it" ? ita : eng;

  const customErrorMap: z.ZodErrorMap = (issue, ctx) => {
    let message = "";

    switch (issue.code) {
      case z.ZodIssueCode.invalid_type:
        if (issue.received === z.ZodParsedType.undefined) {
          message = data.errors.invalid_type_received_undefined;
        } else {
          message = FormErrorTranslation(data.errors.invalid_type, {
            expected: data.local_types[issue.expected],
            received: data.local_types[issue.received],
          });
        }
        break;
      case z.ZodIssueCode.invalid_literal:
        message = FormErrorTranslation(data.errors.invalid_literal, {
          expected: JSON.stringify(issue.expected, jsonStringifyReplacer),
        });
        break;
      case z.ZodIssueCode.unrecognized_keys:
        message = FormErrorTranslation(data.errors.unrecognized_keys, {
          keys: joinValues(issue.keys, ", "),
          count: issue.keys.length,
        });
        break;
      case z.ZodIssueCode.invalid_union:
        message = data.errors.invalid_union;
        break;
      case z.ZodIssueCode.invalid_union_discriminator:
        message = FormErrorTranslation(
          data.errors.invalid_union_discriminator,
          {
            options: joinValues(issue.options),
          },
        );
        break;
      case z.ZodIssueCode.invalid_enum_value:
        message = FormErrorTranslation(data.errors.invalid_enum_value, {
          options: joinValues(issue.options),
        });
        break;
      case z.ZodIssueCode.invalid_arguments:
        message = data.errors.invalid_arguments;
        break;
      case z.ZodIssueCode.invalid_return_type:
        message = data.errors.invalid_return_type;
        break;
      case z.ZodIssueCode.invalid_date:
        message = data.errors.invalid_date;
        break;
      case z.ZodIssueCode.invalid_string:
        if (typeof issue.validation === "object") {
          if ("startsWith" in issue.validation) {
            message = FormErrorTranslation(
              data.errors.invalid_string.startsWith,
              {
                startsWith: issue.validation.startsWith,
              },
            );
          } else if ("endsWith" in issue.validation) {
            message = FormErrorTranslation(
              data.errors.invalid_string.endsWith,
              {
                endsWith: issue.validation.endsWith,
              },
            );
          }
        } else {
          message = FormErrorTranslation(
            data.errors.invalid_string[
              issue.validation as keyof typeof data.errors.invalid_string
            ],
            {
              validation: FormErrorTranslation(
                data.validations[
                  issue.validation as keyof typeof data.validations
                ],
                {
                  defaultValue: issue.validation,
                },
              ),
            },
          );
        }
        break;
      case z.ZodIssueCode.too_small: {
        const minimum =
          issue.type === "date"
            ? new Date(issue.minimum as number)
            : issue.minimum;

        const incl_excl = issue.exact
          ? "exact"
          : issue.inclusive
            ? "inclusive"
            : "not_inclusive";
        message = FormErrorTranslation(
          data.errors.too_small[
            issue.type as keyof typeof data.errors.too_small
          ][incl_excl],
          {
            minimum: minimum,

            //count: typeof minimum === "number" ? minimum : undefined,
          },
        );

        break;
      }
      case z.ZodIssueCode.too_big: {
        const maximum =
          issue.type === "date"
            ? new Date(issue.maximum as number)
            : issue.maximum;
        message = FormErrorTranslation(
          data.errors.too_big[issue.type as keyof typeof data.errors.too_big][
            issue.exact
              ? "exact"
              : issue.inclusive
                ? "inclusive"
                : "not_inclusive"
          ],
          {
            maximum: maximum,
            //count: typeof maximum === "number" ? maximum : undefined,
          },
        );
        break;
      }

      case z.ZodIssueCode.invalid_intersection_types:
        message = data.errors.invalid_intersection_types;
        break;
      case z.ZodIssueCode.not_multiple_of:
        message = FormErrorTranslation(data.errors.not_multiple_of, {
          multipleOf: issue.multipleOf,
        });
        break;
      case z.ZodIssueCode.not_finite:
        message = data.errors.not_finite;
        break;
      case z.ZodIssueCode.custom:
        message =
          data.customErrors[
            issue.params?.type as keyof typeof data.customErrors
          ];
        break;
      default:
        message = ctx.defaultError;
    }
    return { message: message };
  };

  return customErrorMap;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const jsonStringifyReplacer = (_: string, value: any): any => {
  if (typeof value === "bigint") {
    return value.toString();
  }
  return value;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function joinValues<T extends any[]>(array: T, separator = " | "): string {
  return (
    array
      // eslint-disable-next-line @typescript-eslint/no-unsafe-return
      .map((val) => (typeof val === "string" ? `'${val}'` : val))
      .join(separator)
  );
}
