import React, { useMemo, useCallback, useEffect } from 'react';
import { useFormik, type FormikProps } from 'formik';
import * as Yup from 'yup';
import { Box } from '@radix-ui/themes';
import { Typo } from '@/atoms/typo';
import {
  DynamicFormProvider,
  useDynamicForm,
} from '@/context/dynamic-form-context';
import type {
  DynamicFormFieldSchema,
  ValidationRule,
} from '@/types/dynamic-form-schema-generated';
import { Form } from './form/form';
import { DynamicFormField } from './dynamic-field';

export interface DynamicFormProps {
  fields: DynamicFormFieldSchema[];
  initialValues?: Record<string, unknown>;
  onSubmit: (values: Record<string, unknown>) => void | Promise<void>;
  children: React.ReactNode;
}

/**
 * 첫 번째 에러 필드로 스크롤하는 함수
 */
function scrollToFirstError(errors: Record<string, unknown>, _fields: DynamicFormFieldSchema[]): void {
  const firstErrorFieldName = Object.keys(errors)[0];
  if (!firstErrorFieldName) return;

  // 해당 필드의 DOM 요소를 찾기 (여러 선택자 시도)
  let fieldElement = document.querySelector(`[name="${firstErrorFieldName}"]`);
  
  // input 요소를 찾지 못한 경우, 해당 필드의 컨테이너를 찾기
  if (!fieldElement) {
    fieldElement = document.querySelector(`[data-field-name="${firstErrorFieldName}"]`);
  }
  
  // 여전히 찾지 못한 경우, FormItem의 에러 메시지 영역을 찾기
  if (!fieldElement) {
    fieldElement = document.querySelector(`[data-error-field="${firstErrorFieldName}"]`);
  }

  if (fieldElement) {
    fieldElement.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
    });
  }
}

function DynamicFormRoot({
  fields,
  initialValues = {},
  onSubmit,
  children,
}: DynamicFormProps): React.ReactElement {
  const validationSchema = useMemo(() => {
    const schema: Record<string, Yup.Schema<unknown>> = {};
    fields.forEach((field) => {
      if (field.validation) {
        const validation = createYupValidation(field, field.validation);
        if (validation) {
          schema[field.name] = validation;
        }
      } else if (field.required) {
        // choices 타입은 배열 스키마로 처리
        if (field.type === 'choices') {
          schema[field.name] = Yup.array()
            .min(1, `${field.label}을(를) 하나 이상 선택해주세요`)
            .required(`${field.label}은(는) 필수입니다`);
        } else if (field.type === 'yes_no') {
          // yes_no 타입은 boolean 스키마로 처리
          schema[field.name] = Yup.boolean().oneOf([true], `${field.label}에 동의해주세요`);
        } else {
          // choice, dropdown, short_text, long_text, number, email 타입은 문자열 스키마로 처리
          schema[field.name] = Yup.string().required(
            `${field.label}은(는) 필수입니다`
          );
        }
      }
    });
    return Object.keys(schema).length > 0 ? Yup.object(schema) : undefined;
  }, [fields]);

  const handleSubmit = useCallback(async (values: Record<string, unknown>) => {
    await onSubmit(values);
  }, [onSubmit]);

  const formik: FormikProps<Record<string, unknown>> = useFormik({
    initialValues: fields.reduce<Record<string, unknown>>((acc, field) => {
      if (field.type === 'choices') {
        acc[field.name] = initialValues[field.name] || [];
      } else if (field.type === 'yes_no') {
        acc[field.name] = initialValues[field.name] || false;
      } else {
        acc[field.name] = initialValues[field.name] || '';
      }
      return acc;
    }, {}),
    validationSchema,
    validateOnBlur: true,
    validateOnChange: true,
    onSubmit: handleSubmit,
  });

  // submit 시 validation 에러가 있으면 첫 번째 에러 필드로 스크롤
  useEffect(() => {
    if (formik.submitCount > 0 && Object.keys(formik.errors).length > 0) {
      // 약간의 지연을 두어 DOM 업데이트 후 스크롤 실행
      const timeoutId = setTimeout(() => {
        scrollToFirstError(formik.errors, fields);
      }, 100);
      
      return () => {
        clearTimeout(timeoutId);
      };
    }
  }, [formik.submitCount, formik.errors, fields]);

  return (
    <DynamicFormProvider fields={fields} formik={formik}>
      {children}
    </DynamicFormProvider>
  );
}

interface DynamicFormFieldListProps {
  fields?: DynamicFormFieldSchema[];
  labelVariant?: 'title' | 'body' | 'caption';
}
function DynamicFormFieldList({
  fields: fieldsProp,
  labelVariant,
}: DynamicFormFieldListProps): React.ReactElement {
  const { formik, fields: fieldsFromContext } = useDynamicForm();
  const fields = fieldsProp || fieldsFromContext;

  return (
    <>
      {fields.map((field) => {
        const errorMessage =
          formik.touched[field.name] && formik.errors[field.name]
            ? String(formik.errors[field.name])
            : undefined;

        return (
          <Box
            data-error-field={errorMessage ? field.name : undefined}
            data-field-name={field.name}
            key={field.name}
            mb="2"
          >
            <Form.FormItem
              errorMessage={errorMessage}
              label={field.label}
              labelVariant={labelVariant}
              name={field.name}
              required={field.required}
            >
              <DynamicFormField field={field} />
              {field.description ? <Typo>{field.description}</Typo> : null}
            </Form.FormItem>
          </Box>
        );
      })}
    </>
  );
}

export const DynamicForm = {
  Root: DynamicFormRoot,
  FieldList: DynamicFormFieldList,
};

export function createYupValidation(
  field: DynamicFormFieldSchema,
  validation?: ValidationRule
): Yup.Schema<unknown> | undefined {
  if (!validation) return undefined;

  let schema: Yup.Schema<unknown>;

  // field.type을 기준으로 기본 스키마 생성
  if (field.type === 'number') {
    schema = Yup.number();
  } else if (field.type === 'email') {
    schema = Yup.string().email(
      validation.message || '올바른 이메일 형식이 아닙니다'
    );
  } else if (field.type === 'choices') {
    schema = Yup.array();
  } else if (field.type === 'yes_no') {
    schema = Yup.boolean();
  } else {
    // choice, dropdown, short_text, long_text 타입은 문자열 스키마로 처리
    schema = Yup.string();
  }

  if (validation.min !== undefined) {
    if (field.type === 'number') {
      schema = (schema as Yup.NumberSchema).min(
        validation.min,
        validation.message || `최소 ${validation.min}이어야 합니다`
      );
    } else if (field.type === 'choices') {
      schema = (schema as ReturnType<typeof Yup.array>).min(
        validation.min,
        validation.message || `최소 ${validation.min}개 이상 선택해주세요`
      );
    } else if (field.type === 'yes_no') {
      // yes_no 타입은 boolean이므로 min/max 검증 불가
      // 대신 required 검증만 적용
    } else {
      schema = (schema as Yup.StringSchema).min(
        validation.min,
        validation.message || `최소 ${validation.min}자 이상이어야 합니다`
      );
    }
  }

  if (validation.max !== undefined) {
    if (field.type === 'number') {
      schema = (schema as Yup.NumberSchema).max(
        validation.max,
        validation.message || `최대 ${validation.max}이어야 합니다`
      );
    } else if (field.type === 'choices') {
      schema = (schema as ReturnType<typeof Yup.array>).max(
        validation.max,
        validation.message || `최대 ${validation.max}개까지 선택 가능합니다`
      );
    } else if (field.type === 'yes_no') {
      // yes_no 타입은 boolean이므로 min/max 검증 불가
      // 대신 required 검증만 적용
    } else {
      schema = (schema as Yup.StringSchema).max(
        validation.max,
        validation.message || `최대 ${validation.max}자 이하여야 합니다`
      );
    }
  }

  if (validation.pattern) {
    if (field.type === 'yes_no') {
      // yes_no 타입은 boolean이므로 pattern 검증 불가
      // 대신 required 검증만 적용
    } else {
      schema = (schema as Yup.StringSchema).matches(
        new RegExp(validation.pattern),
        validation.message || '올바른 형식이 아닙니다'
      );
    }
  }

  // field.required가 true인 경우 required 검증 추가
  if (field.required) {
    if (field.type === 'yes_no') {
      schema = (schema as Yup.BooleanSchema).oneOf([true], validation.message || `${field.label}에 동의해주세요`);
    } else if (field.type === 'choices') {
      schema = (schema as ReturnType<typeof Yup.array>).min(1, validation.message || `${field.label}을(를) 하나 이상 선택해주세요`);
    } else {
      schema = (schema as Yup.StringSchema).required(validation.message || `${field.label}은(는) 필수입니다`);
    }
  }

  return schema;
}
