// ImportModal.tsx — Fenêtre d'import de données (Inventaire, Établissements, Utilisateurs)
// Design: Corporate Modernism — Itinova Budget SI

import { useState, useRef, useEffect } from 'react';
import { trpc } from '@/lib/trpc';
import { useAnnee } from '../contexts/AnneeContext';
import {
  Upload,
  X,
  Monitor,
  Building2,
  Users,
  CheckCircle2,
  AlertCircle,
  FileSpreadsheet,
  Info,
  Pencil,
  Save,
  RotateCcw,
  ChevronDown,
  ChevronUp,
  BrainCircuit,
} from 'lucide-react';
import { toast } from 'sonner';

// ─── Helper : lecture fichier → tableau de lignes (CSV ou XLSX) ────────────────
async function fileToRows(file: File): Promise<string[][]> {
  const isXlsx = file.name.match(/\.(xlsx|xls)$/i);
  if (isXlsx) {
    // XLSX pèse plusieurs centaines de Ko : il n'est requis qu'à l'ouverture
    // d'un fichier d'import et ne doit pas ralentir l'affichage initial.
    const XLSX = await import('xlsx');
    const buffer = await file.arrayBuffer();
    const wb = XLSX.read(buffer, { type: 'array', cellDates: true });
    const ws = wb.Sheets[wb.SheetNames[0]];
    // sheet_to_json avec header:1 retourne un tableau de tableaux
    const rows = XLSX.utils.sheet_to_json<string[]>(ws, { header: 1, defval: '' });
    return rows.map(r => r.map(c => String(c ?? '').trim()));
  } else {
    // CSV : détection du séparateur
    const text = await file.text();
    const lines = text.split('\n').filter(l => l.trim());
    const sep = lines[0].includes(';') ? ';' : ',';
    return lines.map(l => l.split(sep).map(c => c.trim().replace(/"/g, '').replace(/[\r\n]/g, '')));
  }
}

// ─── Helpers date / vétusté ──────────────────────────────────────────────────

const DATE_REF_IMPORT = new Date('2027-01-01');

function parseImportDate(val: string): Date | null {
  if (!val || val === '-' || val.trim() === '') return null;
  const clean = val.trim();
  const dmy = clean.match(/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2,4})$/);
  if (dmy) {
    const [, d, m, y] = dmy;
    const year = y.length === 2 ? (parseInt(y) > 50 ? 1900 + parseInt(y) : 2000 + parseInt(y)) : parseInt(y);
    return new Date(year, parseInt(m) - 1, parseInt(d));
  }
  const ymd = clean.match(/^(\d{4})-(\d{2})-(\d{2})/);
  if (ymd) return new Date(clean);
  const dt = new Date(clean);
  return isNaN(dt.getTime()) ? null : dt;
}

function calcAgeAns(dateStr: string): number | null {
  const d = parseImportDate(dateStr);
  if (!d) return null;
  const diffMs = DATE_REF_IMPORT.getTime() - d.getTime();
  if (diffMs < 0) return null;
  return Math.floor(diffMs / (1000 * 60 * 60 * 24 * 365.25));
}

// ─── Types ────────────────────────────────────────────────────────────────────

export interface Etablissement {
  code: string;
  nom: string;
  groupe?: string;
  ville?: string;
  actif: boolean;
}

export interface Utilisateur {
  id: string;
  nom: string;
  prenom: string;
  email: string;
  role: 'admin' | 'standard' | 'lecture';
  etablissements: string[]; // codes établissements
  actif: boolean;
}

// ─── Storage helpers ──────────────────────────────────────────────────────────

const ETAB_KEY = 'budgetsi_etablissements';
const USER_KEY = 'budgetsi_utilisateurs';
const IMPORT_PROMPT_KEY = 'budgetsi_import_prompt';

const DEFAULT_IMPORT_PROMPT = `# Processus d'import de l'inventaire ISI-APP

## Format du fichier source
- Export CSV depuis ISI-APP (séparateur : point-virgule ou virgule)
- Encodage : UTF-8 ou Windows-1252
- Première ligne : en-têtes de colonnes

## Extraction du code établissement
- Colonne source : "libellé" (1ère colonne)
- Règle : prendre les 8 premiers caractères du libellé
- Exemple : libellé "1083MAS-F4833" → code établissement "1083MAS"
- Exemple : libellé "3069UDA-P5191" → code établissement "3069UDA"

## Extraction du nom établissement
- Colonne source : "entité"
- Utiliser la valeur telle quelle comme nom de l'établissement

## Détection du type de poste (fixe / portable)
- Colonne source : "libellé"
- PC fixe : le libellé contient "-F" ou "-FIX" après le code établissement
  Exemple : 1083SYL-F4833 → PC Fixe
- PC portable : le libellé contient "-P" ou "-POR" après le code établissement
  Exemple : 3069UDA-P5191 → PC Portable

## Calcul de la vétusté
- Utiliser en priorité la colonne "date de mise en service"
- Si cette colonne est vide ou absente, utiliser la colonne "date d'achat"
- L'âge en années est calculé depuis cette date jusqu'à la date de référence (01/01/2027)

## Règles de traitement
1. Les établissements uniques (code + nom) sont extraits et enregistrés dans la liste des établissements
2. Les établissements déjà présents ne sont pas écrasés (fusion intelligente)
3. Les lignes sans libellé ou sans entité sont ignorées
4. Chaque ligne = un poste (un poste = une ligne)

## Comportement attendu
- Créer ou mettre à jour la liste des établissements (code 8 car. + nom depuis "entité")
- Classifier chaque poste en fixe ou portable selon le libellé
- Calculer l'âge à partir de la date de mise en service (ou date d'achat si vide)
- Enregistrer les métadonnées du fichier importé (nom, date, nb lignes)
- Afficher un résumé : nb lignes traitées, nb établissements détectés, nb fixes, nb portables`;

export function loadImportPrompt(): string {
  try {
    const raw = localStorage.getItem(IMPORT_PROMPT_KEY);
    return raw ? JSON.parse(raw) : DEFAULT_IMPORT_PROMPT;
  } catch { return DEFAULT_IMPORT_PROMPT; }
}

export function saveImportPrompt(prompt: string) {
  localStorage.setItem(IMPORT_PROMPT_KEY, JSON.stringify(prompt));
}

export function loadEtablissements(): Etablissement[] {
  try {
    const raw = localStorage.getItem(ETAB_KEY);
    return raw ? JSON.parse(raw) : [];
  } catch { return []; }
}

export function saveEtablissements(data: Etablissement[]) {
  localStorage.setItem(ETAB_KEY, JSON.stringify(data));
}

export function loadUtilisateurs(): Utilisateur[] {
  try {
    const raw = localStorage.getItem(USER_KEY);
    return raw ? JSON.parse(raw) : [];
  } catch { return []; }
}

export function saveUtilisateurs(data: Utilisateur[]) {
  localStorage.setItem(USER_KEY, JSON.stringify(data));
}

// ─── Sub-components ───────────────────────────────────────────────────────────

type ImportTab = 'inventaire' | 'etablissements' | 'utilisateurs';

interface DropZoneProps {
  label: string;
  description: string;
  accept: string;
  onFile: (file: File) => void;
  status: 'idle' | 'success' | 'error';
  statusMsg?: string;
}

function DropZone({ label, description, accept, onFile, status, statusMsg }: DropZoneProps) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [dragging, setDragging] = useState(false);

  const handleFile = (file: File) => {
    if (!file) return;
    onFile(file);
  };

  return (
    <div
      className={`border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-all duration-200 ${
        dragging
          ? 'border-blue-400 bg-blue-50'
          : status === 'success'
          ? 'border-emerald-400 bg-emerald-50'
          : status === 'error'
          ? 'border-red-400 bg-red-50'
          : 'border-border hover:border-blue-300 hover:bg-blue-50/40'
      }`}
      onClick={() => inputRef.current?.click()}
      onDragOver={e => { e.preventDefault(); setDragging(true); }}
      onDragLeave={() => setDragging(false)}
      onDrop={e => {
        e.preventDefault();
        setDragging(false);
        const file = e.dataTransfer.files[0];
        if (file) handleFile(file);
      }}
    >
      <input
        ref={inputRef}
        type="file"
        accept={accept}
        className="hidden"
        onChange={e => { const f = e.target.files?.[0]; if (f) handleFile(f); e.target.value = ''; }}
      />
      {status === 'success' ? (
        <CheckCircle2 className="w-10 h-10 text-emerald-500 mx-auto mb-3" />
      ) : status === 'error' ? (
        <AlertCircle className="w-10 h-10 text-red-500 mx-auto mb-3" />
      ) : (
        <FileSpreadsheet className="w-10 h-10 text-blue-400 mx-auto mb-3" />
      )}
      <p className="font-semibold text-sm text-foreground">{label}</p>
      <p className="text-xs text-muted-foreground mt-1">{description}</p>
      {statusMsg && (
        <p className={`text-xs mt-2 font-medium ${status === 'success' ? 'text-emerald-600' : 'text-red-600'}`}>
          {statusMsg}
        </p>
      )}
      {status === 'idle' && (
        <button className="mt-3 inline-flex items-center gap-1.5 px-4 py-1.5 bg-blue-600 text-white text-xs rounded-lg hover:bg-blue-700 transition-colors">
          <Upload className="w-3.5 h-3.5" />
          Choisir un fichier
        </button>
      )}
    </div>
  );
}

// ─── Main Component ───────────────────────────────────────────────────────────

interface ImportModalProps {
  open: boolean;
  onClose: () => void;
}

export function ImportModal({ open, onClose }: ImportModalProps) {
  const { annee } = useAnnee();
  const utils = trpc.useUtils();
  // Ne faire la requête que quand la modale est ouverte (et non sur /login)
  const isLoginPage = typeof window !== 'undefined' && window.location.pathname === '/login';
  const { data: inventaireMetaList } = trpc.inventaire.listMeta.useQuery(undefined, {
    enabled: open && !isLoginPage,
    retry: false,
  });
  const importInventaireMutation = trpc.inventaire.import.useMutation({
    onSuccess: () => {
      utils.inventaire.get.invalidate();
      utils.inventaire.listMeta.invalidate();
      utils.inventaire.getStats.invalidate();
      window.dispatchEvent(new CustomEvent('budgetsi_inventaire_updated', { detail: { annee } }));
    },
  });
  const upsertEtabMutation = trpc.etablissements.upsert.useMutation({
    onSuccess: () => utils.etablissements.list.invalidate(),
  });
  const importUsersMutation = trpc.users.importBulk.useMutation();
  const [tab, setTab] = useState<ImportTab>('inventaire');
  const [inventaireStatus, setInventaireStatus] = useState<'idle' | 'success' | 'error'>('idle');
  const [inventaireMsg, setInventaireMsg] = useState('');
  const [etabStatus, setEtabStatus] = useState<'idle' | 'success' | 'error'>('idle');
  const [etabMsg, setEtabMsg] = useState('');
  const [userStatus, setUserStatus] = useState<'idle' | 'success' | 'error'>('idle');
  const [userMsg, setUserMsg] = useState('');

  // ── Prompt de processus d'import ────────────────────────────────────────────
  const [importPrompt, setImportPrompt] = useState<string>(() => loadImportPrompt());
  const [promptDraft, setPromptDraft] = useState<string>('');
  const [promptEditing, setPromptEditing] = useState(false);
  const [promptExpanded, setPromptExpanded] = useState(false);
  const [promptSaved, setPromptSaved] = useState(false);

  // Sync prompt depuis localStorage à l'ouverture
  useEffect(() => {
    if (open) {
      const p = loadImportPrompt();
      setImportPrompt(p);
      setPromptDraft(p);
      setPromptEditing(false);
    }
  }, [open]);

  const handlePromptEdit = () => {
    setPromptDraft(importPrompt);
    setPromptEditing(true);
    setPromptExpanded(true);
  };

  const handlePromptSave = () => {
    saveImportPrompt(promptDraft);
    setImportPrompt(promptDraft);
    setPromptEditing(false);
    setPromptSaved(true);
    toast.success('Prompt enregistré', { description: 'La configuration du processus d\'import a été sauvegardée.' });
    setTimeout(() => setPromptSaved(false), 2500);
  };

  const handlePromptCancel = () => {
    setPromptDraft(importPrompt);
    setPromptEditing(false);
  };

  const handlePromptReset = () => {
    if (!confirm('Réinitialiser le prompt aux valeurs par défaut ?')) return;
    setPromptDraft(DEFAULT_IMPORT_PROMPT);
    saveImportPrompt(DEFAULT_IMPORT_PROMPT);
    setImportPrompt(DEFAULT_IMPORT_PROMPT);
    setPromptEditing(false);
    toast.info('Prompt réinitialisé');
  };

  if (!open) return null;

  // ── Inventaire ISI-APP ──────────────────────────────────────────────────────
  // Colonnes attendues dans l'export ISI-APP :
  //   code_etab (ou code_etablissement, code etab…) — obligatoire
  //   nom_etab  (ou nom_etablissement, libelle etab, etablissement…) — obligatoire
  //   type      (fixe / portable / bureau / laptop…)
  //   date_achat, modele, fabricant — optionnels
  const handleInventaireFile = async (file: File) => {
    try {
      if (!file.name.match(/\.(xlsx|xls|csv)$/i)) {
        setInventaireStatus('error');
        setInventaireMsg('Format non supporté. Utilisez .xlsx, .xls ou .csv');
        return;
      }

      // Lecture unifiée CSV + XLSX via SheetJS
      const rows = await fileToRows(file);
      if (rows.length < 2) {
        setInventaireStatus('error');
        setInventaireMsg('Fichier vide ou format invalide (moins de 2 lignes)');
        return;
      }

      const rawHeaders = rows[0].map(h => h.toLowerCase().replace(/[\r\n]/g, ''));

      // ── Format ISI-APP : extraction par libellé et entité ──────────────────
      // Colonne "libellé" : contient le code poste (ex: 1083MAS-F4833)
      //   → code établissement = 8 premiers caractères avant le tiret
      //   → type = F/FIX → fixe, P/POR → portable
      // Colonne "entité" : nom de l'établissement
      // Colonne "date de mise en service" (priorité) ou "date d'achat" : pour la vétusté

      const libelleIdx = rawHeaders.findIndex(h =>
        h === 'libellé' || h === 'libelle' || h === 'label' ||
        h.includes('libellé') || h.includes('libelle')
      );
      const entiteIdx = rawHeaders.findIndex(h =>
        h === 'entité' || h === 'entite' || h === 'entity' ||
        h.includes('entité') || h.includes('entite')
      );
      const dateMiseEnServiceIdx = rawHeaders.findIndex(h =>
        h.includes('mise en service') || h.includes('miseenservice') || h.includes('mise_en_service') ||
        h.includes('service')
      );
      const dateAchatIdx = rawHeaders.findIndex(h =>
        h.includes('achat') || h.includes('purchase') || h.includes('date_achat')
      );
      const groupeIdx = rawHeaders.findIndex(h =>
        h.includes('groupe') || h.includes('association') || h.includes('groupement')
      );
      const villeIdx = rawHeaders.findIndex(h =>
        h.includes('ville') || h.includes('commune') || h.includes('localite') || h.includes('localité')
      );

      // Fallback : si pas de colonne libellé/entité, chercher code/nom classiques
      const codeIdxFallback = libelleIdx === -1 ? rawHeaders.findIndex(h =>
        h === 'code' || h.includes('code_etab') || h.includes('code etablissement') ||
        (h.startsWith('code') && h.length < 20)
      ) : -1;
      const nomIdxFallback = entiteIdx === -1 ? rawHeaders.findIndex(h =>
        h === 'nom' || h.includes('nom_etab') || h.includes('nom etablissement') ||
        h.includes('etablissement') || (h.startsWith('nom') && h.length < 20)
      ) : -1;

      const useIsiFormat = libelleIdx !== -1 && entiteIdx !== -1;
      const useClassicFormat = !useIsiFormat && codeIdxFallback !== -1 && nomIdxFallback !== -1;

      if (!useIsiFormat && !useClassicFormat) {
        setInventaireStatus('error');
        setInventaireMsg(
          `Colonnes introuvables. En-têtes détectés : ${rawHeaders.slice(0, 10).join(', ')}…\n` +
          'Format ISI-APP attendu : colonnes "libellé" et "entité". ' +
          'Format alternatif : colonnes "code" et "nom".'
        );
        return;
      }

      // Index colonnes supplémentaires ISI-APP
      const typeMaterielIdx = rawHeaders.findIndex(h =>
        h === 'type de matériel' || h === 'type materiel' || h === 'type' ||
        h.includes('type de mat') || h.includes('type mat')
      );
      const modeleIdx = rawHeaders.findIndex(h =>
        h === 'modèle' || h === 'modele' || h === 'model' ||
        h.includes('modèle') || h.includes('modele')
      );
      const fabricantIdx = rawHeaders.findIndex(h =>
        h === 'fabricant' || h === 'marque' || h === 'brand' ||
        h.includes('fabricant') || h.includes('marque')
      );
      // Compteurs pour le résumé
      let nbFixes = 0;
      let nbPortables = 0;

      // ── Structures pour stocker les postes complets par établissement ────────────
      // etabData : code → { nom, fixes[], portables[] }

      interface PosteData {
        libelle: string;
        date_achat: string;
        age_ans: number | null;
        a_renouveler: boolean;
        modele: string;
        fabricant: string;
      }
      interface EtabData {
        nom: string;
        groupe?: string;
        ville?: string;
        fixes: PosteData[];
        portables: PosteData[];
      }

      const etabData = new Map<string, EtabData>();

      for (const cols of rows.slice(1)) {
        let code = '';
        let nom = '';
        let typePoste: 'fixe' | 'portable' | 'autre' = 'autre';

        if (useIsiFormat) {
          const libelle = cols[libelleIdx]?.trim() || '';
          const dashIdx = libelle.indexOf('-');
          code = dashIdx > 0 ? libelle.substring(0, dashIdx) : libelle.substring(0, 8);
          code = code.trim();
          nom = cols[entiteIdx]?.trim() || '';

          // Priorité 1 : colonne "Type de matériel"
          if (typeMaterielIdx >= 0) {
            const t = (cols[typeMaterielIdx] || '').toLowerCase();
            if (t === 'fixe' || t === 'pc fixe' || t === 'desktop') typePoste = 'fixe';
            else if (t === 'portable' || t === 'laptop' || t === 'pc portable' || t === 'notebook') typePoste = 'portable';
          }
          // Fallback : détection par libellé
          if (typePoste === 'autre') {
            const afterDash = dashIdx > 0 ? libelle.substring(dashIdx + 1).toUpperCase() : '';
            if (afterDash.startsWith('F') || afterDash.startsWith('FIX')) typePoste = 'fixe';
            else if (afterDash.startsWith('P') || afterDash.startsWith('POR')) typePoste = 'portable';
          }
        } else {
          code = cols[codeIdxFallback]?.trim() || '';
          nom = cols[nomIdxFallback]?.trim() || '';
          // Format classique : colonne type si disponible
          if (typeMaterielIdx >= 0) {
            const t = (cols[typeMaterielIdx] || '').toLowerCase();
            if (t.includes('fixe') || t.includes('desktop')) typePoste = 'fixe';
            else if (t.includes('portable') || t.includes('laptop')) typePoste = 'portable';
          }
        }

        if (!code || !nom) continue;
        // Ignorer les postes hors production si statut présent
        // (on garde tout : en production + en stock)

        // Initialiser l'établissement si besoin
        if (!etabData.has(code)) {
          etabData.set(code, {
            nom,
            groupe: groupeIdx >= 0 ? cols[groupeIdx]?.trim() || undefined : undefined,
            ville: villeIdx >= 0 ? cols[villeIdx]?.trim() || undefined : undefined,
            fixes: [],
            portables: [],
          });
        }

        // Calculer l'âge du poste
        const dateMES = dateMiseEnServiceIdx >= 0 ? cols[dateMiseEnServiceIdx]?.trim() || '' : '';
        const dateAchat = dateAchatIdx >= 0 ? cols[dateAchatIdx]?.trim() || '' : '';
        const dateRef = dateMES || dateAchat; // priorité mise en service
        const age = calcAgeAns(dateRef);
        const libelle = useIsiFormat ? cols[libelleIdx]?.trim() || '' : code;
        const modele = modeleIdx >= 0 ? cols[modeleIdx]?.trim() || '' : '';
        const fabricant = fabricantIdx >= 0 ? cols[fabricantIdx]?.trim() || '' : '';

        const poste: PosteData = {
          libelle,
          date_achat: dateRef,
          age_ans: age,
          a_renouveler: false, // sera recalculé dynamiquement par useBudgetData
          modele,
          fabricant,
        };

        const etab = etabData.get(code)!;
        if (typePoste === 'fixe') {
          etab.fixes.push(poste);
          nbFixes++;
        } else if (typePoste === 'portable') {
          etab.portables.push(poste);
          nbPortables++;
        } else {
          // Type inconnu : on l'ignore dans les compteurs mais on ne le perd pas
          // On peut le mettre en fixe par défaut si besoin
        }
      }

      if (etabData.size === 0) {
        setInventaireStatus('error');
        setInventaireMsg('Aucun établissement extrait. Vérifiez que les colonnes code/nom contiennent des données.');
        return;
      }

      // ── Construire le payload pour tRPC ──────────────────────────────────────
      const postesPayload: Array<{ etablissementCode: string; libelle?: string | null; typePoste: 'fixe' | 'portable'; dateRef?: string | null; ageAns?: string | null; modele?: string | null; fabricant?: string | null }> = [];

      etabData.forEach((d, code) => {
        d.fixes.forEach(p => postesPayload.push({ etablissementCode: code, libelle: p.libelle || null, typePoste: 'fixe', dateRef: p.date_achat || null, ageAns: p.age_ans !== null ? String(p.age_ans) : null, modele: p.modele || null, fabricant: p.fabricant || null }));
        d.portables.forEach(p => postesPayload.push({ etablissementCode: code, libelle: p.libelle || null, typePoste: 'portable', dateRef: p.date_achat || null, ageAns: p.age_ans !== null ? String(p.age_ans) : null, modele: p.modele || null, fabricant: p.fabricant || null }));
      });

      // ── Envoyer à la base de données via tRPC ──────────────────────────────────
      await importInventaireMutation.mutateAsync({ annee, filename: file.name, postes: postesPayload });

      // ── Upsert établissements dans la BDD ──────────────────────────────────────
      for (const [code, info] of Array.from(etabData.entries())) {
        try {
          await upsertEtabMutation.mutateAsync({ code, nom: info.nom, groupe: info.groupe ?? null, ville: info.ville ?? null, actif: true });
        } catch { /* ignore les erreurs d'upsert établissement */ }
      }

      const details = [
        `${rows.length - 1} poste${rows.length - 1 > 1 ? 's' : ''}`,
        `${etabData.size} établissement${etabData.size > 1 ? 's' : ''} détectés`,
        `${nbFixes} fixes`,
        `${nbPortables} portables`,
      ].join(' — ');

      setInventaireStatus('success');
      setInventaireMsg(`"${file.name}" importé — ${details}`);
      toast.success('Inventaire importé', {
        description: `${etabData.size} établissements — ${nbFixes} fixes, ${nbPortables} portables.`,
      });
    } catch (err) {
      console.error('Import inventaire:', err);
      setInventaireStatus('error');
      setInventaireMsg('Erreur lors de la lecture du fichier. Vérifiez le format CSV.');
    }
  };

  // ── Établissements ──────────────────────────────────────────────────────────
  const handleEtabFile = async (file: File) => {
    try {
      if (!file.name.match(/\.(xlsx|xls|csv)$/i)) {
        setEtabStatus('error');
        setEtabMsg('Format non supporté. Utilisez .xlsx, .xls ou .csv');
        return;
      }
      const text = await file.text();
      const lines = text.split('\n').filter(l => l.trim());
      if (lines.length < 2) {
        setEtabStatus('error');
        setEtabMsg('Fichier vide ou format invalide');
        return;
      }
      // Parsing CSV simple (séparateur ; ou ,)
      const sep = lines[0].includes(';') ? ';' : ',';
      const headers = lines[0].split(sep).map(h => h.trim().toLowerCase().replace(/"/g, ''));
      const codeIdx = headers.findIndex(h => h.includes('code'));
      const nomIdx = headers.findIndex(h => h.includes('nom') || h.includes('libelle') || h.includes('établissement'));
      const groupeIdx = headers.findIndex(h => h.includes('groupe') || h.includes('association'));
      const villeIdx = headers.findIndex(h => h.includes('ville') || h.includes('commune'));

      if (codeIdx === -1 || nomIdx === -1) {
        setEtabStatus('error');
        setEtabMsg('Colonnes "code" et "nom" requises dans le fichier');
        return;
      }

      const etabs: Etablissement[] = lines.slice(1).map(line => {
        const cols = line.split(sep).map(c => c.trim().replace(/"/g, ''));
        return {
          code: cols[codeIdx] || '',
          nom: cols[nomIdx] || '',
          groupe: groupeIdx >= 0 ? cols[groupeIdx] : undefined,
          ville: villeIdx >= 0 ? cols[villeIdx] : undefined,
          actif: true,
        };
      }).filter(e => e.code && e.nom);

      // Upsert dans la BDD via tRPC
      let nbImported = 0;
      for (const etab of etabs) {
        try {
          await upsertEtabMutation.mutateAsync({ code: etab.code, nom: etab.nom, groupe: etab.groupe ?? null, ville: etab.ville ?? null, actif: etab.actif });
          nbImported++;
        } catch { /* ignore */ }
      }
      // Aussi sauvegarder en localStorage pour compatibilité
      saveEtablissements(etabs);
      setEtabStatus('success');
      setEtabMsg(`${nbImported} établissements importés`);
      toast.success('Établissements importés', { description: `${nbImported} établissements chargés` });
    } catch {
      setEtabStatus('error');
      setEtabMsg('Erreur lors de la lecture du fichier');
    }
  };

  // ── Utilisateurs ────────────────────────────────────────────────────────────
  const handleUserFile = async (file: File) => {
    try {
      if (!file.name.match(/\.(xlsx|xls|csv)$/i)) {
        setUserStatus('error');
        setUserMsg('Format non supporté. Utilisez .xlsx, .xls ou .csv');
        return;
      }
      const text = await file.text();
      const lines = text.split('\n').filter(l => l.trim());
      if (lines.length < 2) {
        setUserStatus('error');
        setUserMsg('Fichier vide ou format invalide');
        return;
      }
      const sep = lines[0].includes(';') ? ';' : ',';
      const headers = lines[0].split(sep).map(h => h.trim().toLowerCase().replace(/"/g, ''));
      const nomIdx = headers.findIndex(h => h.includes('nom'));
      const prenomIdx = headers.findIndex(h => h.includes('prenom') || h.includes('prénom'));
      const emailIdx = headers.findIndex(h => h.includes('email') || h.includes('mail'));
      const roleIdx = headers.findIndex(h => h.includes('role') || h.includes('profil'));
      const etabIdx = headers.findIndex(h => h.includes('etablissement') || h.includes('code'));

      if (nomIdx === -1 || emailIdx === -1) {
        setUserStatus('error');
        setUserMsg('Colonnes "nom" et "email" requises dans le fichier');
        return;
      }

      const users: Utilisateur[] = lines.slice(1).map((line, i) => {
        const cols = line.split(sep).map(c => c.trim().replace(/"/g, ''));
        const roleRaw = roleIdx >= 0 ? cols[roleIdx].toLowerCase() : '';
        const role: Utilisateur['role'] = roleRaw.includes('admin') ? 'admin'
          : roleRaw.includes('lecture') ? 'lecture'
          : 'standard';
        const etabsRaw = etabIdx >= 0 ? cols[etabIdx] : '';
        const etablissements = etabsRaw ? etabsRaw.split('|').map(e => e.trim()).filter(Boolean) : [];
        return {
          id: `user_${Date.now()}_${i}`,
          nom: cols[nomIdx] || '',
          prenom: prenomIdx >= 0 ? cols[prenomIdx] : '',
          email: cols[emailIdx] || '',
          role,
          etablissements,
          actif: true,
        };
      }).filter(u => u.nom && u.email);

      // Import dans la BDD via tRPC
      const usersPayload = users.map(u => ({
        login: u.email.split('@')[0].replace(/[^a-zA-Z0-9._-]/g, '') || `user_${Date.now()}`,
        password: 'Itinova2027!',
        email: u.email || null,
        firstName: u.prenom || null,
        lastName: u.nom || null,
        role: (u.role === 'lecture' ? 'readonly' : u.role) as 'admin' | 'standard' | 'readonly',
      }));
      try {
        const result = await importUsersMutation.mutateAsync(usersPayload);
        setUserStatus('success');
        setUserMsg(`${result.created} utilisateurs créés, ${result.skipped} ignorés (login existant)`);
        toast.success('Utilisateurs importés', { description: `${result.created} créés — mot de passe par défaut : Itinova2027!` });
      } catch (err) {
        // Fallback localStorage
        saveUtilisateurs(users);
        setUserStatus('success');
        setUserMsg(`${users.length} utilisateurs importés (local)`);
        toast.success('Utilisateurs importés', { description: `${users.length} utilisateurs chargés` });
      }
    } catch {
      setUserStatus('error');
      setUserMsg('Erreur lors de la lecture du fichier');
    }
  };

  const tabs: { id: ImportTab; label: string; icon: React.ElementType; color: string }[] = [
    { id: 'inventaire', label: 'Inventaire postes', icon: Monitor, color: 'text-blue-600' },
    { id: 'etablissements', label: 'Établissements', icon: Building2, color: 'text-emerald-600' },
    { id: 'utilisateurs', label: 'Utilisateurs', icon: Users, color: 'text-violet-600' },
  ];

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4" style={{ background: 'rgba(0,0,0,0.5)' }}>
      <div className="bg-background rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] flex flex-col border border-border">
        {/* Header */}
        <div className="flex items-center justify-between px-6 py-4 border-b border-border">
          <div className="flex items-center gap-3">
            <div className="w-9 h-9 rounded-lg bg-blue-100 flex items-center justify-center">
              <Upload className="w-4.5 h-4.5 text-blue-600" />
            </div>
            <div>
              <h2 className="font-bold text-base text-foreground" style={{ fontFamily: 'Sora, sans-serif' }}>
                Imports &amp; Données
              </h2>
              <p className="text-xs text-muted-foreground">Importer des données depuis des fichiers CSV ou Excel</p>
            </div>
          </div>
          <button
            onClick={onClose}
            className="w-8 h-8 rounded-lg flex items-center justify-center text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
          >
            <X className="w-4 h-4" />
          </button>
        </div>

        {/* Tabs */}
        <div className="flex border-b border-border px-6">
          {tabs.map(t => {
            const Icon = t.icon;
            return (
              <button
                key={t.id}
                onClick={() => setTab(t.id)}
                className={`flex items-center gap-2 px-4 py-3 text-sm font-medium border-b-2 transition-colors -mb-px ${
                  tab === t.id
                    ? `border-blue-600 ${t.color}`
                    : 'border-transparent text-muted-foreground hover:text-foreground'
                }`}
              >
                <Icon className="w-4 h-4" />
                {t.label}
              </button>
            );
          })}
        </div>

        {/* Content */}
        <div className="flex-1 overflow-y-auto p-6 space-y-4">
          {tab === 'inventaire' && (
            <>
              {/* ── Prompt de processus d'import ── */}
              <div className="border border-border rounded-xl overflow-hidden">
                {/* En-tête du prompt */}
                <div
                  className="flex items-center justify-between px-4 py-3 bg-muted/40 cursor-pointer select-none"
                  onClick={() => !promptEditing && setPromptExpanded(e => !e)}
                >
                  <div className="flex items-center gap-2.5">
                    <div className="w-7 h-7 rounded-md bg-blue-100 flex items-center justify-center flex-shrink-0">
                      <BrainCircuit className="w-3.5 h-3.5 text-blue-600" />
                    </div>
                    <div>
                      <p className="text-sm font-semibold text-foreground">Prompt de processus d'import</p>
                      <p className="text-xs text-muted-foreground">Instructions de traitement du fichier ISI-APP</p>
                    </div>
                  </div>
                  <div className="flex items-center gap-2">
                    {!promptEditing && (
                      <button
                        onClick={e => { e.stopPropagation(); handlePromptEdit(); }}
                        className="flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-lg bg-blue-100 text-blue-700 hover:bg-blue-200 transition-colors font-medium"
                        title="Modifier le prompt"
                      >
                        <Pencil className="w-3 h-3" />
                        Modifier
                      </button>
                    )}
                    {!promptEditing && (
                      <span className="text-muted-foreground">
                        {promptExpanded
                          ? <ChevronUp className="w-4 h-4" />
                          : <ChevronDown className="w-4 h-4" />}
                      </span>
                    )}
                  </div>
                </div>

                {/* Corps du prompt */}
                {(promptExpanded || promptEditing) && (
                  <div className="border-t border-border">
                    {promptEditing ? (
                      <div className="p-4 space-y-3">
                        <textarea
                          value={promptDraft}
                          onChange={e => setPromptDraft(e.target.value)}
                          rows={16}
                          className="w-full px-3 py-2.5 text-xs font-mono border border-border rounded-lg bg-muted/30 focus:outline-none focus:ring-2 focus:ring-blue-300 focus:border-blue-400 resize-y leading-relaxed"
                          placeholder="Décrivez ici le processus d'import, les colonnes attendues, les règles de traitement…"
                          spellCheck={false}
                        />
                        <div className="flex items-center justify-between">
                          <button
                            onClick={handlePromptReset}
                            className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors"
                          >
                            <RotateCcw className="w-3 h-3" />
                            Réinitialiser par défaut
                          </button>
                          <div className="flex items-center gap-2">
                            <button
                              onClick={handlePromptCancel}
                              className="px-3 py-1.5 text-xs rounded-lg bg-muted hover:bg-muted/70 text-muted-foreground transition-colors"
                            >
                              Annuler
                            </button>
                            <button
                              onClick={handlePromptSave}
                              className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-medium transition-all ${
                                promptSaved
                                  ? 'bg-emerald-600 text-white'
                                  : 'bg-blue-600 text-white hover:bg-blue-700'
                              }`}
                            >
                              <Save className="w-3 h-3" />
                              {promptSaved ? 'Enregistré !' : 'Enregistrer'}
                            </button>
                          </div>
                        </div>
                      </div>
                    ) : (
                      <pre className="px-4 py-3 text-xs font-mono text-muted-foreground whitespace-pre-wrap leading-relaxed max-h-48 overflow-y-auto bg-muted/20">{importPrompt}</pre>
                    )}
                  </div>
                )}
              </div>

              {/* ── Indicateur imports par année ── */}
              <div className="border border-border rounded-xl overflow-hidden">
                <div className="px-4 py-2.5 bg-muted/30 border-b border-border">
                  <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">État des inventaires par année</p>
                </div>
                <div className="flex flex-wrap gap-2 p-3">
                  {[2025, 2026, 2027, 2028, 2029, 2030].map(yr => {
                    const metaRow = inventaireMetaList?.find(m => m.annee === yr);
                    const hasData = !!metaRow;
                    const meta = metaRow ? { filename: metaRow.filename, nbLignes: (metaRow.nbFixes ?? 0) + (metaRow.nbPortables ?? 0), date: metaRow.dateImport?.toISOString() ?? '' } : null;
                    return (
                      <div
                        key={yr}
                        className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg border text-xs font-medium ${
                          yr === annee
                            ? hasData
                              ? 'bg-emerald-100 border-emerald-400 text-emerald-800'
                              : 'bg-blue-100 border-blue-400 text-blue-800'
                            : hasData
                              ? 'bg-emerald-50 border-emerald-200 text-emerald-700'
                              : 'bg-muted/40 border-border text-muted-foreground'
                        }`}
                        title={meta ? `${meta.filename} — ${meta.nbLignes} lignes — ${new Date(meta.date).toLocaleDateString('fr-FR')}` : 'Aucun inventaire importé'}
                      >
                        <span className={`w-1.5 h-1.5 rounded-full flex-shrink-0 ${hasData ? 'bg-emerald-500' : 'bg-muted-foreground/40'}`} />
                        <span>{yr}</span>
                        {yr === annee && <span className="text-[10px] opacity-70">(actif)</span>}
                      </div>
                    );
                  })}
                </div>
              </div>

              {/* ── Info résumée ── */}
              <div className="flex items-start gap-3 p-3 bg-blue-50 border border-blue-200 rounded-xl">
                <Info className="w-4 h-4 text-blue-600 flex-shrink-0 mt-0.5" />
                <p className="text-xs text-blue-700">
                  Formats acceptés : <strong>.xlsx, .xls, .csv</strong> — Export ISI-APP.
                  Les établissements seront automatiquement extraits et enregistrés.
                  Consultez le prompt ci-dessus pour le détail des règles de traitement.
                </p>
              </div>

              {/* ── Zone de dépôt ── */}
              <DropZone
                label="Glissez votre fichier d'inventaire ici"
                description="Formats acceptés : .xlsx, .xls, .csv — Export ISI-APP"
                accept=".xlsx,.xls,.csv"
                onFile={handleInventaireFile}
                status={inventaireStatus}
                statusMsg={inventaireMsg}
              />
              {inventaireStatus !== 'idle' && (
                <button
                  onClick={() => { setInventaireStatus('idle'); setInventaireMsg(''); }}
                  className="text-xs text-muted-foreground hover:text-foreground underline"
                >
                  Réinitialiser
                </button>
              )}
            </>
          )}

          {tab === 'etablissements' && (
            <>
              <div className="flex items-start gap-3 p-4 bg-emerald-50 border border-emerald-200 rounded-xl">
                <Info className="w-4 h-4 text-emerald-600 flex-shrink-0 mt-0.5" />
                <div className="text-sm text-emerald-800">
                  <p className="font-semibold mb-1">Liste des établissements</p>
                  <p className="text-xs text-emerald-700">
                    Importez la liste des établissements au format CSV. Colonnes attendues :
                    <strong> code, nom</strong> (obligatoires), <em>groupe, ville</em> (optionnels).
                    Séparateur : <code>;</code> ou <code>,</code>.
                  </p>
                </div>
              </div>
              <DropZone
                label="Glissez votre fichier d'établissements ici"
                description="Formats acceptés : .xlsx, .xls, .csv"
                accept=".xlsx,.xls,.csv"
                onFile={handleEtabFile}
                status={etabStatus}
                statusMsg={etabMsg}
              />
              {etabStatus !== 'idle' && (
                <button
                  onClick={() => { setEtabStatus('idle'); setEtabMsg(''); }}
                  className="text-xs text-muted-foreground hover:text-foreground underline"
                >
                  Réinitialiser
                </button>
              )}
            </>
          )}

          {tab === 'utilisateurs' && (
            <>
              <div className="flex items-start gap-3 p-4 bg-violet-50 border border-violet-200 rounded-xl">
                <Info className="w-4 h-4 text-violet-600 flex-shrink-0 mt-0.5" />
                <div className="text-sm text-violet-800">
                  <p className="font-semibold mb-1">Liste des utilisateurs</p>
                  <p className="text-xs text-violet-700">
                    Importez la liste des utilisateurs au format CSV. Colonnes attendues :
                    <strong> nom, email</strong> (obligatoires), <em>prenom, role (admin/standard/lecture), etablissements</em> (optionnels).
                    Pour plusieurs établissements, séparez les codes par <code>|</code> dans la colonne <em>etablissements</em>.
                  </p>
                </div>
              </div>
              <DropZone
                label="Glissez votre fichier d'utilisateurs ici"
                description="Formats acceptés : .xlsx, .xls, .csv"
                accept=".xlsx,.xls,.csv"
                onFile={handleUserFile}
                status={userStatus}
                statusMsg={userMsg}
              />
              {userStatus !== 'idle' && (
                <button
                  onClick={() => { setUserStatus('idle'); setUserMsg(''); }}
                  className="text-xs text-muted-foreground hover:text-foreground underline"
                >
                  Réinitialiser
                </button>
              )}
            </>
          )}
        </div>

        {/* Footer */}
        <div className="px-6 py-4 border-t border-border flex justify-end">
          <button
            onClick={onClose}
            className="px-5 py-2 text-sm rounded-lg bg-muted hover:bg-muted/80 text-foreground transition-colors font-medium"
          >
            Fermer
          </button>
        </div>
      </div>
    </div>
  );
}
