// ============================================================================
//  import.jsx — Modale Import depuis un CSV.
//  Flux à 3 étapes :
//    1. Choix de la source (LockSelf, Bitwarden, 1Password, Chrome, Firefox,
//       Autre/CSV générique)
//    2. Sélection du fichier
//    3. Mapping colonne CSV → champ CléPass (auto-rempli selon la source)
// ============================================================================

(function (global) {
  'use strict';

  const { useState, useRef, useMemo, useEffect } = React;

  // ─── Parseur CSV minimaliste ──────────────────────────────────────────────

  function parseCSV(text) {
    const lines = [];
    let line = [], field = '', inQuotes = false;
    const len = text.length;
    for (let i = 0; i < len; i++) {
      const ch = text[i];
      if (inQuotes) {
        if (ch === '"') {
          if (text[i + 1] === '"') { field += '"'; i++; }
          else { inQuotes = false; }
        } else {
          field += ch;
        }
      } else {
        if (ch === '"') { inQuotes = true; }
        else if (ch === ',' || ch === ';' || ch === '\t') {
          line.push(field); field = '';
        } else if (ch === '\n' || ch === '\r') {
          if (ch === '\r' && text[i + 1] === '\n') i++;
          line.push(field); field = '';
          if (line.some(c => c !== '')) lines.push(line);
          line = [];
        } else {
          field += ch;
        }
      }
    }
    if (field !== '' || line.length) {
      line.push(field);
      if (line.some(c => c !== '')) lines.push(line);
    }
    return lines;
  }

  // ─── Sources et leurs mappings préconfigurés ──────────────────────────────
  // Les clés `map` sont des HEADERS exacts (case-sensitive sauf indication).
  // Les valeurs sont les targets CléPass.

  const TARGETS = [
    { value: '',         label: '— Ignorer —' },
    { value: 'name',     label: 'Nom' },
    { value: 'domain',   label: 'Domaine / URL' },
    { value: 'username', label: 'Identifiant' },
    { value: 'password', label: 'Mot de passe' },
    { value: 'notes',    label: 'Notes (description) — concaténé si plusieurs' },
    { value: 'folder',   label: 'Dossier (chemin Cat/SubCat)' },
    { value: 'totp',     label: '2FA / TOTP (secret Base32)' },
  ];

  const IMPORT_SOURCES = [
    {
      id: 'locksoft',
      label: 'LockSelf',
      hint: 'Export CSV (séparateur ;)',
      map: {
        'Name': 'name',
        'Url': 'domain',
        'Username': 'username',
        'Password': 'password',
        'Category/SubCategory': 'folder',
        'Description': 'notes',
        'Opt1': 'notes',
        'Opt2': 'notes',
        'Opt3': 'notes',
        'Totp': 'totp',
      },
    },
    {
      id: 'bitwarden',
      label: 'Bitwarden',
      hint: 'Export CSV depuis Bitwarden Web Vault',
      map: {
        'name': 'name',
        'login_uri': 'domain',
        'login_username': 'username',
        'login_password': 'password',
        'folder': 'folder',
        'notes': 'notes',
        'login_totp': 'totp',
      },
    },
    {
      id: 'onepassword',
      label: '1Password',
      hint: 'Export CSV depuis 1Password 8',
      map: {
        'Title': 'name',
        'Url': 'domain',
        'Username': 'username',
        'Password': 'password',
        'Notes': 'notes',
        'OTPAuth': 'totp',
        'OTP Auth': 'totp',
      },
    },
    {
      id: 'chrome',
      label: 'Chrome / Edge',
      hint: 'chrome://password-manager/passwords → Exporter',
      map: {
        'name': 'name',
        'url': 'domain',
        'username': 'username',
        'password': 'password',
        'note': 'notes',
      },
    },
    {
      id: 'firefox',
      label: 'Firefox',
      hint: 'about:logins → Exporter en CSV',
      map: {
        'url': 'domain',
        'username': 'username',
        'password': 'password',
        'httpRealm': 'notes',
      },
    },
    {
      id: 'generic',
      label: 'Autre / Auto-détection',
      hint: 'CSV avec en-têtes lisibles (français ou anglais)',
      map: null, // auto-detect
    },
  ];

  // Aliases pour la détection auto (utilisés en mode 'generic' et en
  // complément quand la source n'a pas de match exact)
  const HEADER_ALIASES = {
    name:     ['name', 'title', 'nom', 'libelle', 'libellé', 'service', 'site'],
    domain:   ['url', 'uri', 'domain', 'domaine', 'website', 'web', 'adresse', 'login_uri'],
    username: ['username', 'user', 'email', 'login', 'identifiant', 'utilisateur', 'courriel', 'login_username'],
    password: ['password', 'motdepasse', 'mot de passe', 'pass', 'pwd', 'secret', 'login_password'],
    notes:    ['note', 'notes', 'comment', 'commentaire', 'remarque', 'description', 'extra',
               'opt1', 'opt2', 'opt3', 'option1', 'option2', 'option3'],
    folder:   ['folder', 'dossier', 'category', 'categorie', 'catégorie',
               'category/subcategory', 'subcategory', 'sous-categorie',
               'group', 'groupe', 'collection'],
    totp:     ['totp', '2fa', 'otp', 'otpauth', 'login_totp', 'otp_secret', 'authenticator', 'otp auth'],
  };

  // Construit le mapping {colIdx: targetField} pour une liste d'en-têtes,
  // selon la source choisie. Le map de la source prime, fallback sur aliases.
  function autoMapForSource(headers, sourceId) {
    const source = IMPORT_SOURCES.find(s => s.id === sourceId);
    const sourceMap = source?.map || null;
    const out = {};
    headers.forEach((h, i) => {
      const trimmed = String(h || '').trim();
      if (!trimmed) return;
      // 1) Match exact via le preset de la source.
      if (sourceMap && sourceMap[trimmed] !== undefined) {
        out[i] = sourceMap[trimmed];
        return;
      }
      // 2) Fallback : aliases génériques (case-insensitive).
      const lc = trimmed.toLowerCase();
      for (const [target, aliases] of Object.entries(HEADER_ALIASES)) {
        if (aliases.includes(lc)) {
          out[i] = target;
          return;
        }
      }
      // 3) Sinon : ignoré (pas dans le mapping).
    });
    return out;
  }

  // ─── Création de sous-dossiers à partir des paths « Cat/SubCat » ─────────

  function ensureFoldersFromPaths(paths, currentFolders) {
    const cloned = JSON.parse(JSON.stringify(currentFolders || []));
    const pathToId = new Map();
    const findChild = (list, name) =>
      list.find(f => (f.name || '').trim().toLowerCase() === name.toLowerCase());

    paths.forEach(path => {
      const segments = String(path).split('/').map(s => s.trim()).filter(Boolean);
      if (segments.length === 0) return;
      let parent = null;
      let list = cloned;
      segments.forEach((seg, idx) => {
        let f = findChild(list, seg);
        if (!f) {
          f = {
            id: CPCrypto.genId('f'),
            name: seg,
            icon: idx === 0 ? 'tool' : 'note',
            parentId: parent ? parent.id : null,
            children: [],
          };
          list.push(f);
        }
        if (!f.children) f.children = [];
        parent = f;
        list = f.children;
      });
      if (parent) pathToId.set(path, parent.id);
    });
    return { folders: cloned, pathToId };
  }

  // ─── Modal ────────────────────────────────────────────────────────────────

  const ImportModal = ({ folders, onImport, onCancel }) => {
    // step: 'source' | 'file' | 'mapping'
    const [step, setStep] = useState('source');
    const [source, setSource] = useState(null); // id de la source choisie
    const [rawText, setRawText] = useState('');
    const [rows, setRows] = useState([]);
    const [headers, setHeaders] = useState([]);
    const [reverseMap, setReverseMap] = useState({}); // { colIdx: target }
    const [fileName, setFileName] = useState('');
    const [error, setError] = useState('');
    const [busy, setBusy] = useState(false);
    const [progress, setProgress] = useState({ done: 0, total: 0 });
    const [defaultFolder, setDefaultFolder] = useState('');
    const [hasHeader, setHasHeader] = useState(true);
    const fileInputRef = useRef(null);

    // ─── Étape Fichier ──────────────────────────────────────────────────────
    const handleFile = async (file) => {
      if (!file) return;
      setError('');
      setFileName(file.name);
      try {
        const text = await file.text();
        setRawText(text);
        parseAndDetect(text, hasHeader);
        setStep('mapping');
      } catch (err) {
        setError('Lecture du fichier impossible : ' + err.message);
      }
    };

    const parseAndDetect = (text, withHeader) => {
      const all = parseCSV(text);
      if (all.length === 0) { setError('Fichier vide ou non valide.'); return; }
      let h, body;
      if (withHeader) {
        h = all[0];
        body = all.slice(1);
      } else {
        h = all[0].map((_, i) => 'Colonne ' + (i + 1));
        body = all;
      }
      setHeaders(h);
      setRows(body);
      setReverseMap(autoMapForSource(h, source));
    };

    const onDrop = (e) => {
      e.preventDefault();
      const file = e.dataTransfer.files?.[0];
      if (file) handleFile(file);
    };

    const updateRev = (colIdx, target) => {
      setReverseMap(m => ({ ...m, [colIdx]: target }));
    };

    // ─── Construction du payload final ──────────────────────────────────────
    const buildPayload = () => {
      // Inversion : { target: [colIdx, colIdx, ...] }
      const targetToCols = {};
      Object.entries(reverseMap).forEach(([colIdxStr, target]) => {
        if (!target) return;
        const idx = Number(colIdxStr);
        if (!targetToCols[target]) targetToCols[target] = [];
        targetToCols[target].push(idx);
      });

      // Helper : récupère la première valeur non vide pour un target
      const firstValue = (row, target) => {
        const cols = targetToCols[target];
        if (!cols) return '';
        for (const i of cols) {
          const v = (row[i] || '').trim();
          if (v) return v;
        }
        return '';
      };

      // Helper : concatène toutes les valeurs notes (avec préfixe header sauf
      // pour la première qui prend la valeur brute si c'est la "vraie"
      // description).
      const buildNotes = (row) => {
        const cols = targetToCols.notes || [];
        const parts = [];
        cols.forEach((i, idx) => {
          const v = (row[i] || '').trim();
          if (!v) return;
          // Si plusieurs colonnes mappées notes, on préfixe avec le header
          // pour distinguer (sauf la 1re qui passe en clair).
          if (cols.length === 1 || idx === 0) parts.push(v);
          else parts.push(`${headers[i]} : ${v}`);
        });
        return parts.join('\n');
      };

      // 1. Pré-création des sous-dossiers à partir des paths.
      const paths = new Set();
      (targetToCols.folder || []).forEach(i => {
        rows.forEach(row => {
          const v = (row[i] || '').trim();
          if (v) paths.add(v);
        });
      });
      const { folders: newFolders, pathToId } = ensureFoldersFromPaths(Array.from(paths), folders);

      // 2. Construit les entries.
      const entries = [];
      rows.forEach((row, i) => {
        const name = firstValue(row, 'name') || firstValue(row, 'domain') || ('Importé ' + (i + 1));
        const username = firstValue(row, 'username');
        const password = firstValue(row, 'password');
        const totpSecret = firstValue(row, 'totp');
        const notes = buildNotes(row);

        // Skip si tout est vide.
        if (!password && !username && !notes && !totpSecret) return;

        const domain = firstValue(row, 'domain').replace(/^https?:\/\//, '').replace(/\/.*$/, '');
        const csvPath = firstValue(row, 'folder');
        const folderId = (csvPath && pathToId.get(csvPath)) || defaultFolder || null;

        const accountId = CPCrypto.genId('a');
        const accounts = [{
          id: accountId, label: 'Principal',
          username, password,
          created: new Date().toLocaleDateString('fr-FR'),
        }];

        const totp = [];
        let extraNotes = '';
        if (totpSecret) {
          try {
            CPCrypto.base32Decode(totpSecret);
            totp.push({
              id: CPCrypto.genId('t'),
              label: 'Authenticator',
              secret: totpSecret.replace(/\s+/g, '').toUpperCase(),
              period: 30, code: '------', remaining: 30,
              accountId,
            });
          } catch {
            extraNotes = `\nTOTP non valide (${totpSecret})`;
          }
        }

        entries.push({
          id: CPCrypto.genId('e'),
          name, domain,
          category: 'imported',
          folderId,
          favorite: false, shared: false,
          modified: 'importé',
          color: CPCrypto.colorFor(name),
          avatarType: 'letter',
          iconName: null,
          strength: CPCrypto.passwordStrength(password).level,
          accounts, totp,
          notes: notes + extraNotes,
        });
      });

      return { entries, folders: newFolders };
    };

    const submit = async () => {
      setError('');
      const payload = buildPayload();
      if (payload.entries.length === 0) {
        setError('Aucune entrée à importer (vérifiez le mapping des colonnes).');
        return;
      }
      setBusy(true);
      setProgress({ done: 0, total: payload.entries.length });
      try {
        await onImport(payload);
        setProgress({ done: payload.entries.length, total: payload.entries.length });
      } catch (err) {
        setError(err.message || 'Échec de l’import');
        setBusy(false);
      }
    };

    const goBack = () => {
      if (step === 'mapping') {
        setStep('file');
        setRows([]); setHeaders([]); setReverseMap({}); setFileName(''); setRawText('');
      } else if (step === 'file') {
        setStep('source');
      }
    };

    const folderOptions = useMemo(() => {
      const out = [{ value: '', label: '— Selon le CSV ou aucun —' }];
      folders.forEach(f => {
        out.push({ value: f.id, label: f.name });
        (f.children || []).forEach(c => out.push({ value: c.id, label: '  ' + f.name + ' › ' + c.name }));
      });
      return out;
    }, [folders]);

    // Validation : il faut au moins une colonne mappée vers 'name'.
    const valid = rows.length > 0 &&
      Object.values(reverseMap).some(t => t === 'name');

    // Compte des entrées qui passeraient le filtre.
    const importableCount = useMemo(() => {
      if (rows.length === 0) return 0;
      return buildPayload().entries.length;
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [rows, reverseMap, defaultFolder]);

    // ─── Rendu ──────────────────────────────────────────────────────────────

    const renderSourceStep = () => (
      <>
        <div className="cp-modal-body cp-form">
          <p className="cp-import-step-intro">
            Choisissez le service d'origine. CléPass adaptera automatiquement
            les correspondances de colonnes.
          </p>
          <div className="cp-import-source-grid">
            {IMPORT_SOURCES.map(s => (
              <button
                key={s.id}
                type="button"
                className={`cp-import-source ${source === s.id ? 'is-active' : ''}`}
                onClick={() => setSource(s.id)}
              >
                <div className="cp-import-source-label">{s.label}</div>
                <div className="cp-import-source-desc">{s.hint}</div>
              </button>
            ))}
          </div>
        </div>
        <div className="cp-modal-foot">
          <button type="button" className="cp-btn" onClick={onCancel}>Annuler</button>
          <button type="button" className="cp-btn cp-btn-soft"
                  onClick={() => setStep('file')}
                  disabled={!source}>
            Suivant
          </button>
        </div>
      </>
    );

    const renderFileStep = () => (
      <>
        <div className="cp-modal-body cp-form">
          <div className="cp-import-step-tag">
            Source : <b>{IMPORT_SOURCES.find(s => s.id === source)?.label}</b>
            <button type="button" className="cp-auth-link" onClick={goBack}>Changer</button>
          </div>
          <div
            className="cp-import-drop"
            onDragOver={(e) => e.preventDefault()}
            onDrop={onDrop}
            onClick={() => fileInputRef.current?.click()}
          >
            <Icon name="upload" size={28} />
            <div className="cp-import-drop-t">Déposer un CSV ici, ou cliquer pour choisir</div>
            <div className="cp-import-drop-s">.csv · .txt — séparateur , ; ou tabulation</div>
            <input
              ref={fileInputRef}
              type="file"
              accept=".csv,.txt,text/csv,text/plain"
              style={{ display: 'none' }}
              onChange={(e) => handleFile(e.target.files?.[0])}
            />
          </div>
          {error && <div className="cp-form-error"><Icon name="alert" size={12} /><span>{error}</span></div>}
        </div>
        <div className="cp-modal-foot">
          <button type="button" className="cp-btn" onClick={goBack}>Précédent</button>
          <button type="button" className="cp-btn" onClick={onCancel}>Annuler</button>
        </div>
      </>
    );

    const renderMappingStep = () => (
      <>
        <div className="cp-modal-body cp-form">
          <div className="cp-import-step-tag">
            Source : <b>{IMPORT_SOURCES.find(s => s.id === source)?.label}</b>
            <span> · </span>
            <b>{fileName}</b>
            <span> · </span>
            <span>{rows.length} ligne{rows.length > 1 ? 's' : ''}</span>
            <button type="button" className="cp-auth-link" onClick={goBack}>Changer le fichier</button>
          </div>

          <div className="cp-form-section">
            <div className="cp-section-head">
              <h3>Mapping des colonnes du CSV</h3>
              <span className="cp-section-count">{headers.length} colonne{headers.length > 1 ? 's' : ''}</span>
            </div>
            <div className="cp-import-mapping">
              {headers.map((h, i) => {
                const sample = rows[0]?.[i] || '';
                return (
                  <div key={i} className="cp-import-mapping-row">
                    <div className="cp-import-mapping-from">
                      <div className="cp-import-mapping-header">{h || `Colonne ${i + 1}`}</div>
                      <div className="cp-import-mapping-sample" title={sample}>
                        {sample || <span style={{ opacity: .5 }}>(exemple vide)</span>}
                      </div>
                    </div>
                    <div className="cp-import-mapping-arrow">→</div>
                    <select
                      className="cp-form-input cp-import-mapping-to"
                      value={reverseMap[i] || ''}
                      onChange={(e) => updateRev(i, e.target.value)}
                    >
                      {TARGETS.map(t => (
                        <option key={t.value} value={t.value}>{t.label}</option>
                      ))}
                    </select>
                  </div>
                );
              })}
            </div>
          </div>

          <div className="cp-form-section">
            <div className="cp-section-head"><h3>Options</h3></div>
            <label className="cp-form-field">
              <span>Dossier par défaut (si non précisé dans le CSV)</span>
              <select
                className="cp-form-input"
                value={defaultFolder}
                onChange={(e) => setDefaultFolder(e.target.value)}
              >
                {folderOptions.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
              </select>
            </label>
            <div className="cp-form-toggle">
              <span>La 1ʳᵉ ligne est l'en-tête</span>
              <button type="button"
                className="twk-toggle"
                data-on={hasHeader ? '1' : '0'}
                onClick={() => {
                  const next = !hasHeader;
                  setHasHeader(next);
                  if (rawText) parseAndDetect(rawText, next);
                }}
              ><i /></button>
            </div>
          </div>

          {error && <div className="cp-form-error"><Icon name="alert" size={12} /><span>{error}</span></div>}

          {busy && progress.total > 0 && (
            <div className="cp-import-progress">
              <div className="cp-form-strength-bar">
                <div className="cp-form-strength-fill"
                     style={{ width: `${(progress.done / progress.total) * 100}%`, background: '#10B981' }} />
              </div>
              <span>{progress.done} / {progress.total} entrées chiffrées et envoyées</span>
            </div>
          )}
        </div>
        <div className="cp-modal-foot">
          <button type="button" className="cp-btn" onClick={goBack} disabled={busy}>Précédent</button>
          <button type="button" className="cp-btn" onClick={onCancel} disabled={busy}>Annuler</button>
          <button type="button" className="cp-btn cp-btn-soft"
                  onClick={submit}
                  disabled={!valid || busy}>
            {busy ? `Import… (${progress.done}/${progress.total})`
                  : valid ? `Importer ${importableCount} entrée${importableCount > 1 ? 's' : ''}`
                          : 'Mapper au moins une colonne vers « Nom »'}
          </button>
        </div>
      </>
    );

    return (
      <div className="cp-modal-bg" onClick={busy ? undefined : onCancel}>
        <div className="cp-modal cp-modal-form" onClick={(e) => e.stopPropagation()}
             style={{ width: 'min(720px, calc(100vw - 48px))' }}>
          <button className="cp-modal-x" onClick={onCancel} aria-label="Fermer">✕</button>
          <div className="cp-modal-head">
            <div>
              <h2>Importer des mots de passe</h2>
              <p>
                {step === 'source'  && 'Étape 1 / 3 — Choisir la source des données.'}
                {step === 'file'    && 'Étape 2 / 3 — Déposer le fichier CSV.'}
                {step === 'mapping' && 'Étape 3 / 3 — Vérifier la correspondance des colonnes.'}
              </p>
            </div>
          </div>
          {step === 'source'  && renderSourceStep()}
          {step === 'file'    && renderFileStep()}
          {step === 'mapping' && renderMappingStep()}
        </div>
      </div>
    );
  };

  global.ImportModal = ImportModal;
})(window);
