'use client';

import React, { useEffect, useRef, useState } from 'react';
import { Formik, Form, useField, useFormikContext } from 'formik';
import { Cpu, Wifi, Plus, QrCode, CheckCircle2, X, Loader2, XCircle, AlertTriangle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '@/store';
import { setSaving } from '@/store/slices/installation-slice';
import {
  FormSelectField,
  SegmentedField,
  SectionCard,
  FieldGroup,
  FormikErrorToast,
} from '@/components/teltoinstall/_index';
import { useDeviceStep, useReferenceData } from '@/hooks/teltoinstall/_index';
import { InstallationRepository } from '@/repositories/teltoinstall/_index';
import { WIZARD_FORM_ID } from '../constants';

interface Props {
  onNext: () => void;
}

type ImeiCheck = 'idle' | 'checking' | 'found' | 'not_found' | 'error';

/** Retour visuel de la vérification Teltonika, sous le champ IMEI. */
function ImeiCheckStatus({ status }: { status: ImeiCheck }) {
  const { t } = useTranslation();
  if (status === 'checking') {
    return (
      <p className="flex items-center gap-1.5 text-xs text-muted-foreground">
        <Loader2 className="w-3.5 h-3.5 animate-spin" />
        {t('device.imeiChecking')}
      </p>
    );
  }
  if (status === 'found') {
    return (
      <p className="flex items-center gap-1.5 text-xs text-green-600">
        <CheckCircle2 className="w-3.5 h-3.5" />
        {t('device.imeiFound')}
      </p>
    );
  }
  if (status === 'not_found') {
    return (
      <p className="flex items-center gap-1.5 text-xs text-red-500">
        <XCircle className="w-3.5 h-3.5" />
        {t('device.imeiNotFound')}
      </p>
    );
  }
  if (status === 'error') {
    return (
      <p className="flex items-center gap-1.5 text-xs text-amber-600">
        <AlertTriangle className="w-3.5 h-3.5" />
        {t('device.imeiCheckError')}
      </p>
    );
  }
  return null;
}

/**
 * Interroge Teltonika pour un IMEI complet. Ne lève jamais : renvoie un statut
 * exploitable tel quel (l'endpoint répond toujours 200 ; un échec réseau est
 * traduit en « error »). Réutilisé au blur (affichage) et à la soumission (gate).
 */
async function checkImei(value: string): Promise<ImeiCheck> {
  if (value.length !== 15) return 'idle';
  try {
    const status = await InstallationRepository.verifyImei(value);
    return status === 'found' ? 'found' : status === 'not_found' ? 'not_found' : 'error';
  } catch {
    return 'error';
  }
}

function ImeiField() {
  const { t } = useTranslation();
  const [field, meta, helpers] = useField('imei');
  // Le statut de vérification vit dans Formik (`imei_status`) et non en state
  // local : la soumission peut ainsi le lire pour bloquer « suivant » tant que
  // l'IMEI n'est pas confirmé « found » chez Teltonika.
  const { values, setFieldValue } = useFormikContext<{ imei_status: ImeiCheck }>();
  const imei = (field.value as string) ?? '';
  const showError = meta.touched && Boolean(meta.error);
  const check = values.imei_status;

  // IMEI de la dernière requête lancée : ignore une réponse obsolète si
  // l'installateur a re-modifié le champ entre-temps (course entre requêtes).
  const lastChecked = useRef('');

  async function verify(value: string) {
    if (value.length !== 15) {
      setFieldValue('imei_status', 'idle');
      return;
    }
    lastChecked.current = value;
    setFieldValue('imei_status', 'checking');
    const status = await checkImei(value);
    if (lastChecked.current !== value) return; // réponse d'une saisie précédente
    setFieldValue('imei_status', status);
  }

  // On n'affiche le retour Teltonika que si l'IMEI est complet et sans erreur de
  // format Formik (sinon le message de format prime).
  const showCheck = !showError && imei.length === 15 && check !== 'idle';

  return (
    <FieldGroup
      label={`${t('device.imei')} (15 ${t('device.digits')})`}
      required
      hint="Numéro IMEI à 15 chiffres, visible sur l'étiquette du boîtier"
      error={showError ? meta.error : undefined}
    >
      <div className="relative">
        <input
          id="imei"
          type="tel"
          inputMode="numeric"
          placeholder={t('device.imeiPlaceholder')}
          maxLength={15}
          value={imei}
          onBlur={(e) => {
            field.onBlur(e); // conserve le `touched` de Formik
            verify(imei); // vérifie l'IMEI auprès de Teltonika dès la sortie du champ
          }}
          name="imei"
          onChange={(e) => {
            helpers.setValue(e.target.value.replace(/\D/g, '').slice(0, 15));
            setFieldValue('imei_status', 'idle'); // toute modification annule le retour précédent
          }}
          className={`field-input font-mono pr-10 ${
            showError ? 'border-red-400 focus:ring-red-200 focus:border-red-400' : ''
          }`}
        />
        <QrCode className="absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-muted-foreground" />
      </div>
      {showCheck && <ImeiCheckStatus status={check} />}
    </FieldGroup>
  );
}

/**
 * Numéro de programme du boîtier : visible et obligatoire uniquement lorsque le
 * boîtier embarque un bus CAN (has_can_module dérivé du boîtier sélectionné).
 */
function ProgramNumberField() {
  const { t } = useTranslation();
  const { values } = useFormikContext<{ has_can_module: boolean }>();
  const [field, meta, helpers] = useField('program_number');
  const showError = meta.touched && Boolean(meta.error);

  if (!values.has_can_module) return null;

  return (
    <FieldGroup
      label={t('device.programNumber')}
      required
      hint={t('device.programNumberHint')}
      error={showError ? meta.error : undefined}
    >
      <input
        id="program_number"
        type="text"
        placeholder={t('device.programNumberPlaceholder')}
        name="program_number"
        onBlur={field.onBlur}
        value={(field.value as string) ?? ''}
        onChange={(e) => helpers.setValue(e.target.value)}
        className={`field-input ${
          showError ? 'border-red-400 focus:ring-red-200 focus:border-red-400' : ''
        }`}
      />
    </FieldGroup>
  );
}

/**
 * Boîtier posé = produit du catalogue fleet (source de vérité distante).
 * Sélectionner un produit fixe telematics_box_id et dérive gamme/modèle
 * (snapshots envoyés au backend, utilisés par le filtrage du brochage).
 */
/** Code du type de module CAN présélectionné quand le boîtier embarque un bus CAN. */
const DEFAULT_CAN_MODULE_CODE = 'lecteur_can';

function TelematicsBoxField() {
  const { t } = useTranslation();
  const { values, setFieldValue } = useFormikContext<{ product_line?: string }>();
  const { telematicsBoxes, canModuleTypes } = useReferenceData();
  const [field, meta] = useField('telematics_box_id');
  const showError = meta.touched && Boolean(meta.error);
  // Boîtier verrouillé : pose démarrée depuis une demande confirmée.
  const locked = useSelector((s: RootState) => s.installation.locked.telematics_box_id);

  // Présélectionne le module CAN selon le boîtier : « Module CAN additionnel »
  // reflète le bus CAN du boîtier (Oui s'il en a un, Non sinon) — le toggle est
  // verrouillé côté UI. Quand le boîtier a un CAN, on choisit « Lecteur CAN » par défaut.
  function applyCanPreselect(box?: { has_can?: boolean }) {
    const hasCan = Boolean(box?.has_can);
    setFieldValue('has_can_module', hasCan);
    if (!hasCan) {
      // Boîtier sans bus CAN : le numéro de programme ne s'applique pas.
      setFieldValue('program_number', '');
      return;
    }
    const lecteurCan = canModuleTypes.find((c) => c.code === DEFAULT_CAN_MODULE_CODE);
    if (lecteurCan) setFieldValue('can_module_type_id', lecteurCan.id);
  }

  // Dérive gamme/modèle depuis le boîtier sélectionné quand ils manquent (cas du
  // démarrage depuis une demande : seul telematics_box_id est pré-rempli, mais le
  // filtrage du brochage a besoin de product_line). La présélection CAN du boîtier
  // pré-rempli est, elle, gérée en amont dans initialValues (sans latence).
  useEffect(() => {
    if (!field.value || values.product_line) return;
    const box = telematicsBoxes.find((b) => String(b.id) === String(field.value));
    if (box) {
      setFieldValue('product_line', box.product_line ?? '');
      setFieldValue('model', box.model ?? '');
    }
  }, [field.value, values.product_line, telematicsBoxes, setFieldValue]);

  function handleChange(e: React.ChangeEvent<HTMLSelectElement>) {
    const id = e.target.value;
    setFieldValue('telematics_box_id', id);
    const box = telematicsBoxes.find((b) => String(b.id) === id);
    setFieldValue('product_line', box?.product_line ?? '');
    setFieldValue('model', box?.model ?? '');
    applyCanPreselect(box);
  }

  return (
    <FieldGroup
      label={t('device.telematicsBox')}
      required
      hint={t('device.telematicsBoxHint')}
      error={showError ? meta.error : undefined}
    >
      <select
        id="telematics_box_id"
        name="telematics_box_id"
        value={field.value ?? ''}
        onBlur={field.onBlur}
        onChange={handleChange}
        disabled={locked}
        className={`field-input ${locked ? 'opacity-60 cursor-not-allowed bg-muted' : ''} ${showError ? 'border-red-400 focus:ring-red-200 focus:border-red-400' : ''}`}
      >
        <option value="">{t('device.telematicsBoxPlaceholder')}</option>
        {telematicsBoxes.map((b) => (
          <option key={b.id} value={b.id}>
            {b.name}
          </option>
        ))}
      </select>
      {telematicsBoxes.length === 0 && (
        <p className="text-xs text-amber-600">{t('device.telematicsBoxUnavailable')}</p>
      )}
    </FieldGroup>
  );
}

function CanModuleSection() {
  const { t } = useTranslation();
  const { values } = useFormikContext<{ has_can_module: boolean }>();
  const { canModuleTypes } = useReferenceData();

  return (
    <div className="space-y-4">
      <div className="space-y-1.5">
        <label className="text-sm font-medium text-foreground">{t('device.hasCanModule')}</label>
        {/* Toggle en lecture seule : la présence d'un module CAN est dérivée du
            boîtier (has_can) et présélectionnée par TelematicsBoxField. */}
        <div className="toggle-group">
          {[
            { label: t('device.canModuleNo'), value: false },
            { label: t('device.canModuleYes'), value: true },
          ].map((opt) => (
            <button
              key={opt.label}
              type="button"
              disabled
              className={`toggle-btn opacity-60 cursor-not-allowed ${
                values.has_can_module === opt.value ? 'active' : ''
              }`}
            >
              {opt.label}
            </button>
          ))}
        </div>
      </div>

      {values.has_can_module && (
        <FormSelectField
          name="can_module_type_id"
          label={t('device.canModuleType')}
          options={canModuleTypes.map((c) => ({ value: c.id, label: c.label }))}
          required
        />
      )}
    </div>
  );
}

function AccessoriesSection() {
  const { t } = useTranslation();
  const { values, setFieldValue } = useFormikContext<{ accessory_ids: number[] }>();
  const { accessories } = useReferenceData();
  const [open, setOpen] = useState(false);

  const selected = accessories.filter((a) => values.accessory_ids.includes(a.id));

  function toggle(id: number) {
    const current = values.accessory_ids;
    setFieldValue(
      'accessory_ids',
      current.includes(id) ? current.filter((x) => x !== id) : [...current, id]
    );
  }

  return (
    <div className="space-y-2">
      {selected.length === 0 ? (
        <p className="text-sm text-muted-foreground text-center py-2">{t('device.noAccessory')}</p>
      ) : (
        <div className="space-y-1.5">
          {selected.map((acc) => (
            <div
              key={acc.id}
              className="flex items-center justify-between p-2.5 bg-primary/5 rounded-lg border border-primary/20"
            >
              <span className="text-sm font-medium text-primary">{acc.label}</span>
              <button
                type="button"
                onClick={() => toggle(acc.id)}
                className="w-6 h-6 flex items-center justify-center text-red-500 hover:bg-red-50 rounded"
              >
                <X className="w-4 h-4" />
              </button>
            </div>
          ))}
        </div>
      )}

      <button type="button" onClick={() => setOpen(!open)} className="btn-secondary text-sm py-3">
        {open ? t('device.closeList') : t('device.addAccessory')}
      </button>

      {open && (
        <div className="border border-border rounded-xl overflow-hidden animate-fade-in">
          {accessories.map((acc) => {
            const isSelected = values.accessory_ids.includes(acc.id);
            return (
              <button
                key={acc.id}
                type="button"
                onClick={() => toggle(acc.id)}
                className={`w-full text-left px-4 py-3 text-sm border-b border-border last:border-b-0 transition-colors ${
                  isSelected
                    ? 'bg-primary/10 text-primary font-medium'
                    : 'bg-white text-foreground hover:bg-muted'
                }`}
              >
                {isSelected && <CheckCircle2 className="w-4 h-4 inline mr-2 text-primary" />}
                {acc.label}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

export default function DeviceStep({ onNext }: Props) {
  const { t } = useTranslation();
  const dispatch = useDispatch();
  const { device, program_number, validationSchema, saveDevice } = useDeviceStep();
  const { canModuleTypes, telematicsBoxes } = useReferenceData();

  // Présélection synchrone du module CAN selon le boîtier (évite le délai d'un
  // useEffect post-rendu) : un boîtier à bus CAN impose « Oui » + « Lecteur CAN ».
  const selectedBox = telematicsBoxes.find(
    (b) => String(b.id) === String(device.telematics_box_id)
  );
  const lecteurCanId = canModuleTypes.find((c) => c.code === DEFAULT_CAN_MODULE_CODE)?.id;

  const initialValues = {
    stock_origin: device.stock_origin ?? '',
    telematics_box_id: device.telematics_box_id ?? '',
    product_line: device.product_line ?? '',
    model: device.model ?? '',
    imei: device.imei ?? '',
    has_can_module: selectedBox?.has_can ?? device.has_can_module ?? false,
    can_module_type_id: selectedBox?.has_can
      ? lecteurCanId ?? device.can_module_type_id ?? canModuleTypes[0]?.id ?? ''
      : device.can_module_type_id ?? canModuleTypes[0]?.id ?? '',
    program_number: program_number ?? '',
    iccid: device.sim_card?.iccid ?? '',
    accessory_ids: device.accessory_ids ?? [],
    // Statut de vérification Teltonika (non envoyé au backend) : pilote le retour
    // sous le champ et le blocage de « suivant ». On repart de « idle » pour
    // forcer une (re)vérification à la soumission.
    imei_status: 'idle' as ImeiCheck,
  };

  return (
    <Formik
      initialValues={initialValues}
      validationSchema={validationSchema}
      enableReinitialize
      onSubmit={async (values, { setErrors, setTouched, setFieldValue }) => {
        // Gate : l'IMEI doit être confirmé valide chez Teltonika avant de
        // continuer. Si le blur l'a déjà validé (« found »), on passe ; sinon on
        // (re)vérifie maintenant et on bloque tant que ce n'est pas « found »
        // (introuvable, ou vérification impossible). `isSaving` fait tourner le
        // spinner de « suivant » et empêche un double-clic pendant la vérif.
        let imeiStatus = values.imei_status;
        if (imeiStatus !== 'found') {
          dispatch(setSaving(true));
          setFieldValue('imei_status', 'checking');
          imeiStatus = await checkImei(values.imei);
          setFieldValue('imei_status', imeiStatus);
          dispatch(setSaving(false));
        }
        if (imeiStatus !== 'found') {
          setTouched({ imei: true } as never, false);
          setErrors({
            imei: imeiStatus === 'not_found' ? t('device.imeiNotFound') : t('device.imeiCheckError'),
          } as never);
          return;
        }

        const { ok, fieldErrors } = await saveDevice({
          stock_origin: values.stock_origin as never,
          telematics_box_id: values.telematics_box_id ? Number(values.telematics_box_id) : null,
          product_line: values.product_line as never,
          model: values.model || undefined,
          imei: values.imei,
          has_can_module: values.has_can_module,
          can_module_type_id: values.can_module_type_id ? Number(values.can_module_type_id) : undefined,
          program_number: values.has_can_module ? values.program_number : '',
          sim_card: { iccid: values.iccid || undefined },
          accessory_ids: values.accessory_ids as number[],
        });
        if (ok) {
          onNext();
        } else if (fieldErrors) {
          // Affiche les erreurs serveur en ligne (ex. IMEI déjà utilisé).
          setTouched(
            Object.keys(fieldErrors).reduce((acc, k) => ({ ...acc, [k]: true }), {}),
            false
          );
          setErrors(fieldErrors);
        }
      }}
    >
      <Form id={WIZARD_FORM_ID}>
        <FormikErrorToast />
        <SectionCard icon={<Cpu className="w-4 h-4" />} title={t('device.sectionTitle')}>
          <SegmentedField
            name="stock_origin"
            label={t('device.stockOrigin')}
            options={[
              { value: 'client', label: t('device.stockOriginClient') },
              { value: 'installer', label: t('device.stockOriginInstaller') },
              { value: 'both', label: t('device.stockOriginBoth') },
            ]}
            required
          />

          <TelematicsBoxField />

          <ImeiField />

          <ProgramNumberField />

          <CanModuleSection />
        </SectionCard>

        <SectionCard icon={<Wifi className="w-4 h-4" />} title={t('device.simCardTitle')}>
          <FieldGroup
            label={t('device.iccid')}
            hint="ICCID imprimé sur la carte SIM (19–20 chiffres)"
          >
            <IccidField />
          </FieldGroup>
          <div className="flex items-center gap-2 p-3 bg-blue-50 rounded-lg border border-blue-200">
            <Wifi className="w-4 h-4 text-blue-600 shrink-0" />
            <p className="text-xs text-blue-700">{t('device.iccidNote')}</p>
          </div>
        </SectionCard>

        <SectionCard icon={<Plus className="w-4 h-4" />} title={t('device.accessories')}>
          <AccessoriesSection />
        </SectionCard>
      </Form>
    </Formik>
  );
}

function IccidField() {
  const [field, , helpers] = useField('iccid');
  return (
    <input
      id="iccid"
      type="tel"
      inputMode="numeric"
      placeholder="Ex: 8944501910198765432"
      maxLength={20}
      name="iccid"
      onBlur={field.onBlur}
      value={(field.value as string) ?? ''}
      onChange={(e) => helpers.setValue(e.target.value.replace(/\D/g, '').slice(0, 20))}
      className="field-input font-mono"
    />
  );
}
