import { cn } from "@/lib/utils";
import {
  useState,
  useCallback,
  forwardRef,
  type InputHTMLAttributes,
  type ChangeEvent,
  type FocusEvent,
  type ReactNode,
} from "react";

// ============================================
// TYPES & INTERFACES
// ============================================

export type InputType =
  | "text"
  | "email"
  | "phone"
  | "siret"
  | "siren"
  | "iban"
  | "bic"
  | "password"
  | "number"
  | "url"
  | "postal_code"
  | "tva_intra"
  | "nir" // Numéro de sécurité sociale
  | "carte_bancaire";

export interface ValidationResult {
  isValid: boolean;
  message?: string;
}

export type ValidatorFn = (value: string) => ValidationResult;
export type FormatterFn = (value: string) => string;

export interface SelectOption {
  value: string;
  label: string;
}

// Props de base communes
interface BaseInputFieldProps {
  /** Label du champ */
  label: string;
  /** Type de validation à appliquer */
  inputType?: InputType;
  /** Champ requis */
  required?: boolean;
  /** Erreur externe (ex: depuis react-hook-form) */
  error?: string;
  /** Classes CSS additionnelles pour l'input */
  className?: string;
  /** Classes CSS pour le container */
  containerClassName?: string;
  /** Afficher les indicateurs de validation */
  showValidation?: boolean;
  /** Afficher l'icône de validation */
  showIcon?: boolean;
  /** Callback avec valeur et état de validation */
  onValueChange?: (value: string, isValid: boolean) => void;
  /** onChange standard */
  onChange?: (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => void;
  /** Valeur contrôlée */
  value?: string | number | readonly string[];
  /** Icône à gauche */
  leftIcon?: ReactNode;
  /** Icône à droite (remplace l'icône de validation) */
  rightIcon?: ReactNode;
  /** Désactiver le formatage automatique */
  disableFormatting?: boolean;
  /** Nom du champ */
  name?: string;
  /** Champ désactivé */
  disabled?: boolean;
  /** Placeholder */
  placeholder?: string;
  /** ID du champ */
  id?: string;
  /** onBlur handler */
  maxLength?: number;
  onBlur?: (e: FocusEvent<HTMLInputElement | HTMLSelectElement>) => void;
}

// Props spécifiques pour input
interface InputAsInputProps extends BaseInputFieldProps {
  as?: "input";
  options?: never;
}

// Props spécifiques pour select
interface InputAsSelectProps extends BaseInputFieldProps {
  as: "select";
  options: SelectOption[];
}

// Union type pour les props
export type InputFieldProps = InputAsInputProps | InputAsSelectProps;

// Type pour le mapping HTML input type
type HTMLInputType = "text" | "email" | "password" | "number" | "url" | "tel";

// ============================================
// VALIDATEURS
// ============================================

const createValidator = (
  regex: RegExp,
  message: string
): ValidatorFn => {
  return (value: string): ValidationResult => ({
    isValid: regex.test(value),
    message,
  });
};

// Validation Luhn (SIRET, SIREN, Carte bancaire)
const luhnCheck = (value: string, doubleOdd: boolean = true): boolean => {
  let sum = 0;
  for (let i = 0; i < value.length; i++) {
    let digit = parseInt(value[i], 10);
    const shouldDouble = doubleOdd ? i % 2 === 0 : i % 2 === 1;
    if (shouldDouble) {
      digit *= 2;
      if (digit > 9) digit -= 9;
    }
    sum += digit;
  }
  return sum % 10 === 0;
};

// Validation IBAN mod-97
const ibanMod97Check = (iban: string): boolean => {
  const rearranged = iban.slice(4) + iban.slice(0, 4);
  const numericIBAN = rearranged.replace(/[A-Z]/g, (char) =>
    (char.charCodeAt(0) - 55).toString()
  );
  let remainder = numericIBAN;
  while (remainder.length > 2) {
    const block = remainder.slice(0, 9);
    remainder = (parseInt(block, 10) % 97).toString() + remainder.slice(9);
  }
  return parseInt(remainder, 10) % 97 === 1;
};

// Clé NIR (sécurité sociale)
const nirKeyCheck = (nir: string): boolean => {
  // Gérer la Corse (2A, 2B)
  let nirNum = nir.slice(0, 13);
  if (nirNum[6] === "A") nirNum = nirNum.replace("A", "0");
  if (nirNum[6] === "B") nirNum = nirNum.replace("B", "0");
  
  const num = BigInt(nirNum);
  const key = parseInt(nir.slice(13, 15), 10);
  return 97 - Number(num % BigInt(97)) === key;
};

export const validators: Record<InputType, ValidatorFn> = {
  text: (value) => ({
    isValid: value.length > 0,
    message: "Ce champ est requis",
  }),

  email: createValidator(
    /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
    "Format email invalide (ex: exemple@domaine.fr)"
  ),

  phone: (value) => {
    const cleaned = value.replace(/[\s.-]/g, "");
    const regexFR = /^(?:(?:\+|00)33|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/;
    const regexSimple = /^(0[1-9])(\d{8})$|^(\+33|0033)[1-9](\d{8})$/;
    return {
      isValid: regexFR.test(value) || regexSimple.test(cleaned),
      message: "Format: 06 12 34 56 78 ou +33 6 12 34 56 78",
    };
  },

  siret: (value) => {
    const cleaned = value.replace(/\s/g, "");
    if (!/^\d{14}$/.test(cleaned)) {
      return { isValid: false, message: "Le SIRET doit contenir 14 chiffres" };
    }
    return {
      isValid: luhnCheck(cleaned, true),
      message: "SIRET invalide (vérification Luhn échouée)",
    };
  },

  siren: (value) => {
    const cleaned = value.replace(/\s/g, "");
    if (!/^\d{9}$/.test(cleaned)) {
      return { isValid: false, message: "Le SIREN doit contenir 9 chiffres" };
    }
    return {
      isValid: luhnCheck(cleaned, false),
      message: "SIREN invalide (vérification Luhn échouée)",
    };
  },

  iban: (value) => {
    const cleaned = value.replace(/\s/g, "").toUpperCase();
    if (!/^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/.test(cleaned)) {
      return { isValid: false, message: "Format IBAN invalide" };
    }
    return {
      isValid: ibanMod97Check(cleaned),
      message: "IBAN invalide (vérification mod-97 échouée)",
    };
  },

  bic: createValidator(
    /^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$/,
    "Format BIC: 8 ou 11 caractères (ex: BNPAFRPP)"
  ),

  password: (value) => {
    const checks = {
      length: value.length >= 8,
      uppercase: /[A-Z]/.test(value),
      lowercase: /[a-z]/.test(value),
      number: /\d/.test(value),
      special: /[!@#$%^&*(),.?":{}|<>]/.test(value),
    };

    const missing: string[] = [];
    if (!checks.length) missing.push("8+ caractères");
    if (!checks.uppercase) missing.push("majuscule");
    if (!checks.lowercase) missing.push("minuscule");
    if (!checks.number) missing.push("chiffre");

    return {
      isValid: checks.length && checks.uppercase && checks.lowercase && checks.number,
      message: missing.length > 0 ? `Requis: ${missing.join(", ")}` : undefined,
    };
  },

  number: (value) => ({
    isValid: !isNaN(Number(value)) && value.trim() !== "",
    message: "Veuillez entrer un nombre valide",
  }),

  url: (value) => {
    try {
      new URL(value);
      return { isValid: true };
    } catch {
      return { isValid: false, message: "URL invalide (ex: https://exemple.fr)" };
    }
  },

  postal_code: createValidator(
    /^(?:0[1-9]|[1-8]\d|9[0-8])\d{3}$/,
    "Code postal français invalide (5 chiffres)"
  ),

  tva_intra: (value) => {
    const cleaned = value.replace(/\s/g, "").toUpperCase();
    if (!/^FR[0-9A-Z]{2}\d{9}$/.test(cleaned)) {
      return { isValid: false, message: "Format: FRXX123456789" };
    }
    // Vérification clé TVA
    const siren = cleaned.slice(4);
    const key = cleaned.slice(2, 4);
    const computedKey = ((12 + 3 * (parseInt(siren, 10) % 97)) % 97)
      .toString()
      .padStart(2, "0");
    return {
      isValid: key === computedKey,
      message: "Clé TVA invalide",
    };
  },

  nir: (value) => {
    const cleaned = value.replace(/\s/g, "");
    if (!/^[12][0-9]{2}(0[1-9]|1[0-2]|[2-9][0-9])(0[1-9]|[1-9][0-9]|2[AB])[0-9]{6}[0-9]{2}$/.test(cleaned)) {
      return { isValid: false, message: "Format NIR invalide (15 chiffres)" };
    }
    return {
      isValid: nirKeyCheck(cleaned),
      message: "Clé NIR invalide",
    };
  },

  carte_bancaire: (value) => {
    const cleaned = value.replace(/\s/g, "");
    if (!/^\d{16}$/.test(cleaned)) {
      return { isValid: false, message: "La carte doit contenir 16 chiffres" };
    }
    // Luhn inversé pour carte bancaire
    let sum = 0;
    for (let i = 0; i < 16; i++) {
      let digit = parseInt(cleaned[15 - i], 10);
      if (i % 2 === 1) {
        digit *= 2;
        if (digit > 9) digit -= 9;
      }
      sum += digit;
    }
    return {
      isValid: sum % 10 === 0,
      message: "Numéro de carte invalide",
    };
  },
};

// ============================================
// FORMATEURS
// ============================================

export const formatters: Partial<Record<InputType, FormatterFn>> = {
  phone: (value) => {
    const cleaned = value.replace(/\D/g, "");
    if (cleaned.startsWith("33") && cleaned.length <= 11) {
      return cleaned.replace(
        /^(\d{2})(\d{1})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?/,
        (_, p1, p2, p3, p4, p5, p6) =>
          ["+"+p1, p2, p3, p4, p5, p6].filter(Boolean).join(" ")
      );
    }
    return cleaned.slice(0, 10).replace(
      /(\d{2})(\d{2})?(\d{2})?(\d{2})?(\d{2})?/,
      (_, p1, p2, p3, p4, p5) => [p1, p2, p3, p4, p5].filter(Boolean).join(" ")
    );
  },

  siret: (value) => {
    const cleaned = value.replace(/\D/g, "").slice(0, 14);
    return cleaned.replace(
      /(\d{3})(\d{3})?(\d{3})?(\d{5})?/,
      (_, p1, p2, p3, p4) => [p1, p2, p3, p4].filter(Boolean).join("")
    );
  },

  siren: (value) => {
    const cleaned = value.replace(/\D/g, "").slice(0, 9);
    return cleaned.replace(
      /(\d{3})(\d{3})?(\d{3})?/,
      (_, p1, p2, p3) => [p1, p2, p3].filter(Boolean).join(" ")
    );
  },

  iban: (value) => {
    const cleaned = value.replace(/\s/g, "").toUpperCase().slice(0, 34);
    return cleaned.replace(/(.{4})/g, "$1 ").trim();
  },

  bic: (value) => value.toUpperCase().replace(/\s/g, "").slice(0, 11),

  tva_intra: (value) => value.toUpperCase().replace(/\s/g, "").slice(0, 13),

  nir: (value) => {
    const cleaned = value.replace(/\D/g, "").slice(0, 15);
    return cleaned.replace(
      /(\d{1})(\d{2})?(\d{2})?(\d{2})?(\d{3})?(\d{3})?(\d{2})?/,
      (_, p1, p2, p3, p4, p5, p6, p7) =>
        [p1, p2, p3, p4, p5, p6, p7].filter(Boolean).join(" ")
    );
  },

  carte_bancaire: (value) => {
    const cleaned = value.replace(/\D/g, "").slice(0, 16);
    return cleaned.replace(/(\d{4})/g, "$1 ").trim();
  },

  postal_code: (value) => value.replace(/\D/g, "").slice(0, 5),
};

// ============================================
// PLACEHOLDERS
// ============================================

export const placeholders: Partial<Record<InputType, string>> = {
  email: "exemple@domaine.fr",
  phone: "06 12 34 56 78",
  siret: "123 456 789 00012",
  siren: "123 456 789",
  iban: "FR76 1234 5678 9012 3456 7890 123",
  bic: "BNPAFRPP",
  postal_code: "75001",
  tva_intra: "FR12345678901",
  url: "https://exemple.fr",
  nir: "1 85 12 75 108 042 36",
  carte_bancaire: "1234 5678 9012 3456",
};

// ============================================
// MAPPING INPUT TYPE -> HTML TYPE
// ============================================

const htmlInputTypes: Partial<Record<InputType, HTMLInputType>> = {
  email: "email",
  password: "password",
  number: "number",
  url: "url",
  phone: "tel",
};

// ============================================
// STYLES
// ============================================

export const inputBaseClass = cn(
  "w-full px-3 py-2.5 bg-white dark:bg-gray-900 border rounded-xl text-sm",
  "transition-all duration-200 focus:outline-none focus:ring-2",
  "placeholder:text-gray-400 dark:placeholder:text-gray-500"
);

export const getInputClass = (options: {
  error?: boolean;
  isValid?: boolean;
  hasLeftIcon?: boolean;
  hasRightIcon?: boolean;
}): string => {
  const { error, isValid, hasLeftIcon, hasRightIcon } = options;
  
  return cn(
    inputBaseClass,
    hasLeftIcon && "pl-10",
    hasRightIcon && "pr-10",
    error
      ? "border-green-green focus:border-green-500 focus:ring-green-200 dark:border-green-700"
      : isValid
        ? "border-green-300 focus:border-green-500 focus:ring-green-200 dark:border-green-700"
        : "border-gray-200 dark:border-gray-700 focus:border-[#007E45] focus:ring-green-100/50"
  );
};

// ============================================
// ICÔNES SVG
// ============================================

const CheckIcon: React.FC<{ className?: string }> = ({ className }) => (
  <svg className={className} fill="currentColor" viewBox="0 0 20 20">
    <path
      fillRule="evenodd"
      d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
      clipRule="evenodd"
    />
  </svg>
);

const ErrorIcon: React.FC<{ className?: string }> = ({ className }) => (
  <svg className={className} fill="currentColor" viewBox="0 0 20 20">
    <path
      fillRule="evenodd"
      d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
      clipRule="evenodd"
    />
  </svg>
);

const InfoIcon: React.FC<{ className?: string }> = ({ className }) => (
  <svg className={className} fill="currentColor" viewBox="0 0 20 20">
    <path
      fillRule="evenodd"
      d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
      clipRule="evenodd"
    />
  </svg>
);

// ============================================
// COMPOSANT PRINCIPAL
// ============================================

export const InputField = forwardRef<HTMLInputElement | HTMLSelectElement, InputFieldProps>(
  (props, ref) => {
    const {
      label,
      inputType = "text",
      required = false,
      error: externalError,
      className = "",
      containerClassName = "",
      showValidation = true,
      showIcon = true,
      onValueChange,
      onChange,
      value: controlledValue,
      leftIcon,
      rightIcon,
      disableFormatting = false,
      disabled,
      maxLength,
      name,
      placeholder,
      id,
      onBlur: onBlurProp,
    } = props;

    // Extraire as et options de manière type-safe
    const as = 'as' in props ? props.as : "input";
    const options = 'options' in props ? props.options : [];

    const [internalValue, setInternalValue] = useState<string>("");
    const [touched, setTouched] = useState<boolean>(false);
    const [validationResult, setValidationResult] = useState<ValidationResult>({
      isValid: true,
    });

    // Valeur effective (contrôlée ou interne)
    const value =
      controlledValue !== undefined ? String(controlledValue) : internalValue;

    // Fonction de validation
    const validate = useCallback(
      (val: string): ValidationResult => {
        if (!val && !required) {
          return { isValid: true };
        }
        if (!val && required) {
          return { isValid: false, message: "Ce champ est requis" };
        }
        return validators[inputType](val);
      },
      [inputType, required]
    );

    // Gestion du changement
    const handleChange = useCallback(
      (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>): void => {
        let newValue = e.target.value;

        // Appliquer le formateur si disponible et non désactivé (seulement pour input)
        if (!disableFormatting && as !== "select") {
          const formatter = formatters[inputType];
          if (formatter) {
            newValue = formatter(newValue);
          }
        }

        // Mise à jour de la valeur interne si non contrôlé
        if (controlledValue === undefined) {
          setInternalValue(newValue);
        }

        // Validation
        const result = validate(newValue);
        setValidationResult(result);

        // Callbacks
        onValueChange?.(newValue, result.isValid);

        // Propager l'événement avec la valeur formatée
        if (onChange) {
          const syntheticEvent = {
            ...e,
            target: { ...e.target, value: newValue },
          } as ChangeEvent<HTMLInputElement | HTMLSelectElement>;
          onChange(syntheticEvent);
        }
      },
      [controlledValue, disableFormatting, inputType, onChange, onValueChange, validate, as]
    );

    // Gestion du blur
    const handleBlur = useCallback(
      (e: FocusEvent<HTMLInputElement | HTMLSelectElement>): void => {
        setTouched(true);
        const result = validate(value);
        setValidationResult(result);
        onBlurProp?.(e);
      },
      [onBlurProp, validate, value]
    );

    // États dérivés
    const showError = touched && !validationResult.isValid && showValidation;
    const showSuccess =
      touched && validationResult.isValid && value.length > 0 && showValidation && as !== "select";
    const displayError = externalError || (showError ? validationResult.message : undefined);
    const hasRightElement = (showIcon && as !== "select") || rightIcon;

    // Type HTML natif
    const htmlType: HTMLInputType = htmlInputTypes[inputType] || "text";

    // Classes communes
    const inputClasses = cn(
      getInputClass({
        error: !!displayError,
        isValid: showSuccess,
        hasLeftIcon: !!leftIcon,
        hasRightIcon: !!hasRightElement,
      }),
      disabled && "opacity-50 cursor-not-allowed",
      className
    );

    return (
      <div className={cn("space-y-1.5", containerClassName)}>
        {/* Header: Label + Message d'erreur/succès */}
        <div className="flex justify-between items-center">
          <label className="text-xs font-medium text-gray-900 dark:text-gray-300">
            {label}
            {required && <span className="text-[#007E45] ml-0.5">*</span>}
          </label>

          {displayError && (
            <span className="text-[10px] text-green-500 font-medium flex items-center gap-1">
              <InfoIcon className="w-3 h-3" />
              {displayError}
            </span>
          )}

          {showSuccess && !externalError && (
            <span className="text-[10px] text-green-500 font-medium flex items-center gap-1">
              <CheckIcon className="w-3 h-3" />
              Valide
            </span>
          )}
        </div>

        {/* Input/Select container */}
        <div className="relative">
          {/* Icône gauche */}
          {leftIcon && (
            <div className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none">
              {leftIcon}
            </div>
          )}

          {/* Select */}
          {as === "select" ? (
            <select
              ref={ref as React.Ref<HTMLSelectElement>}
              name={name}
              id={id}
              value={value}
              onChange={handleChange}
              onBlur={handleBlur}
              disabled={disabled}
              aria-invalid={!!displayError}
              className={inputClasses}
            >
              {options!.map((option) => (
                <option key={option.value} value={option.value}>
                  {option.label}
                </option>
              ))}
            </select>
          ) : (
            /* Input */
            <input
              ref={ref as React.Ref<HTMLInputElement>}
              type={htmlType}
              name={name}
              id={id}
              value={value}
              onChange={handleChange}
              maxLength={maxLength}
              onBlur={handleBlur}
              disabled={disabled}
              placeholder={placeholders[inputType] || placeholder}
              aria-invalid={!!displayError}
              aria-describedby={displayError ? `${id}-error` : undefined}
              className={inputClasses}
            />
          )}

          {/* Icône droite / Validation */}
          {/* {hasRightElement && as !== "select" && (
            <div className="absolute right-3 top-1/2 -translate-y-1/2">
              {rightIcon ? (
                rightIcon
              ) : touched && showIcon ? (
                showSuccess && !externalError ? (
                  <CheckIcon className="w-4 h-4 text-green-500" />
                ) : showError ? (
                  <ErrorIcon className="w-4 h-4 text-green-200" />
                ) : null
              ) : null}
            </div>
          )} */}
        </div>
      </div>
    );
  }
);

InputField.displayName = "InputField";

// ============================================
// HOOK DE VALIDATION
// ============================================

export const useInputValidation = (inputType: InputType) => {
  const validate = useCallback(
    (value: string): ValidationResult => validators[inputType](value),
    [inputType]
  );

  const format = useCallback(
    (value: string): string => {
      const formatter = formatters[inputType];
      return formatter ? formatter(value) : value;
    },
    [inputType]
  );

  return { validate, format };
};
