import { Theme } from '@material-hu/mui/styles';

import {
  InputType,
  InputValidations,
  SectionComponent,
  SectionContent,
} from 'src/pages/dashboard/serviceManagement/types';
import { LokaliseTFunction } from 'src/utils/i18n';

import {
  DATE_DD_MM_YYYY,
  DATE_MM_DD_YYYY,
  DATE_MM_YYYY,
  DATETIME_DD_MM_YYYY_HH_MM_SS,
  DatePattern,
  EMAIL_REGEX,
  MAX_NUMBER_VALUE,
  MIN_NUMBER_VALUE,
  TIME_HH_MM,
} from './constants';

export function isValidPdfFormCompletion(
  formValues: Record<string, any>,
  currentForm: SectionContent,
): boolean {
  for (const component of currentForm.components) {
    const fieldAnswer = formValues[component.nameId];
    const isValid = isValidFieldAnswer(
      component.type,
      fieldAnswer,
      component.validations,
      component.content.required,
    );
    if (!isValid) {
      return false;
    }
  }
  return true;
}

export function isValidFieldAnswer(
  type: InputType,
  answer: any,
  validations: InputValidations | undefined,
  required: boolean,
): boolean {
  if (
    required &&
    (answer === undefined ||
      answer === null ||
      (typeof answer === 'string' && answer === '') ||
      (Array.isArray(answer) && answer.length === 0))
  ) {
    return false;
  }

  if (validations) {
    return validateFieldAnswer(type, answer, validations);
  }

  return true;
}

function compileSafeRegex(pattern: string): RegExp | null {
  if (!pattern || pattern.length > 256) return null;
  try {
    const needsUnicode = /\\p\{/.test(pattern);
    return needsUnicode ? new RegExp(pattern, 'u') : new RegExp(pattern);
  } catch {
    return null;
  }
}

export function validateFieldAnswer(
  type: InputType,
  answer: any,
  validations: InputValidations | undefined,
): boolean {
  // Allow empty values here. Requiredness is handled by isValidFieldAnswer
  if (
    answer === undefined ||
    answer === null ||
    answer === '' ||
    (Array.isArray(answer) && answer.length === 0)
  ) {
    return true;
  }
  switch (type) {
    case InputType.DATE: {
      if (answer instanceof Date) {
        return !Number.isNaN(answer.getTime());
      }
      const dateFromString = new Date(answer);
      return !Number.isNaN(dateFromString.getTime());
    }
    case InputType.INTEGER: {
      const numericValue =
        typeof answer === 'number' ? answer : Number(String(answer).trim());
      if (!Number.isFinite(numericValue) || !Number.isInteger(numericValue)) {
        return false;
      }
      if (validations?.minimum !== undefined) {
        if (numericValue < validations.minimum) {
          return false;
        }
      }
      if (validations?.maximum !== undefined) {
        if (numericValue > validations.maximum) {
          return false;
        }
      }
      if (validations?.pattern) {
        const regex = compileSafeRegex(validations.pattern);
        if (regex && !regex.test(String(answer))) return false;
      }
      return true;
    }
    case InputType.FLOAT: {
      const numericValue =
        typeof answer === 'number' ? answer : Number(String(answer).trim());
      if (!Number.isFinite(numericValue)) {
        return false;
      }
      if (validations?.minimum !== undefined) {
        if (numericValue < validations.minimum) return false;
      }
      if (validations?.maximum !== undefined) {
        if (numericValue > validations.maximum) return false;
      }
      if (validations?.pattern) {
        const regex = compileSafeRegex(validations.pattern);
        if (regex && !regex.test(String(answer))) return false;
      }
      return true;
    }
    case InputType.TEXT: {
      const value = String(answer);
      if (validations?.minLength !== undefined) {
        if (value.length < validations.minLength) {
          return false;
        }
      }
      if (validations?.maxLength !== undefined) {
        if (value.length > validations.maxLength) {
          return false;
        }
      }
      if (validations?.pattern) {
        const regex = compileSafeRegex(validations.pattern);
        if (regex && !regex.test(value)) return false;
      }
      return true;
    }
    default:
      return true;
  }
}

export function getFieldError(
  field: SectionComponent,
  answer: any,
  t: LokaliseTFunction,
) {
  const errors: string[] = [];
  const noAnswer =
    answer === undefined ||
    answer === null ||
    (typeof answer === 'string' && answer === '') ||
    (Array.isArray(answer) && answer.length === 0);
  if (field.content.required && noAnswer) {
    errors.push(t('empty_field_error'));
  }

  switch (field.type) {
    case InputType.TEXT:
      if (
        field.validations?.maxLength &&
        !noAnswer &&
        answer?.length > field.validations.maxLength
      ) {
        errors.push(
          t('max_length', {
            count: field.validations.maxLength,
          }),
        );
      }
      if (field.validations?.pattern && !noAnswer) {
        try {
          const regex = compileSafeRegex(field.validations.pattern);
          if (regex && !regex.test(answer)) {
            field.validations.pattern === EMAIL_REGEX
              ? errors.push(t('invalid_email_pattern'))
              : errors.push(t('invalid_alpha_pattern'));
          }
        } catch (_e) {
          // Ignore invalid regex
        }
      }
      break;
    case InputType.FLOAT:
    case InputType.INTEGER:
      if (!noAnswer && isNaN(Number(answer))) {
        errors.push(t('invalid_number_field'));
        // Short circuit return to avoid further validation
        return errors;
      }
      if (
        field.type === InputType.INTEGER &&
        !noAnswer &&
        !Number.isInteger(Number(answer))
      ) {
        errors.push(t('invalid_integer_pattern'));
      }
      if (
        field.validations?.minimum !== undefined &&
        !noAnswer &&
        Number(answer) < field.validations.minimum
      ) {
        errors.push(
          t('minimum_validation', { min: field.validations.minimum }),
        );
      }
      if (
        field.validations?.maximum !== undefined &&
        !noAnswer &&
        Number(answer) > field.validations.maximum
      ) {
        errors.push(
          t('maximum_validation', { max: field.validations.maximum }),
        );
      }
      if (!noAnswer && Number(answer) > MAX_NUMBER_VALUE) {
        errors.push(t('maximum_validation', { max: MAX_NUMBER_VALUE }));
      }
      if (!noAnswer && Number(answer) < MIN_NUMBER_VALUE) {
        errors.push(t('minimum_validation', { min: MIN_NUMBER_VALUE }));
      }
      break;
    default:
      break;
  }

  return errors;
}

export function getFieldContainerSxProps(
  hasErrors: boolean,
  dimensionSxProps: { width: string; height: string },
  theme: Theme,
) {
  return {
    width: dimensionSxProps.width,
    height: dimensionSxProps.height,
    p: '2px',
    cursor: 'pointer',
    borderRadius: theme.shape.borderRadiusS,
    opacity: 0.8,
    border: `1px solid ${hasErrors ? theme.palette.graphics?.errorText : theme.palette.border?.neutralBorder}`,
    '&:hover': {
      border: `1px solid ${hasErrors ? theme.palette.graphics?.errorText : theme.palette.border?.primaryBorder}`,
    },
  };
}

export function getOptionSize(
  optionsLength: number,
  dimensions: { width: number; height: number },
  padding: number,
  gap: number,
) {
  if (optionsLength <= 0) return 0;
  const widthAvail = Math.max(0, dimensions.width - 2 * padding);
  const heightAvail = Math.max(0, dimensions.height - 2 * padding);
  const verticalSize =
    (heightAvail - gap * (optionsLength - 1)) / optionsLength;
  const size = Math.max(0, Math.min(widthAvail, verticalSize));

  return Math.round(size * 100) / 100; // round to 2 decimals
}

export function isValidDateOrTimeValue(value: unknown): value is Date {
  return value instanceof Date && !Number.isNaN(value.getTime());
}

export function getViewsForPattern(pattern: DatePattern) {
  switch (pattern) {
    case DATE_MM_YYYY:
      return ['year', 'month'] as const;
    case DATE_DD_MM_YYYY:
    case DATE_MM_DD_YYYY:
      return ['year', 'month', 'day'] as const;
    case TIME_HH_MM:
      return ['hours', 'minutes'] as const;
    case DATETIME_DD_MM_YYYY_HH_MM_SS:
      return ['year', 'month', 'day', 'hours', 'minutes'] as const;
    default:
      return ['year', 'month', 'day'] as const;
  }
}
