import {z} from 'zod';
import i18next from '@config/i18n';
import {REQUIRED_ERROR_TEXT} from '@shared/schemas';

import {
  FormCommentableType,
  FormInputType,
  FormStep,
  FormStepRule,
  FormValue,
  FormValues,
} from './interfaces';

const validateString = (
  value: string,
  {max, min, required}: FormStepRule = {},
): string | undefined => {
  if (required && !value) {
    return i18next.t('general.required_field');
  }

  if (max && value.length > max) {
    return i18next.t('forms.max_char', {maxSize: max});
  }

  if (min && value.length < min) {
    return i18next.t('forms.min_char', {minSize: min});
  }

  return undefined;
};

const validateNumber = (
  value: number,
  {max, min, required}: FormStepRule = {},
): string | undefined => {
  if (required && value !== 0 && !value) {
    return i18next.t('general.required_field');
  }

  if (max && value > max) {
    return i18next.t('forms.max_char', {maxSize: max});
  }

  if (min && value < min) {
    return i18next.t('forms.min_char', {minSize: min});
  }

  return undefined;
};

const validateArray = (
  value: (string | number)[],
  {max, min, required}: FormStepRule = {},
): string | undefined => {
  if (required && value.length === 0) {
    return i18next.t('general.required_field');
  }

  if (max && value.length > max) {
    return i18next.t('forms.max_char', {maxSize: max});
  }

  if (min && value.length < min) {
    return i18next.t('forms.min_char', {minSize: min});
  }

  return undefined;
};

export const validateInput = <T = FormValue>(
  value: T,
  rules: FormStepRule = {},
): string | undefined => {
  switch (typeof value) {
    case 'string':
      return validateString(value, rules);
    case 'number':
      return validateNumber(value, rules);
    case 'object':
      if (Array.isArray(value)) {
        return validateArray(value, rules);
      }

      if (value === null && rules.required) {
        return i18next.t('general.required_field');
      }

      return undefined;
    case 'undefined':
      return rules.required ? i18next.t('general.required_field') : undefined;
    default:
      return undefined;
  }
};

const getZodFieldSchema = (q: FormStep): z.ZodTypeAny => {
  const isRequired = q.rules?.required;

  switch (q.input?.type) {
    case FormInputType.TEXT:
      return isRequired
        ? z
            .string({required_error: REQUIRED_ERROR_TEXT})
            .min(1, REQUIRED_ERROR_TEXT)
        : z.string().optional();

    case FormInputType.CHECKBOX:
    case FormInputType.RATING:
      return isRequired
        ? z.number({required_error: REQUIRED_ERROR_TEXT})
        : z.number().optional();

    case FormInputType.MULTIPLE_CHOICE:
      return isRequired
        ? z.array(z.number(), {required_error: REQUIRED_ERROR_TEXT})
        : z.array(z.number()).optional();

    default:
      return z.any();
  }
};

export const createZodSchemaFromQuestion = (q: FormStep): z.ZodObject<any> => {
  const shape: Record<string, z.ZodTypeAny> = {};

  shape[q.name] = getZodFieldSchema(q);

  if (q.commentable) {
    const commentSchema = z.string();
    shape[`${q.name}-comment`] =
      q.commentable === FormCommentableType.REQUIRED
        ? commentSchema.min(1, REQUIRED_ERROR_TEXT)
        : commentSchema.optional();
  }

  return z.object(shape);
};

export const createZodSchemaFromQuestions = (
  questions: FormStep[],
): z.ZodObject<any> => {
  const shape: Record<string, z.ZodTypeAny> = {};

  for (const q of questions) {
    const fieldSchema = getZodFieldSchema(q);
    shape[q.name] = fieldSchema;

    if (q.commentable) {
      const commentSchema = z.string({required_error: REQUIRED_ERROR_TEXT});
      shape[`${q.name}-comment`] =
        q.commentable === FormCommentableType.REQUIRED
          ? commentSchema.min(1, REQUIRED_ERROR_TEXT)
          : commentSchema.optional();
    }
  }

  return z.object(shape);
};

export const groupFormValues = (flatObject: {[key: string]: string}) => {
  const formValues: FormValues = {};

  Object.entries(flatObject).forEach(([key, val]) => {
    if (key.endsWith('-comment')) {
      const baseKey = key.replace('-comment', '');
      if (!formValues[baseKey]) {
        formValues[baseKey] = {};
      }
      formValues[baseKey].comment = val;
    } else {
      if (!formValues[key]) {
        formValues[key] = {};
      }
      formValues[key].value = val;
    }
  });
  return formValues;
};
