import {
  forwardRef,
  InputHTMLAttributes,
  TextareaHTMLAttributes,
  useState,
} from 'react';
import { ChevronDown, Eye, EyeOff } from 'lucide-react';
import { cn } from '@/lib/utils';
import {
  useController,
  useFormContext,
  UseControllerProps,
  FieldValues,
  FieldPath,
} from 'react-hook-form';
import { Button } from './button';

// ─────────────────────────────────────────────────────────────
// Shared style helpers
// ─────────────────────────────────────────────────────────────
const fieldBase = [
  'w-full rounded-xl border text-sm bg-gray-50 text-gray-900 placeholder:text-gray-400',
  'focus:outline-none focus:ring-2 transition-all duration-200',
  'disabled:opacity-60 disabled:cursor-not-allowed disabled:bg-gray-100',
].join(' ');

const fieldState = (error?: string) =>
  error
    ? 'border-red-400 bg-red-50/50 focus:ring-red-500/20 focus:border-red-500'
    : 'border-gray-200 hover:border-gray-300 focus:ring-blue-500/20 focus:border-blue-500';

// ─────────────────────────────────────────────────────────────
// Label
// ─────────────────────────────────────────────────────────────
interface LabelProps {
  htmlFor?: string;
  required?: boolean;
  children: React.ReactNode;
}

function Label({ htmlFor, required, children }: LabelProps) {
  return (
    <label
      htmlFor={htmlFor}
      className="block text-sm font-medium text-gray-700"
    >
      {children}
      {required && <span className="text-red-400 ml-0.5">*</span>}
    </label>
  );
}

// ─────────────────────────────────────────────────────────────
// FieldMessage — shows error (red) or hint (gray), error wins
// ─────────────────────────────────────────────────────────────
function FieldMessage({ error, hint }: { error?: string; hint?: string }) {
  const message = error ?? hint;
  if (!message) return null;
  return (
    <p
      className={cn(
        'text-xs flex items-center gap-1',
        error ? 'text-red-500' : 'text-gray-400'
      )}
    >
      {error && <span className="w-1 h-1 rounded-full bg-red-500 shrink-0" />}
      {message}
    </p>
  );
}

// ─────────────────────────────────────────────────────────────
// useFormField — bridges a field name to RHF context
// ─────────────────────────────────────────────────────────────
/**
 * Internal hook. Pulls field registration + error state from the nearest
 * FormProvider. Falls back gracefully if there's no RHF context (standalone use).
 */
function useFormField<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(name?: TName, rules?: UseControllerProps<TFieldValues, TName>['rules']) {
  // FormContext is null when used outside <FormProvider>
  const ctx = useFormContext<TFieldValues>();
  const hasContext = !!ctx && !!name;

  const controller = hasContext
    ? // eslint-disable-next-line react-hooks/rules-of-hooks
      useController<TFieldValues, TName>({ name, control: ctx.control, rules })
    : null;

  return {
    /** Pass directly to the <input> / <textarea> / <select> */
    fieldProps: controller?.field ?? {},
    /** The RHF validation error message, if any */
    rhfError: controller?.fieldState.error?.message,
  };
}

// ─────────────────────────────────────────────────────────────
// Input
// ─────────────────────────────────────────────────────────────
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
  label?: string;
  error?: string;
  hint?: string;
  /** Leading icon (left side) */
  icon?: React.ReactNode;
  /** Trailing icon — for non-interactive decorations; password toggle is automatic */
  rightIcon?: React.ReactNode;
  /** Called when the trailing icon area is clicked (e.g. clear button) */
  onRightIconClick?: () => void;
  wrapperClassName?: string;
}

/**
 * Base text input. Use standalone or wrap in `<FormProvider>` and pass `name`
 * to connect automatically to React Hook Form.
 *
 * When `type="password"`, a visibility toggle is injected automatically.
 */
export const Input = forwardRef<HTMLInputElement, InputProps>(
  (
    {
      label,
      error,
      hint,
      icon,
      rightIcon,
      onRightIconClick,
      className,
      wrapperClassName,
      id,
      type = 'text',
      ...props
    },
    ref
  ) => {
    const inputId = id ?? label?.toLowerCase().replace(/\s+/g, '-');
    const isPassword = type === 'password';
    const [showPassword, setShowPassword] = useState(false);

    const trailingIcon = isPassword ? (
      <Button
        type="button"
        tabIndex={-1}
        onClick={() => setShowPassword((v) => !v)}
        aria-label={showPassword ? 'Hide password' : 'Show password'}
        className="text-gray-400 hover:text-gray-600 transition-colors"
      >
        {showPassword ? (
          <EyeOff className="w-4 h-4" />
        ) : (
          <Eye className="w-4 h-4" />
        )}
      </Button>
    ) : rightIcon ? (
      onRightIconClick ? (
        <button
          type="button"
          tabIndex={-1}
          onClick={onRightIconClick}
          className="text-gray-400 hover:text-gray-600 transition-colors"
        >
          {rightIcon}
        </button>
      ) : (
        <span className="text-gray-400 pointer-events-none">{rightIcon}</span>
      )
    ) : null;

    return (
      <div className={cn('space-y-1.5', wrapperClassName)}>
        {label && (
          <Label htmlFor={inputId} required={props.required}>
            {label}
          </Label>
        )}

        <div className="relative">
          {icon && (
            <div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-gray-400">
              {icon}
            </div>
          )}

          <input
            ref={ref}
            id={inputId}
            type={isPassword ? (showPassword ? 'text' : 'password') : type}
            className={cn(
              fieldBase,
              fieldState(error),
              'py-2.5',
              icon ? 'pl-10' : 'pl-3.5',
              trailingIcon ? 'pr-10' : 'pr-3.5',
              className
            )}
            {...props}
          />

          {trailingIcon && (
            <div className="absolute inset-y-0 right-0 pr-3.5 flex items-center">
              {trailingIcon}
            </div>
          )}
        </div>

        <FieldMessage error={error} hint={hint} />
      </div>
    );
  }
);

Input.displayName = 'Input';

// ─────────────────────────────────────────────────────────────
// RHFInput — Input wired to React Hook Form
// ─────────────────────────────────────────────────────────────
export interface RHFInputProps<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> extends Omit<InputProps, 'name'> {
  /** Field name — must match a key in your form schema */
  name: TName;
  /** Optional validation rules (or use schema-level validation via resolver) */
  rules?: UseControllerProps<TFieldValues, TName>['rules'];
}

/**
 * Drop-in replacement for `Input` inside a `<FormProvider>`.
 *
 * ```tsx
 * const methods = useForm<{ email: string }>();
 *
 * <FormProvider {...methods}>
 *   <form onSubmit={methods.handleSubmit(onSubmit)}>
 *     <RHFInput name="email" label="Email" type="email" required />
 *   </form>
 * </FormProvider>
 * ```
 */
export function RHFInput<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({ name, rules, error, ...props }: RHFInputProps<TFieldValues, TName>) {
  const { fieldProps, rhfError } = useFormField<TFieldValues, TName>(
    name,
    rules
  );
  return <Input {...fieldProps} {...props} error={error ?? rhfError} />;
}

// ─────────────────────────────────────────────────────────────
// Textarea
// ─────────────────────────────────────────────────────────────
export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
  label?: string;
  error?: string;
  hint?: string;
  wrapperClassName?: string;
}

/**
 * Multiline textarea with matching visual style as `Input`.
 * Pass `rows` to control height; defaults to browser default.
 */
export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
  ({ label, error, hint, className, wrapperClassName, id, ...props }, ref) => {
    const inputId = id ?? label?.toLowerCase().replace(/\s+/g, '-');

    return (
      <div className={cn('space-y-1.5', wrapperClassName)}>
        {label && (
          <Label htmlFor={inputId} required={props.required}>
            {label}
          </Label>
        )}

        <textarea
          ref={ref}
          id={inputId}
          className={cn(
            fieldBase,
            fieldState(error),
            'px-3.5 py-2.5 resize-none',
            className
          )}
          {...props}
        />

        <FieldMessage error={error} hint={hint} />
      </div>
    );
  }
);

Textarea.displayName = 'Textarea';

// ─────────────────────────────────────────────────────────────
// RHFTextarea — Textarea wired to React Hook Form
// ─────────────────────────────────────────────────────────────
export interface RHFTextareaProps<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> extends Omit<TextareaProps, 'name'> {
  name: TName;
  rules?: UseControllerProps<TFieldValues, TName>['rules'];
}

/**
 * Drop-in replacement for `Textarea` inside a `<FormProvider>`.
 */
export function RHFTextarea<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({ name, rules, error, ...props }: RHFTextareaProps<TFieldValues, TName>) {
  const { fieldProps, rhfError } = useFormField<TFieldValues, TName>(
    name,
    rules
  );
  return <Textarea {...fieldProps} {...props} error={error ?? rhfError} />;
}

// ─────────────────────────────────────────────────────────────
// SelectInput
// ─────────────────────────────────────────────────────────────
export interface SelectOption {
  value: string;
  label: string;
}

export interface SelectInputProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
  label?: string;
  error?: string;
  hint?: string;
  options: SelectOption[];
  /** Disabled placeholder rendered as the first option */
  placeholder?: string;
  /** Leading icon (left side) */
  icon?: React.ReactNode;
  wrapperClassName?: string;
}

/**
 * Styled `<select>` with optional leading icon, placeholder, error & hint.
 * Trailing chevron is injected automatically.
 */
export const SelectInput = forwardRef<HTMLSelectElement, SelectInputProps>(
  (
    {
      label,
      error,
      hint,
      options,
      placeholder,
      icon,
      className,
      wrapperClassName,
      id,
      ...props
    },
    ref
  ) => {
    const inputId = id ?? label?.toLowerCase().replace(/\s+/g, '-');

    return (
      <div className={cn('space-y-1.5', wrapperClassName)}>
        {label && (
          <Label htmlFor={inputId} required={props.required}>
            {label}
          </Label>
        )}

        <div className="relative">
          {icon && (
            <div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-gray-400">
              {icon}
            </div>
          )}

          <select
            ref={ref}
            id={inputId}
            className={cn(
              fieldBase,
              fieldState(error),
              'py-2.5 appearance-none cursor-pointer',
              icon ? 'pl-10' : 'pl-3.5',
              'pr-10',
              className
            )}
            {...props}
          >
            {placeholder && (
              <option value="" disabled>
                {placeholder}
              </option>
            )}
            {options.map((opt) => (
              <option key={opt.value} value={opt.value}>
                {opt.label}
              </option>
            ))}
          </select>

          <div className="absolute inset-y-0 right-0 pr-3.5 flex items-center pointer-events-none text-gray-400">
            <ChevronDown className="w-4 h-4" />
          </div>
        </div>

        <FieldMessage error={error} hint={hint} />
      </div>
    );
  }
);

SelectInput.displayName = 'SelectInput';

// ─────────────────────────────────────────────────────────────
// RHFSelectInput — SelectInput wired to React Hook Form
// ─────────────────────────────────────────────────────────────
export interface RHFSelectInputProps<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> extends Omit<SelectInputProps, 'name'> {
  name: TName;
  rules?: UseControllerProps<TFieldValues, TName>['rules'];
}

/**
 * Drop-in replacement for `SelectInput` inside a `<FormProvider>`.
 */
export function RHFSelectInput<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({ name, rules, error, ...props }: RHFSelectInputProps<TFieldValues, TName>) {
  const { fieldProps, rhfError } = useFormField<TFieldValues, TName>(
    name,
    rules
  );
  return <SelectInput {...fieldProps} {...props} error={error ?? rhfError} />;
}

// ─────────────────────────────────────────────────────────────
// Re-export RHF primitives for convenience
// ─────────────────────────────────────────────────────────────
export { FormProvider, useForm, useFormContext } from 'react-hook-form';
export type { SubmitHandler } from 'react-hook-form';
