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

// ============================================
// API HELPER
// ============================================
const api = {
  async request(endpoint, options = {}) {
    const token = localStorage.getItem('token');
    const headers = {
      'Content-Type': 'application/json',
      ...(token && { Authorization: `Bearer ${token}` })
    };
    try {
      const response = await fetch(endpoint, { ...options, headers });
      if (response.status === 401 || response.status === 403) {
        if (!endpoint.includes('/login') && !endpoint.includes('/register')) {
          localStorage.removeItem('token');
          localStorage.removeItem('user');
          window.location.reload();
          return;
        }
      }
      const data = await response.json();
      if (!response.ok) throw new Error(data.error || 'Erreur serveur');
      return data;
    } catch (err) {
      throw err;
    }
  },
  // Auth
  register: (data) => api.request('/api/register', { method: 'POST', body: JSON.stringify(data) }),
  login: (data) => api.request('/api/login', { method: 'POST', body: JSON.stringify(data) }),
  getProfile: () => api.request('/api/user/profile'),
  updateProfile: (data) => api.request('/api/user/profile', { method: 'PUT', body: JSON.stringify(data) }),
  deleteAccount: (data) => api.request('/api/user/account', { method: 'DELETE', body: JSON.stringify(data) }),
  // Categories
  getCategories: () => api.request('/api/charge-categories'),
  createCategory: (data) => api.request('/api/charge-categories', { method: 'POST', body: JSON.stringify(data) }),
  updateCategory: (id, data) => api.request(`/api/charge-categories/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
  deleteCategory: (id) => api.request(`/api/charge-categories/${id}`, { method: 'DELETE' }),
  // Charges
  getCharges: () => api.request('/api/charges'),
  createCharge: (data) => api.request('/api/charges', { method: 'POST', body: JSON.stringify(data) }),
  updateCharge: (id, data) => api.request(`/api/charges/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
  deleteCharge: (id) => api.request(`/api/charges/${id}`, { method: 'DELETE' }),
  // Salaries
  getSalary: (month) => api.request(`/api/salaries?month=${month}`),
  getSalaries: () => api.request('/api/salaries'),
  setSalary: (data) => api.request('/api/salaries', { method: 'POST', body: JSON.stringify(data) }),
  // Additional Incomes
  getAdditionalIncomes: (month) => api.request(`/api/additional-incomes?month=${month}`),
  createAdditionalIncome: (data) => api.request('/api/additional-incomes', { method: 'POST', body: JSON.stringify(data) }),
  updateAdditionalIncome: (id, data) => api.request(`/api/additional-incomes/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
  deleteAdditionalIncome: (id) => api.request(`/api/additional-incomes/${id}`, { method: 'DELETE' }),
  // Expenses
  getExpenses: (month) => api.request(`/api/expenses?month=${month}`),
  createExpense: (data) => api.request('/api/expenses', { method: 'POST', body: JSON.stringify(data) }),
  updateExpense: (id, data) => api.request(`/api/expenses/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
  deleteExpense: (id) => api.request(`/api/expenses/${id}`, { method: 'DELETE' }),
  // Dashboard
  getDashboard: (month) => api.request(`/api/dashboard?month=${month}`),
  // Calendar
  getCalendar: (month) => api.request(`/api/calendar?month=${month}`),
  // Bank Accounts
  getBankAccounts: () => api.request('/api/bank-accounts'),
  getBankAccountCharges: (id) => api.request(`/api/bank-accounts/${id}/charges`),
  createBankAccount: (data) => api.request('/api/bank-accounts', { method: 'POST', body: JSON.stringify(data) }),
  updateBankAccount: (id, data) => api.request(`/api/bank-accounts/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
  deleteBankAccount: (id) => api.request(`/api/bank-accounts/${id}`, { method: 'DELETE' }),
  updateBankAccountCharges: (id, data) => api.request(`/api/bank-accounts/${id}/charges`, { method: 'PUT', body: JSON.stringify(data) }),
  reorderBankAccounts: (order) => api.request('/api/bank-accounts/reorder', { method: 'PUT', body: JSON.stringify({ order }) })
};

// ============================================
// UTILS
// ============================================
// L'espace fine insécable (U+202F) d'Intl n'existe pas dans la police : espace insécable classique
const formatCurrency = (amount) => {
  return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(amount).replace(/\u202f/g, '\u00a0');
};

const getMonthLabel = (monthStr) => {
  const [year, month] = monthStr.split('-');
  const date = new Date(year, parseInt(month) - 1);
  return date.toLocaleDateString('fr-FR', { month: 'long', year: 'numeric' });
};

const getCurrentMonth = () => {
  const now = new Date();
  return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
};

const changeMonth = (monthStr, delta) => {
  const [year, month] = monthStr.split('-').map(Number);
  const d = new Date(year, month - 1 + delta);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
};

// Date à affecter à une dépense saisie depuis le tableau de bord :
// aujourd'hui si le mois affiché est le mois courant, sinon le 1er du mois affiché.
const dateForMonth = (monthStr) => {
  const now = new Date();
  const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
  return today.startsWith(monthStr + '-') ? today : `${monthStr}-01`;
};

const formatDateShort = (dateStr) => {
  const d = new Date(dateStr + 'T00:00:00');
  return d.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' });
};

// ============================================
// CAPTCHA HELPER
// ============================================
const generateCaptcha = () => {
  const a = Math.floor(Math.random() * 10) + 1;
  const b = Math.floor(Math.random() * 10) + 1;
  return { a, b, answer: a + b };
};

// ============================================
// SWIPE HOOK
// ============================================
function useSwipe(onSwipeLeft, onSwipeRight) {
  const touchStart = useRef(null);
  const touchEnd = useRef(null);
  const minSwipeDistance = 50;

  const onTouchStart = (e) => {
    touchEnd.current = null;
    touchStart.current = e.targetTouches[0].clientX;
  };

  const onTouchMove = (e) => {
    touchEnd.current = e.targetTouches[0].clientX;
  };

  const onTouchEnd = () => {
    if (!touchStart.current || !touchEnd.current) return;
    const distance = touchStart.current - touchEnd.current;
    if (Math.abs(distance) >= minSwipeDistance) {
      if (distance > 0) onSwipeLeft();
      else onSwipeRight();
    }
    touchStart.current = null;
    touchEnd.current = null;
  };

  return { onTouchStart, onTouchMove, onTouchEnd };
}

// ============================================
// LOGIN PAGE (with captcha after 3 fails)
// ============================================
function LoginPage({ onLogin, onSwitch }) {
  const [login, setLogin] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  const [failedAttempts, setFailedAttempts] = useState(0);
  const [captcha, setCaptcha] = useState(generateCaptcha());
  const [captchaInput, setCaptchaInput] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    if (failedAttempts >= 3) {
      if (parseInt(captchaInput) !== captcha.answer) {
        setError('Captcha incorrect');
        setCaptcha(generateCaptcha());
        setCaptchaInput('');
        return;
      }
    }
    setLoading(true);
    try {
      const data = await api.login({ login, password });
      onLogin(data);
    } catch (err) {
      const newAttempts = failedAttempts + 1;
      setFailedAttempts(newAttempts);
      setError(err.message);
      if (newAttempts >= 3) {
        setCaptcha(generateCaptcha());
        setCaptchaInput('');
      }
    }
    setLoading(false);
  };

  return (
    <div className="auth-container">
      <div className="auth-card">
        <div className="logo">💰</div>
        <h1>MyWallet</h1>
        <p className="subtitle">Connectez-vous pour gérer votre budget</p>
        {error && <div className="error-msg">{error}</div>}
        <form onSubmit={handleSubmit}>
          <div className="form-group">
            <label>Identifiant ou email</label>
            <input className="form-input" type="text" value={login} onChange={e => setLogin(e.target.value)} placeholder="Votre identifiant" required />
          </div>
          <div className="form-group">
            <label>Mot de passe</label>
            <input className="form-input" type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder="Votre mot de passe" required />
          </div>
          {failedAttempts >= 3 && (
            <div className="captcha-box">
              <span className="captcha-question">{captcha.a} + {captcha.b} =</span>
              <input className="form-input" type="number" value={captchaInput} onChange={e => setCaptchaInput(e.target.value)} placeholder="?" required />
            </div>
          )}
          <button className="btn btn-primary" type="submit" disabled={loading}>
            {loading ? 'Connexion...' : 'Se connecter'}
          </button>
        </form>
      </div>
    </div>
  );
}

// ============================================
// REGISTER PAGE (with captcha)
// ============================================
function RegisterPage({ onLogin, onSwitch }) {
  const [username, setUsername] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  const [captcha, setCaptcha] = useState(generateCaptcha());
  const [captchaInput, setCaptchaInput] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    if (parseInt(captchaInput) !== captcha.answer) {
      setError('Captcha incorrect');
      setCaptcha(generateCaptcha());
      setCaptchaInput('');
      return;
    }
    setLoading(true);
    try {
      const data = await api.register({ username, email, password });
      onLogin(data);
    } catch (err) {
      setError(err.message);
      setCaptcha(generateCaptcha());
      setCaptchaInput('');
    }
    setLoading(false);
  };

  return (
    <div className="auth-container">
      <div className="auth-card">
        <div className="logo">💰</div>
        <h1>MyWallet</h1>
        <p className="subtitle">Créez votre compte pour commencer</p>
        {error && <div className="error-msg">{error}</div>}
        <form onSubmit={handleSubmit}>
          <div className="form-group">
            <label>Nom d'utilisateur</label>
            <input className="form-input" type="text" value={username} onChange={e => setUsername(e.target.value)} placeholder="Choisissez un identifiant" required />
          </div>
          <div className="form-group">
            <label>Email</label>
            <input className="form-input" type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="votre@email.com" required />
          </div>
          <div className="form-group">
            <label>Mot de passe</label>
            <input className="form-input" type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder="Minimum 6 caractères" required minLength={6} />
          </div>
          <div className="captcha-box">
            <span className="captcha-question">{captcha.a} + {captcha.b} =</span>
            <input className="form-input" type="number" value={captchaInput} onChange={e => setCaptchaInput(e.target.value)} placeholder="?" required />
          </div>
          <button className="btn btn-primary" type="submit" disabled={loading}>
            {loading ? 'Création...' : 'Créer mon compte'}
          </button>
        </form>
        <div className="auth-link">
          Déjà un compte ? <a onClick={onSwitch}>Se connecter</a>
        </div>
      </div>
    </div>
  );
}

// ============================================
// MODAL COMPONENT
// ============================================
function Modal({ title, onClose, children }) {
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <div className="modal-header">
          <span className="modal-title">{title}</span>
          <button className="modal-close" onClick={onClose}>&times;</button>
        </div>
        {children}
      </div>
    </div>
  );
}

// ============================================
// ICÔNES (dessinées, remplacent les emojis de l'interface)
// ============================================
const ICONS = {
  budget: <path d="M4 19V9M10 19V5M16 19v-7M22 19H2" />,
  charges: <><path d="M5 4h14v16l-3-2-2 2-2-2-2 2-2-2-3 2z" /><path d="M9 9h6M9 13h6" /></>,
  accounts: <path d="M3 10l9-6 9 6M5 10v8M19 10v8M9.5 10v8M14.5 10v8M3 20h18" />,
  calendar: <><rect x="3" y="5" width="18" height="16" rx="3" /><path d="M3 10h18M8 3v4M16 3v4" /></>,
  plus: <path d="M12 5v14M5 12h14" />,
  edit: <path d="M4 20h4L19 9l-4-4L4 16v4zM13.5 6.5l4 4" />,
  trash: <path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3" />,
  eye: <><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z" /><circle cx="12" cy="12" r="3" /></>,
  eyeOff: <><path d="M3 3l18 18M10.6 5.1A10 10 0 0 1 12 5c6.5 0 10 7 10 7a17 17 0 0 1-3.2 4.2M6.6 6.6A17 17 0 0 0 2 12s3.5 7 10 7a9.7 9.7 0 0 0 5.4-1.6" /><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2" /></>,
  chevronLeft: <path d="M15 6l-6 6 6 6" />,
  chevronRight: <path d="M9 6l6 6-6 6" />,
  chevronDown: <path d="M6 9l6 6 6-6" />,
  backspace: <><path d="M21 5H9l-6 7 6 7h12z" /><path d="M12 9l6 6M18 9l-6 6" /></>,
  bilan: <><path d="M3 20h18" /><path d="M6 16v-4M10 16V8M14 16v-6M18 16V5" /></>,
};

function Icon({ name, size = 20 }) {
  return (
    <svg className="ico" width={size} height={size} viewBox="0 0 24 24" aria-hidden="true">
      {ICONS[name]}
    </svg>
  );
}

// Montant en deux parties pour le grand chiffre : « 741 » et « ,60 € »
const splitAmount = (amount) => {
  const parts = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).formatToParts(amount);
  const join = (list) => list.map(p => p.value).join('').replace(/\u202f/g, '\u00a0');
  const head = join(parts.filter(p => ['minusSign', 'integer', 'group'].includes(p.type)));
  const tail = join(parts.filter(p => !['minusSign', 'integer', 'group'].includes(p.type)));
  return [head, tail];
};

// Euros sans centimes, pour les ordres de grandeur (« 41 € par jour »)
const formatEuros = (amount) => new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }).format(amount).replace(/\u202f/g, '\u00a0').replace('-', '−');

const todayISO = () => {
  const now = new Date();
  return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
};

const shiftDate = (dateStr, days) => {
  const d = new Date(dateStr + 'T00:00:00');
  d.setDate(d.getDate() + days);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
};

// Titre d'un groupe de dépenses : « Aujourd'hui », « Hier », sinon « ven. 20 oct. »
const dayLabel = (dateStr) => {
  const today = todayISO();
  if (dateStr === today) return "Aujourd'hui";
  if (dateStr === shiftDate(today, -1)) return 'Hier';
  return new Date(dateStr + 'T00:00:00').toLocaleDateString('fr-FR', { weekday: 'short', day: 'numeric', month: 'short' });
};

// Teinte stable par nom de dépense : « Lidl » a toujours la même couleur, on reconnaît d'un coup d'œil
const GLYPH_TINTS = 6;
const glyphTint = (name) => {
  let h = 0;
  for (const c of name.trim().toLowerCase()) h = (h * 31 + c.charCodeAt(0)) >>> 0;
  return `tint-${h % GLYPH_TINTS}`;
};

// Saisie au pavé : « 12,5 » → 12.5 ; null si vide ou nul
const parseAmount = (str) => {
  const n = parseFloat(String(str).replace(',', '.'));
  return Number.isFinite(n) && n > 0 ? Math.round(n * 100) / 100 : null;
};

// ============================================
// FEUILLE (monte du bas : ajout rapide, modification)
// ============================================
// Fermeture au glisser : au-delà d'un quart de la hauteur, ou d'un geste vif d'au moins 40 px
const SHEET_CLOSE_RATIO = 0.25;
const SHEET_FLICK_SPEED = 0.4; // px/ms
const SHEET_FLICK_MIN = 40;

function Sheet({ label, onClose, children }) {
  const sheetRef = useRef(null);
  const onCloseRef = useRef(onClose);
  onCloseRef.current = onClose;

  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [onClose]);

  // Tirer la feuille vers le bas la ferme, comme sur iOS — seulement quand son contenu est en haut,
  // sinon le geste fait défiler la liste. Écouteurs natifs : React les pose en passif, sans preventDefault.
  useEffect(() => {
    const sheet = sheetRef.current;
    let startY = 0, startT = 0, dy = 0, mode = null; // mode : null (indécis), 'drag' ou 'scroll'
    const moveTo = (y, animate) => {
      sheet.style.transition = animate ? 'transform 0.25s var(--ease-out)' : 'none';
      sheet.style.transform = y ? `translateY(${y}px)` : '';
    };
    const onStart = (e) => { startY = e.touches[0].clientY; startT = Date.now(); dy = 0; mode = null; };
    const onMove = (e) => {
      const d = e.touches[0].clientY - startY;
      if (mode === null) {
        if (Math.abs(d) < 6) return;
        mode = d > 0 && sheet.scrollTop <= 0 ? 'drag' : 'scroll';
      }
      if (mode !== 'drag') return;
      e.preventDefault();
      dy = Math.max(0, d);
      moveTo(dy, false);
    };
    const onEnd = () => {
      if (mode !== 'drag') return;
      mode = null;
      const speed = dy / Math.max(1, Date.now() - startT);
      if (dy > sheet.offsetHeight * SHEET_CLOSE_RATIO || (dy > SHEET_FLICK_MIN && speed > SHEET_FLICK_SPEED)) {
        moveTo(sheet.offsetHeight, true);
        setTimeout(() => onCloseRef.current(), 250);
      } else {
        moveTo(0, true);
      }
    };
    sheet.addEventListener('touchstart', onStart, { passive: true });
    sheet.addEventListener('touchmove', onMove, { passive: false });
    sheet.addEventListener('touchend', onEnd);
    sheet.addEventListener('touchcancel', onEnd);
    return () => {
      sheet.removeEventListener('touchstart', onStart);
      sheet.removeEventListener('touchmove', onMove);
      sheet.removeEventListener('touchend', onEnd);
      sheet.removeEventListener('touchcancel', onEnd);
    };
  }, []);

  return (
    <div className="sheet-layer" onClick={onClose}>
      <section ref={sheetRef} className="sheet" role="dialog" aria-modal="true" aria-label={label} onClick={e => e.stopPropagation()}>
        <span className="sheet-handle" aria-hidden="true" />
        {children}
      </section>
    </div>
  );
}

// ============================================
// AJOUT RAPIDE D'UNE DÉPENSE
// ============================================
function QuickAddSheet({ month, onClose, onAdded }) {
  const isCurrentMonth = month === getCurrentMonth();
  const baseDate = dateForMonth(month);
  const [amount, setAmount] = useState('');
  const [name, setName] = useState('');
  const [when, setWhen] = useState('base');
  const [otherDate, setOtherDate] = useState(baseDate);
  const [planned, setPlanned] = useState(false);
  const [recent, setRecent] = useState([]);
  const [error, setError] = useState('');
  const [saving, setSaving] = useState(false);

  // Noms récents du mois : un appui les remet dans le champ
  useEffect(() => {
    api.getExpenses(month).then(list => {
      const names = [];
      (list || []).forEach(e => { if (!names.includes(e.name)) names.push(e.name); });
      setRecent(names.slice(0, 4));
    }).catch(() => {});
  }, [month]);

  const press = (key) => {
    setError('');
    setAmount(a => {
      if (key === 'back') return a.slice(0, -1);
      if (key === ',') return a.includes(',') ? a : (a || '0') + ',';
      const [, dec] = a.split(',');
      if (dec !== undefined && dec.length >= 2) return a;
      if (a === '0') return key;
      return a.length >= 8 ? a : a + key;
    });
  };

  const date = when === 'base' ? baseDate : when === 'yesterday' ? shiftDate(baseDate, -1) : otherDate;
  const value = parseAmount(amount);
  const canSave = value !== null && name.trim() !== '' && !!date && !saving;

  const submit = async () => {
    if (!canSave) return;
    setSaving(true);
    try {
      await api.createExpense({ name: name.trim(), amount: value, date, is_planned: planned });
      onAdded();
    } catch (err) {
      setError("La dépense n'a pas été enregistrée. Vérifie ta connexion et réessaie.");
      setSaving(false);
    }
  };

  return (
    <Sheet label="Nouvelle dépense" onClose={onClose}>
      <p className={`sheet-amount ${amount ? '' : 'is-empty'}`}>{amount || '0'} <span>€</span></p>
      <input
        className="sheet-name"
        placeholder="Quoi ? (Lidl, resto…)"
        value={name}
        onChange={e => { setName(e.target.value); setError(''); }}
        maxLength={100}
        aria-label="Nom de la dépense"
      />
      {recent.length > 0 && (
        <div className="chips">
          {recent.map(n => <button key={n} type="button" className="chip" onClick={() => setName(n)}>{n}</button>)}
        </div>
      )}
      <div className="segmented" role="group" aria-label="Date">
        <button type="button" className={when === 'base' ? 'is-on' : ''} onClick={() => setWhen('base')}>
          {isCurrentMonth ? "Aujourd'hui" : `Le ${formatDateShort(baseDate)}`}
        </button>
        {isCurrentMonth && (
          <button type="button" className={when === 'yesterday' ? 'is-on' : ''} onClick={() => setWhen('yesterday')}>Hier</button>
        )}
        <button type="button" className={when === 'other' ? 'is-on' : ''} onClick={() => setWhen('other')}>Autre date</button>
      </div>
      {when === 'other' && (
        <input className="form-input sheet-date" type="date" value={otherDate} onChange={e => setOtherDate(e.target.value)} aria-label="Date de la dépense" />
      )}
      <label className="check-row">
        <input type="checkbox" checked={planned} onChange={e => setPlanned(e.target.checked)} />
        Dépense prévue (pas encore payée)
      </label>
      <div className="keypad">
        {['1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '0'].map(k => (
          <button key={k} type="button" className="key" onClick={() => press(k)}>{k}</button>
        ))}
        <button type="button" className="key" onClick={() => press('back')} aria-label="Effacer"><Icon name="backspace" size={26} /></button>
      </div>
      {error && <p className="sheet-error">{error}</p>}
      <button type="button" className="btn btn-primary sheet-cta" disabled={!canSave} onClick={submit}>
        {value !== null ? `Ajouter ${formatCurrency(value)}` : 'Ajouter'}
      </button>
    </Sheet>
  );
}

// ============================================
// CHOIX DU MOIS (toucher le nom du mois en haut du Budget)
// ============================================
function MonthPickerSheet({ value, onPick, onClose }) {
  const [year, setYear] = useState(Number(value.split('-')[0]));
  const current = getCurrentMonth();
  const names = Array.from({ length: 12 }, (_, i) => {
    const n = new Date(2000, i, 1).toLocaleDateString('fr-FR', { month: 'long' });
    return n.charAt(0).toUpperCase() + n.slice(1);
  });
  return (
    <Sheet label="Choisir un mois" onClose={onClose}>
      <div className="year-switch">
        <button className="icon-btn" onClick={() => setYear(y => y - 1)} aria-label="Année précédente"><Icon name="chevronLeft" /></button>
        <span className="year-name">{year}</span>
        <button className="icon-btn" onClick={() => setYear(y => y + 1)} aria-label="Année suivante"><Icon name="chevronRight" /></button>
      </div>
      <div className="month-grid">
        {names.map((name, i) => {
          const m = `${year}-${String(i + 1).padStart(2, '0')}`;
          const cls = ['month-cell', m === value ? 'is-selected' : '', m === current ? 'is-today' : ''].join(' ').trim();
          return (
            <button key={m} type="button" className={cls} onClick={() => onPick(m)} aria-current={m === value ? 'date' : undefined}>
              {name}
            </button>
          );
        })}
      </div>
      <button type="button" className="btn btn-ghost month-today" onClick={() => onPick(current)}>Revenir au mois en cours</button>
    </Sheet>
  );
}

// ============================================
// MODIFIER / SUPPRIMER UNE DÉPENSE
// ============================================
function ExpenseSheet({ expense, onClose, onSaved }) {
  const [name, setName] = useState(expense.name);
  const [amount, setAmount] = useState(String(expense.amount).replace('.', ','));
  const [date, setDate] = useState(expense.date);
  const [planned, setPlanned] = useState(!!expense.is_planned);
  const [error, setError] = useState('');

  const save = async (e) => {
    e.preventDefault();
    const value = parseAmount(amount);
    if (!name.trim() || value === null || !date) {
      setError('Renseigne un nom, un montant supérieur à 0 et une date.');
      return;
    }
    try {
      await api.updateExpense(expense.id, { name: name.trim(), amount: value, date, is_planned: planned });
      onSaved();
    } catch (err) {
      setError("La modification n'a pas été enregistrée. Réessaie.");
    }
  };

  const remove = async () => {
    try {
      await api.deleteExpense(expense.id);
      onSaved();
    } catch (err) {
      setError("La dépense n'a pas été supprimée. Réessaie.");
    }
  };

  return (
    <Sheet label="Modifier la dépense" onClose={onClose}>
      <p className="sheet-title">Modifier la dépense</p>
      <form className="sheet-fields" onSubmit={save}>
        <div className="form-group">
          <label htmlFor="exp-name">Nom</label>
          <input id="exp-name" className="form-input" value={name} onChange={e => setName(e.target.value)} maxLength={100} />
        </div>
        <div className="form-group">
          <label htmlFor="exp-amount">Montant (€)</label>
          <input id="exp-amount" className="form-input" inputMode="decimal" value={amount} onChange={e => setAmount(e.target.value)} />
        </div>
        <div className="form-group">
          <label htmlFor="exp-date">Date</label>
          <input id="exp-date" className="form-input" type="date" value={date} onChange={e => setDate(e.target.value)} />
        </div>
        <label className="check-row">
          <input type="checkbox" checked={planned} onChange={e => setPlanned(e.target.checked)} />
          Dépense prévue (pas encore payée)
        </label>
        {error && <p className="sheet-error">{error}</p>}
        <div className="sheet-actions">
          <button type="button" className="btn btn-outline-danger" onClick={remove}><Icon name="trash" size={18} />Supprimer</button>
          <button type="submit" className="btn btn-primary">Enregistrer</button>
        </div>
      </form>
    </Sheet>
  );
}

// ============================================
// TABLEAU DE BORD — direction « Rythme »
// ============================================
// Dépenses groupées par jour, la plus récente d'abord (l'API du tableau de bord ne trie pas)
const groupByDay = (expenses) => {
  const groups = [];
  [...expenses].sort((a, b) => (b.date > a.date ? 1 : b.date < a.date ? -1 : b.id - a.id)).forEach(exp => {
    const last = groups[groups.length - 1];
    if (last && last.date === exp.date) last.items.push(exp);
    else groups.push({ date: exp.date, items: [exp] });
  });
  return groups;
};

// Contenu d'une ligne de dépense (Budget et récap du Bilan)
function ExpenseLine({ exp }) {
  return (
    <>
      <span className={`glyph ${exp.is_planned ? 'is-planned' : glyphTint(exp.name)}`} aria-hidden="true">{(exp.name.trim()[0] || '?').toUpperCase()}</span>
      <span className="row-main">
        <span className="row-name">{exp.name}</span>
        {exp.is_planned ? <span className="row-meta">Prévue</span> : null}
      </span>
      <span className={`row-amount ${exp.is_planned ? 'is-planned' : ''}`}>−{formatCurrency(exp.amount)}</span>
    </>
  );
}

function DashboardPage({ month, setMonth }) {
  const [dashboard, setDashboard] = useState(null);
  const [salaryInput, setSalaryInput] = useState('');
  const [editingSalary, setEditingSalary] = useState(false);
  const [salaryHidden, setSalaryHidden] = useState(() => localStorage.getItem('salaryHidden') === 'true');
  const [showDetail, setShowDetail] = useState(false);
  const [editExpense, setEditExpense] = useState(null);
  const [loading, setLoading] = useState(true);
  const [showIncomeModal, setShowIncomeModal] = useState(false);
  const [incomeName, setIncomeName] = useState('');
  const [incomeAmount, setIncomeAmount] = useState('');
  const [editingIncome, setEditingIncome] = useState(null);

  const swipeHandlers = useSwipe(
    () => setMonth(m => changeMonth(m, 1)),
    () => setMonth(m => changeMonth(m, -1))
  );

  const loadDashboard = useCallback(async () => {
    setLoading(true);
    try {
      const data = await api.getDashboard(month);
      setDashboard(data);
      setSalaryInput(data.salary > 0 ? String(data.salary) : '');
    } catch (err) {
      console.error(err);
    }
    setLoading(false);
  }, [month]);

  useEffect(() => { loadDashboard(); }, [loadDashboard]);

  const handleSaveSalary = async () => {
    if (!salaryInput) return;
    try {
      await api.setSalary({ amount: parseFloat(salaryInput), month });
      setEditingSalary(false);
      loadDashboard();
    } catch (err) {
      console.error(err);
    }
  };

  const toggleSalaryVisibility = () => {
    const newValue = !salaryHidden;
    setSalaryHidden(newValue);
    localStorage.setItem('salaryHidden', String(newValue));
  };

  const openIncomeModal = () => {
    setIncomeName('');
    setIncomeAmount('');
    setEditingIncome(null);
    setShowIncomeModal(true);
  };

  const openEditIncomeModal = (income) => {
    setIncomeName(income.name);
    setIncomeAmount(String(income.amount));
    setEditingIncome(income);
    setShowIncomeModal(true);
  };

  const handleSaveIncome = async (e) => {
    e.preventDefault();
    if (!incomeName || !incomeAmount) return;
    try {
      if (editingIncome) {
        await api.updateAdditionalIncome(editingIncome.id, { name: incomeName, amount: parseFloat(incomeAmount) });
      } else {
        await api.createAdditionalIncome({ name: incomeName, amount: parseFloat(incomeAmount), month });
      }
      setShowIncomeModal(false);
      setIncomeName('');
      setIncomeAmount('');
      setEditingIncome(null);
      loadDashboard();
    } catch (err) {
      console.error(err);
    }
  };

  const handleDeleteIncome = async (id) => {
    try {
      await api.deleteAdditionalIncome(id);
      loadDashboard();
    } catch (err) {
      console.error(err);
    }
  };

  if (loading && !dashboard) return <div className="empty-state"><p>Chargement…</p></div>;

  const salary = dashboard?.salary || 0;
  const additionalIncomes = dashboard?.additionalIncomes || [];
  const totalIncome = dashboard?.totalIncome || 0;
  const totalCharges = dashboard?.totalCharges || 0;
  const totalExpenses = dashboard?.totalExpenses || 0;
  const totalPlanned = dashboard?.totalPlanned || 0;
  const remaining = totalIncome > 0 ? (dashboard?.remaining || 0) : 0;
  const expenses = dashboard?.expenses || [];
  const hiddenText = '••••••';
  const money = (v) => (salaryHidden ? hiddenText : formatCurrency(v));

  // Rythme du mois : seulement pour le mois en cours
  const current = getCurrentMonth();
  const isCurrent = month === current;
  const [y, m] = month.split('-').map(Number);
  const daysInMonth = new Date(y, m, 0).getDate();
  const day = new Date().getDate();
  const daysLeft = daysInMonth - day + 1; // aujourd'hui compris
  const freeBudget = totalIncome - totalCharges; // ce qui reste après les charges fixes
  const showPace = isCurrent && freeBudget > 0;
  const spentPct = freeBudget > 0 ? Math.min(100, (totalExpenses / freeBudget) * 100) : 0;
  const timePct = (day / daysInMonth) * 100;
  const avgPerDay = totalExpenses / day;
  const projected = remaining - avgPerDay * (daysInMonth - day);
  const calm = spentPct <= timePct;
  const [head, tail] = splitAmount(remaining);
  const onTrack = remaining >= 0 && !(isCurrent && day >= 3 && totalExpenses > 0 && projected < 0);
  const heroTone = remaining < 0 ? 'is-negative' : onTrack ? 'is-ok' : 'is-warn';

  const heroLabel = !isCurrent
    ? (month < current ? 'Reste à la fin du mois' : 'Reste prévu')
    : (remaining < 0 ? 'Tu dépasses de' : 'Il te reste');

  const groups = groupByDay(expenses);

  return (
    <div {...swipeHandlers}>
      {salary === 0 && !editingSalary ? (
        <div className="salary-prompt">
          <p>Indique ton salaire de {getMonthLabel(month)} pour calculer ce qu'il te reste.</p>
          <div className="inline-form">
            <input className="form-input" type="number" step="0.01" inputMode="decimal" placeholder="Montant en €" value={salaryInput} onChange={e => setSalaryInput(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleSaveSalary()} />
            <button className="btn btn-primary btn-sm" onClick={handleSaveSalary}>Enregistrer</button>
          </div>
        </div>
      ) : null}

      <section className="hero">
        <p className="hero-label">{heroLabel}</p>
        <p className={`hero-amount ${heroTone}`}>
          {head.replace('-', '−')}<span className="hero-cents">{tail}</span>
        </p>
        {isCurrent && remaining > 0 && (
          <p className="hero-daily">soit <strong>{formatEuros(Math.floor(remaining / daysLeft))} par jour</strong> jusqu'au {daysInMonth}</p>
        )}

        {showPace && (
          <div className="pace" aria-label="Rythme du mois">
            <div className="pace-track">
              <div className={`pace-spent ${calm ? 'is-calm' : ''}`} style={{ width: `${spentPct}%` }} />
              <div className="pace-today" style={{ left: `${timePct}%` }}>
                <span>{new Date().toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })}</span>
              </div>
            </div>
            <div className="pace-legend">
              <span><i className={`dot dot-spent ${calm ? 'is-calm' : ''}`} />{Math.round(spentPct)} % du budget libre dépensé</span>
              <span><i className="dot dot-time" />{Math.round(timePct)} % du mois écoulé</span>
            </div>
            {day >= 3 && totalExpenses > 0 && (
              <p className={`pace-verdict ${projected < 0 ? 'is-warn' : 'is-ok'}`}>
                Depuis le 1er, tu dépenses {formatEuros(avgPerDay)} par jour.{' '}
                {projected < 0
                  ? `À ce rythme, tu finis le mois à ${formatEuros(projected)}.`
                  : `À ce rythme, il te restera ${formatEuros(projected)} le ${daysInMonth}.`}
              </p>
            )}
          </div>
        )}
      </section>

      <button type="button" className={`split ${showDetail ? 'is-open' : ''}`} onClick={() => setShowDetail(v => !v)} aria-expanded={showDetail}>
        <span className="split-item">
          <span className="split-value is-income">{money(totalIncome)}</span>
          <span className="split-label">Revenus</span>
        </span>
        <span className="split-item">
          <span className="split-value">{formatCurrency(totalCharges)}</span>
          <span className="split-label">Charges</span>
        </span>
        <span className="split-item">
          <span className="split-value is-spend">{formatCurrency(totalExpenses + totalPlanned)}</span>
          <span className="split-label">Dépenses <Icon name="chevronDown" size={14} /></span>
        </span>
      </button>

      {showDetail && (
        <section className="detail" aria-label="Détail du mois">
          <div className="detail-row">
            <span className="detail-name">Salaire</span>
            {editingSalary ? (
              <div className="inline-form">
                <input className="form-input" type="number" step="0.01" inputMode="decimal" value={salaryInput} onChange={e => setSalaryInput(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleSaveSalary()} autoFocus aria-label="Salaire du mois" />
                <button className="btn btn-primary btn-sm" onClick={handleSaveSalary}>OK</button>
              </div>
            ) : (
              <>
                <span className="detail-amount">{money(salary)}</span>
                <div className="detail-actions">
                  <button className="btn-icon" onClick={toggleSalaryVisibility} title={salaryHidden ? 'Afficher les revenus' : 'Masquer les revenus'} aria-label={salaryHidden ? 'Afficher les revenus' : 'Masquer les revenus'}>
                    <Icon name={salaryHidden ? 'eye' : 'eyeOff'} size={18} />
                  </button>
                  <button className="btn-icon" onClick={() => setEditingSalary(true)} title="Modifier le salaire" aria-label="Modifier le salaire"><Icon name="edit" size={18} /></button>
                </div>
              </>
            )}
          </div>

          {additionalIncomes.map(income => (
            <div key={income.id} className="detail-row">
              <span className="detail-name">{income.name}</span>
              <span className="detail-amount is-plus">{salaryHidden ? hiddenText : `+${formatCurrency(income.amount)}`}</span>
              <div className="detail-actions">
                <button className="btn-icon" onClick={() => openEditIncomeModal(income)} title="Modifier" aria-label={`Modifier ${income.name}`}><Icon name="edit" size={18} /></button>
                <button className="btn-icon" onClick={() => handleDeleteIncome(income.id)} title="Supprimer" aria-label={`Supprimer ${income.name}`}><Icon name="trash" size={18} /></button>
              </div>
            </div>
          ))}
          <button className="btn btn-ghost btn-sm detail-add" onClick={openIncomeModal}><Icon name="plus" size={16} />Ajouter un revenu complémentaire</button>

          {additionalIncomes.length > 0 && (
            <div className="detail-row">
              <span className="detail-name detail-total">Total des revenus</span>
              <span className="detail-amount">{money(totalIncome)}</span>
            </div>
          )}
          {dashboard?.categories?.filter(c => c.deducted_from_salary && c.total > 0).map(cat => (
            <div key={cat.id} className="detail-row">
              <span className="detail-name detail-sub">{cat.icon} {cat.name}</span>
              <span className="detail-amount is-minus">−{formatCurrency(cat.total)}</span>
            </div>
          ))}
          {totalExpenses > 0 && (
            <div className="detail-row">
              <span className="detail-name detail-sub">Dépenses du mois</span>
              <span className="detail-amount is-minus">−{formatCurrency(totalExpenses)}</span>
            </div>
          )}
          {totalPlanned > 0 && (
            <div className="detail-row">
              <span className="detail-name detail-sub">Dépenses prévues</span>
              <span className="detail-amount is-minus">−{formatCurrency(totalPlanned)}</span>
            </div>
          )}
          <div className="detail-row">
            <span className="detail-name detail-total">Reste disponible</span>
            <span className="detail-amount">{formatCurrency(remaining)}</span>
          </div>
        </section>
      )}

      {showIncomeModal && (
        <Modal
          title={editingIncome ? 'Modifier le revenu complémentaire' : 'Ajouter un revenu complémentaire'}
          onClose={() => { setShowIncomeModal(false); setEditingIncome(null); }}
        >
          <form onSubmit={handleSaveIncome}>
            <div className="form-group">
              <label>Nom du revenu</label>
              <input className="form-input" type="text" value={incomeName} onChange={e => setIncomeName(e.target.value)} placeholder="Ex : prime, freelance…" required autoFocus />
            </div>
            <div className="form-group">
              <label>Montant</label>
              <input className="form-input" type="number" step="0.01" value={incomeAmount} onChange={e => setIncomeAmount(e.target.value)} placeholder="0,00" required />
            </div>
            <button className="btn btn-primary" type="submit">{editingIncome ? 'Enregistrer' : 'Ajouter'}</button>
          </form>
        </Modal>
      )}

      <section className="feed">
        <div className="feed-head">
          <h2 className="feed-title">Dépenses</h2>
          <span className="feed-count">{expenses.length} ce mois-ci</span>
        </div>
        {groups.length === 0 ? (
          <div className="feed-empty">Aucune dépense ce mois-ci. Touche + pour noter la première.</div>
        ) : groups.map(group => (
          <div key={group.date}>
            <h3 className="feed-day">{dayLabel(group.date)}</h3>
            <ul className="rows">
              {group.items.map(exp => (
                <li key={exp.id}>
                  <button type="button" className="row" onClick={() => setEditExpense(exp)}>
                    <ExpenseLine exp={exp} />
                  </button>
                </li>
              ))}
            </ul>
          </div>
        ))}
      </section>

      {editExpense && (
        <ExpenseSheet
          expense={editExpense}
          onClose={() => setEditExpense(null)}
          onSaved={() => { setEditExpense(null); loadDashboard(); }}
        />
      )}
    </div>
  );
}

// ============================================
// BILAN — les mois passés côte à côte (remplace l'onglet Calendrier, 2026-10-02)
// ============================================
const BILAN_MAX_MONTHS = 12;

// Récap d'un mois passé, ouvert depuis le Bilan sans quitter la page (demande Noah 2026-10-03)
function MonthRecapSheet({ entry, avgRest, compare, salaryHidden, onClose, onEdit }) {
  const expenses = entry.data.expenses || [];
  const groups = groupByDay(expenses);
  const hasSalary = entry.income > 0;
  const diff = entry.rest - avgRest;
  const [head, tail] = splitAmount(entry.rest);
  return (
    <Sheet label={`Récap de ${getMonthLabel(entry.month)}`} onClose={onClose}>
      <h2 className="sheet-title recap-title">{getMonthLabel(entry.month)}</h2>
      {hasSalary ? (
        <div className="recap-head">
          <p className="hero-label">Reste à la fin du mois</p>
          <p className={`hero-amount recap-amount ${entry.rest < 0 ? 'is-negative' : 'is-ok'}`}>
            {head.replace('-', '−')}<span className="hero-cents">{tail}</span>
          </p>
          {compare && (
            <p className={`trend ${diff >= 0 ? 'is-ok' : 'is-warn'}`}>
              {formatEuros(Math.abs(diff))} {diff >= 0 ? 'de mieux' : 'de moins'} que ta moyenne
            </p>
          )}
        </div>
      ) : (
        <p className="recap-missing">Salaire non saisi : ce mois ne compte pas dans tes moyennes.</p>
      )}

      <section className="split split-static">
        <span className="split-item">
          <span className="split-value is-income">{salaryHidden ? '••••••' : formatCurrency(entry.income)}</span>
          <span className="split-label">Revenus</span>
        </span>
        <span className="split-item">
          <span className="split-value">{formatCurrency(entry.charges)}</span>
          <span className="split-label">Charges</span>
        </span>
        <span className="split-item">
          <span className="split-value is-spend">{formatCurrency(entry.spent)}</span>
          <span className="split-label">Dépenses</span>
        </span>
      </section>

      <section className="feed recap-feed">
        <div className="feed-head">
          <h3 className="feed-title">Dépenses</h3>
          <span className="feed-count">{expenses.length} dans le mois</span>
        </div>
        {groups.length === 0 ? (
          <div className="feed-empty">Aucune dépense notée ce mois-là.</div>
        ) : groups.map(group => (
          <div key={group.date}>
            <h4 className="feed-day">{dayLabel(group.date)}</h4>
            <ul className="rows">
              {group.items.map(exp => (
                <li key={exp.id}><div className="row is-static"><ExpenseLine exp={exp} /></div></li>
              ))}
            </ul>
          </div>
        ))}
      </section>

      <button type="button" className="btn btn-ghost recap-edit" onClick={onEdit}>Modifier ce mois dans Budget</button>
    </Sheet>
  );
}

function BilanPage({ onOpenMonth }) {
  const [months, setMonths] = useState(null);
  const [failed, setFailed] = useState(false);
  const [recap, setRecap] = useState(null);
  const salaryHidden = localStorage.getItem('salaryHidden') === 'true';

  useEffect(() => {
    (async () => {
      try {
        // Premier mois = premier salaire saisi ; au plus 12 mois en arrière
        const salaries = await api.getSalaries();
        const current = getCurrentMonth();
        const first = (salaries || []).map(s => s.month).filter(m => m && m < current).sort()[0];
        if (!first) { setMonths([]); return; }
        const list = [];
        let m = changeMonth(current, -1);
        while (m >= first && list.length < BILAN_MAX_MONTHS) { list.push(m); m = changeMonth(m, -1); }
        const data = await Promise.all(list.map(month => api.getDashboard(month)));
        setMonths(data.map((d, i) => ({
          month: list[i],
          income: d.totalIncome || 0,
          charges: d.totalCharges || 0,
          spent: (d.totalExpenses || 0) + (d.totalPlanned || 0),
          rest: d.remaining || 0,
          data: d,
        })));
      } catch (err) {
        console.error(err);
        setFailed(true);
      }
    })();
  }, []);

  if (failed) return <div className="feed-empty">Le bilan n'a pas pu être chargé. Touche Actualiser pour réessayer.</div>;
  if (!months) return <div className="empty-state"><p>Chargement…</p></div>;

  // Un mois sans salaire saisi fausserait les moyennes : il est listé à part, hors calculs
  const counted = months.filter(m => m.income > 0);
  if (counted.length === 0) {
    return <div className="feed-empty">Le bilan apparaîtra après ton premier mois complet avec un salaire saisi.</div>;
  }

  const avgRest = counted.reduce((s, m) => s + m.rest, 0) / counted.length;
  const avgSpent = counted.reduce((s, m) => s + m.spent, 0) / counted.length;
  const best = counted.reduce((a, b) => (b.rest > a.rest ? b : a));
  const worst = counted.reduce((a, b) => (b.rest < a.rest ? b : a));
  const last = counted[0];
  const diff = last.rest - avgRest;
  // « septembre » → « Septembre » ; « de avril » → « d'avril »
  const monthName = (m) => { const n = getMonthLabel(m).split(' ')[0]; return n.charAt(0).toUpperCase() + n.slice(1); };
  const fromMonth = (m) => { const n = monthName(m).toLowerCase(); return (/^[aeiouéèêh]/.test(n) ? "d'" : 'de ') + n; };
  const signed = (v) => (v > 0 ? '+' : '') + formatEuros(v);

  // Graphique : du plus ancien au plus récent, puis le mois en cours en pointillés
  const chrono = [...counted].reverse();
  const W = 350, H = 170, TOP = 12, AXIS = 150;
  const maxUp = Math.max(1, ...chrono.map(m => m.rest));
  const maxDown = Math.max(0, ...chrono.map(m => -m.rest));
  const zeroY = TOP + (AXIS - TOP) * (maxUp / (maxUp + maxDown));
  const scale = (AXIS - TOP) / (maxUp + maxDown);
  const slot = W / (chrono.length + 1);
  const bw = Math.min(28, slot * 0.56);
  const barX = (i) => i * slot + (slot - bw) / 2;
  const avgY = zeroY - avgRest * scale;
  const maxSpent = Math.max(...counted.map(m => m.spent), avgSpent) * 1.05;

  return (
    <div>
      <section className="hero">
        <p className="hero-label">En fin de mois, il te reste en moyenne</p>
        <p className={`hero-amount ${avgRest < 0 ? 'is-negative' : ''}`}>{formatEuros(avgRest)}</p>
        <p className="hero-daily">
          sur {counted.length} mois, {fromMonth(chrono[0].month)} à {monthName(last.month).toLowerCase()}
        </p>
        {counted.length > 1 && (
          <p className={`trend ${diff >= 0 ? 'is-ok' : 'is-warn'}`}>
            {monthName(last.month)} : {formatEuros(Math.abs(diff))} {diff >= 0 ? 'de mieux' : 'de moins'} que ta moyenne
          </p>
        )}
      </section>

      <figure className="bilan-chart">
        <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label="Ce qu'il t'est resté chaque mois">
          <line x1="0" x2={W} y1={zeroY} y2={zeroY} className="chart-zero" />
          {chrono.map((m, i) => {
            const h = Math.max(2, Math.abs(m.rest) * scale);
            const cls = m.rest < 0 ? 'chart-bar is-neg' : m === best && counted.length > 1 ? 'chart-bar is-best' : 'chart-bar';
            return (
              <g key={m.month}>
                <rect x={barX(i)} y={m.rest >= 0 ? zeroY - h : zeroY} width={bw} height={h} rx="5" className={cls} />
                <text x={barX(i) + bw / 2} y={H - 4} className="chart-axis">{monthName(m.month).charAt(0)}</text>
              </g>
            );
          })}
          <rect x={barX(chrono.length)} y={zeroY - 8} width={bw} height="8" rx="4" className="chart-bar is-current" />
          <text x={barX(chrono.length) + bw / 2} y={H - 4} className="chart-axis is-current">{monthName(getCurrentMonth()).charAt(0)}</text>
          <line x1="0" x2={W} y1={avgY} y2={avgY} className="chart-avg" />
        </svg>
        <figcaption className="chart-cap">Ce qu'il t'est resté chaque mois. Pointillés : ta moyenne. {monthName(getCurrentMonth())} est en cours.</figcaption>
      </figure>

      <section className="split split-static">
        <span className="split-item">
          <span className="split-value">{formatEuros(avgSpent)}</span>
          <span className="split-label">Dépenses / mois</span>
        </span>
        <span className="split-item">
          <span className="split-value is-ok">{monthName(best.month)}</span>
          <span className="split-label">Meilleur mois</span>
        </span>
        <span className="split-item">
          <span className={`split-value ${worst.rest < 0 ? 'is-neg' : ''}`}>{monthName(worst.month)}</span>
          <span className="split-label">Pire mois</span>
        </span>
      </section>

      <section className="feed">
        <div className="feed-head">
          <h2 className="feed-title">Mois par mois</h2>
          <span className="feed-count">Touche un mois pour le détail</span>
        </div>
        <ul className="rows bilan-rows">
          {months.map(m => (
            <li key={m.month}>
              <button type="button" className="bilan-row" onClick={() => setRecap(m)}>
                <span className="bilan-top">
                  <span className="bilan-name">{getMonthLabel(m.month)}</span>
                  {m.income > 0
                    ? <span className={`bilan-rest ${m.rest < 0 ? 'is-neg' : ''}`}>{signed(m.rest)}</span>
                    : <span className="bilan-missing">Salaire non saisi</span>}
                </span>
                <span className="bilan-bar">
                  <span className={`bilan-fill ${m.spent > avgSpent ? 'is-high' : ''}`} style={{ width: `${Math.min(100, (m.spent / maxSpent) * 100)}%` }} />
                  <span className="bilan-avg" style={{ left: `${(avgSpent / maxSpent) * 100}%` }} />
                </span>
                <span className="bilan-meta">
                  Dépenses {formatEuros(m.spent)} · Charges {formatEuros(m.charges)} · Revenus {salaryHidden ? '••••••' : formatEuros(m.income)}
                </span>
              </button>
            </li>
          ))}
        </ul>
      </section>

      {recap && (
        <MonthRecapSheet
          entry={recap}
          avgRest={avgRest}
          compare={recap.income > 0 && counted.length > 1}
          salaryHidden={salaryHidden}
          onClose={() => setRecap(null)}
          onEdit={() => onOpenMonth(recap.month)}
        />
      )}
    </div>
  );
}

// ============================================
// CHARGES PAGE
// ============================================
function ChargesPage() {
  const [categories, setCategories] = useState([]);
  const [charges, setCharges] = useState([]);
  const [expanded, setExpanded] = useState({});
  const [showAddCharge, setShowAddCharge] = useState(null);
  const [newChargeName, setNewChargeName] = useState('');
  const [newChargeAmount, setNewChargeAmount] = useState('');
  const [newChargeEndDate, setNewChargeEndDate] = useState('');
  const [showCategoryModal, setShowCategoryModal] = useState(false);
  const [editCategory, setEditCategory] = useState(null);
  const [catName, setCatName] = useState('');
  const [catColor, setCatColor] = useState('#2872A1');
  const [catIcon, setCatIcon] = useState('📁');
  const [catDeducted, setCatDeducted] = useState(true);
  const [editCharge, setEditCharge] = useState(null);
  const [editChargeName, setEditChargeName] = useState('');
  const [editChargeAmount, setEditChargeAmount] = useState('');
  const [editChargeEndDate, setEditChargeEndDate] = useState('');
  const [showExpired, setShowExpired] = useState(false);
  const [loading, setLoading] = useState(true);

  const loadData = useCallback(async () => {
    try {
      const [cats, chgs] = await Promise.all([api.getCategories(), api.getCharges()]);
      setCategories(cats);
      setCharges(chgs);
    } catch (err) {
      console.error(err);
    }
    setLoading(false);
  }, []);

  useEffect(() => { loadData(); }, [loadData]);

  const toggleExpand = (id) => setExpanded(prev => ({ ...prev, [id]: !prev[id] }));

  const handleAddCharge = async (categoryId) => {
    if (!newChargeName || !newChargeAmount) return;
    try {
      await api.createCharge({
        category_id: categoryId,
        name: newChargeName,
        amount: parseFloat(newChargeAmount),
        end_date: newChargeEndDate || null
      });
      setNewChargeName('');
      setNewChargeAmount('');
      setNewChargeEndDate('');
      setShowAddCharge(null);
      loadData();
    } catch (err) {
      console.error(err);
    }
  };

  const handleDeleteCharge = async (id) => {
    try {
      await api.deleteCharge(id);
      loadData();
    } catch (err) {
      console.error(err);
    }
  };

  const handleSaveCategory = async () => {
    try {
      if (editCategory) {
        await api.updateCategory(editCategory.id, { name: catName, color: catColor, icon: catIcon, deducted_from_salary: catDeducted ? 1 : 0 });
      } else {
        await api.createCategory({ name: catName, color: catColor, icon: catIcon, deducted_from_salary: catDeducted ? 1 : 0 });
      }
      setShowCategoryModal(false);
      setEditCategory(null);
      loadData();
    } catch (err) {
      console.error(err);
    }
  };

  const handleDeleteCategory = async (id) => {
    if (!confirm('Supprimer cette catégorie et toutes ses charges ?')) return;
    try {
      await api.deleteCategory(id);
      loadData();
    } catch (err) {
      console.error(err);
    }
  };

  const openEditCategory = (cat) => {
    setEditCategory(cat);
    setCatName(cat.name);
    setCatColor(cat.color);
    setCatIcon(cat.icon);
    setCatDeducted(!!cat.deducted_from_salary);
    setShowCategoryModal(true);
  };

  const openNewCategory = () => {
    setEditCategory(null);
    setCatName('');
    setCatColor('#2872A1');
    setCatIcon('📁');
    setCatDeducted(true);
    setShowCategoryModal(true);
  };

  const openEditCharge = (charge) => {
    setEditCharge(charge);
    setEditChargeName(charge.name);
    setEditChargeAmount(String(charge.amount));
    setEditChargeEndDate(charge.end_date || '');
  };

  const handleUpdateCharge = async () => {
    if (!editChargeName || !editChargeAmount) return;
    try {
      await api.updateCharge(editCharge.id, {
        name: editChargeName,
        amount: parseFloat(editChargeAmount),
        end_date: editChargeEndDate || null
      });
      setEditCharge(null);
      loadData();
    } catch (err) {
      console.error(err);
    }
  };

  const today = new Date().toISOString().slice(0, 10);
  const chargesByCategory = categories.map(cat => ({
    ...cat,
    charges: charges.filter(c => c.category_id === cat.id && (showExpired || !c.end_date || c.end_date >= today)),
    total: charges.filter(c => c.category_id === cat.id && c.is_active && (!c.end_date || c.end_date >= today)).reduce((s, c) => s + c.amount, 0)
  }));

  const grandTotal = chargesByCategory.reduce((s, c) => s + c.total, 0);

  if (loading) return <div className="empty-state"><div className="icon">...</div><p>Chargement...</p></div>;

  return (
    <div>
      <div className="card" style={{marginBottom:'20px'}}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center'}}>
          <div>
            <div style={{fontSize:'14px',color:'var(--ink-soft)'}}>Total des charges mensuelles</div>
            <div style={{fontSize:'24px',fontWeight:'800',color:'var(--brand)'}}>{formatCurrency(grandTotal)}</div>
          </div>
          <div style={{display:'flex',gap:'8px',alignItems:'center'}}>
            <button className="btn btn-ghost btn-sm" onClick={() => setShowExpired(v => !v)} style={{fontSize:'12px',color:'var(--ink-soft)'}}>
              {showExpired ? 'Masquer historique' : 'Voir historique'}
            </button>
            <button className="btn btn-primary btn-sm" onClick={openNewCategory}>+ Catégorie</button>
          </div>
        </div>
      </div>

      {chargesByCategory.map(cat => (
        <div key={cat.id} className="category-section">
          <div className="category-header" onClick={() => toggleExpand(cat.id)}>
            <div className="category-left">
              <div className="category-dot" style={{background: cat.color}} />
              <span style={{fontSize:'18px'}}>{cat.icon}</span>
              <span className="category-name">{cat.name}</span>
            </div>
            <div className="category-right">
              <span className="category-total">{formatCurrency(cat.total)}</span>
              <span className={`category-arrow ${expanded[cat.id] ? 'open' : ''}`}>▼</span>
            </div>
          </div>

          {expanded[cat.id] && (
            <div className="category-charges">
              {cat.charges.map(charge => {
                const isExpired = charge.end_date && charge.end_date < today;
                return (
                  <div key={charge.id} className="charge-item" style={{opacity: isExpired ? 0.5 : 1}}>
                    {editCharge && editCharge.id === charge.id ? (
                      <div style={{display:'flex',flexDirection:'column',gap:'8px',flex:1}}>
                        <div style={{display:'flex',gap:'8px',alignItems:'center'}}>
                          <input className="form-input" value={editChargeName} onChange={e => setEditChargeName(e.target.value)} style={{flex:1,padding:'6px 10px',fontSize:'13px'}} />
                          <input className="form-input" type="number" step="0.01" value={editChargeAmount} onChange={e => setEditChargeAmount(e.target.value)} style={{width:'100px',padding:'6px 10px',fontSize:'13px'}} />
                        </div>
                        <div style={{display:'flex',gap:'8px',alignItems:'center'}}>
                          <label style={{fontSize:'11px',color:'var(--ink-soft)',whiteSpace:'nowrap'}}>Date de fin :</label>
                          <input className="form-input" type="date" value={editChargeEndDate} onChange={e => setEditChargeEndDate(e.target.value)} style={{flex:1,padding:'6px 10px',fontSize:'13px'}} />
                        </div>
                        <div style={{display:'flex',gap:'8px'}}>
                          <button className="btn btn-primary btn-sm" onClick={handleUpdateCharge} style={{padding:'10px 20px',fontSize:'14px'}}>Enregistrer</button>
                          <button className="btn btn-ghost btn-sm" onClick={() => setEditCharge(null)} style={{padding:'10px 20px',fontSize:'14px'}}>Annuler</button>
                        </div>
                      </div>
                    ) : (
                      <>
                        <div style={{flex:1}}>
                          <div className="charge-name">
                            {charge.name}
                            {isExpired && <span style={{marginLeft:'6px',fontSize:'11px',color:'var(--spend)',fontWeight:'600'}}>✖ Expirée</span>}
                          </div>
                          {charge.end_date && !isExpired && (
                            <div style={{fontSize:'11px',color:'var(--ink-soft)',marginTop:'2px'}}>
                              Jusqu'au {new Date(charge.end_date + 'T00:00:00').toLocaleDateString('fr-FR', {day: 'numeric', month: 'short', year: 'numeric'})}
                            </div>
                          )}
                        </div>
                        <span className="charge-amount">{formatCurrency(charge.amount)}</span>
                        <div className="charge-actions">
                          <button onClick={() => openEditCharge(charge)} title="Modifier" aria-label={`Modifier ${charge.name}`}><Icon name="edit" size={16} /></button>
                          <button className="delete" onClick={() => handleDeleteCharge(charge.id)} title="Supprimer" aria-label={`Supprimer ${charge.name}`}><Icon name="trash" size={16} /></button>
                        </div>
                      </>
                    )}
                  </div>
                );
              })}

              {showAddCharge === cat.id ? (
                <div className="add-charge-form" style={{display:'flex',flexDirection:'column',gap:'8px'}}>
                  <div style={{display:'flex',gap:'8px'}}>
                    <input className="form-input" placeholder="Nom" value={newChargeName} onChange={e => setNewChargeName(e.target.value)} style={{flex:1}} />
                    <input className="form-input" type="number" step="0.01" placeholder="€" value={newChargeAmount} onChange={e => setNewChargeAmount(e.target.value)} style={{width:'90px'}} />
                  </div>
                  <div style={{display:'flex',gap:'8px',alignItems:'center'}}>
                    <label style={{fontSize:'12px',color:'var(--ink-soft)',whiteSpace:'nowrap'}}>Date de fin (optionnel) :</label>
                    <input className="form-input" type="date" value={newChargeEndDate} onChange={e => setNewChargeEndDate(e.target.value)} style={{flex:1,padding:'6px 10px',fontSize:'13px'}} />
                  </div>
                  <div style={{display:'flex',gap:'8px'}}>
                    <button className="btn btn-primary btn-sm" onClick={() => handleAddCharge(cat.id)} style={{padding:'8px 14px',flex:1}}>Ajouter</button>
                    <button className="btn btn-ghost btn-sm" onClick={() => setShowAddCharge(null)} style={{padding:'8px 14px'}}>Annuler</button>
                  </div>
                </div>
              ) : (
                <div style={{display:'flex',gap:'8px',marginTop:'8px'}}>
                  <button className="btn btn-ghost btn-sm" onClick={() => { setShowAddCharge(cat.id); setNewChargeName(''); setNewChargeAmount(''); setNewChargeEndDate(''); }}>+ Ajouter une charge</button>
                  <button className="btn btn-ghost btn-sm" onClick={() => openEditCategory(cat)} style={{color:'var(--ink-soft)'}}>Modifier catégorie</button>
                  <button className="btn btn-ghost btn-sm" onClick={() => handleDeleteCategory(cat.id)} style={{color:'var(--spend)'}}>Supprimer</button>
                </div>
              )}
            </div>
          )}
        </div>
      ))}

      {categories.length === 0 && (
        <div className="empty-state">
          <div className="icon">📂</div>
          <p>Aucune catégorie. Créez-en une pour commencer.</p>
        </div>
      )}

      {/* Category Modal */}
      {showCategoryModal && (
        <Modal title={editCategory ? 'Modifier la catégorie' : 'Nouvelle catégorie'} onClose={() => setShowCategoryModal(false)}>
          <div className="form-group">
            <label>Nom</label>
            <input className="form-input" value={catName} onChange={e => setCatName(e.target.value)} placeholder="Ex: Charges fixes" />
          </div>
          <div className="form-group">
            <label>Couleur</label>
            <input type="color" value={catColor} onChange={e => setCatColor(e.target.value)} style={{width:'100%',height:'40px',border:'none',borderRadius:'8px',cursor:'pointer'}} />
          </div>
          <div className="form-group">
            <label>Icône (emoji)</label>
            <input className="form-input" value={catIcon} onChange={e => setCatIcon(e.target.value)} placeholder="Ex: 🏠" />
          </div>
          <div className="settings-item" style={{border:'none',padding:'12px 0'}}>
            <div>
              <div className="settings-label">Déduit du salaire</div>
              <div className="settings-desc">Inclure dans le calcul du reste disponible</div>
            </div>
            <button className={`toggle ${catDeducted ? 'active' : ''}`} onClick={() => setCatDeducted(!catDeducted)} />
          </div>
          <button className="btn btn-primary" onClick={handleSaveCategory} style={{marginTop:'12px'}}>
            {editCategory ? 'Enregistrer' : 'Créer la catégorie'}
          </button>
        </Modal>
      )}
    </div>
  );
}

// ============================================
// CALENDAR PAGE (with swipe)
// ============================================
function CalendarPage() {
  const [month, setMonth] = useState(getCurrentMonth());
  const [expenses, setExpenses] = useState([]);
  const [selectedDay, setSelectedDay] = useState(null);
  const [newName, setNewName] = useState('');
  const [newAmount, setNewAmount] = useState('');
  const [editExp, setEditExp] = useState(null);
  const [editExpName, setEditExpName] = useState('');
  const [editExpAmount, setEditExpAmount] = useState('');
  const [loading, setLoading] = useState(true);

  const swipeHandlers = useSwipe(
    () => setMonth(m => changeMonth(m, 1)),
    () => setMonth(m => changeMonth(m, -1))
  );

  const loadCalendar = useCallback(async () => {
    try {
      const data = await api.getCalendar(month);
      setExpenses(data);
    } catch (err) {
      console.error(err);
    }
    setLoading(false);
  }, [month]);

  useEffect(() => { loadCalendar(); }, [loadCalendar]);

  const [year, monthNum] = month.split('-').map(Number);
  const firstDay = new Date(year, monthNum - 1, 1);
  const lastDay = new Date(year, monthNum, 0);
  const daysInMonth = lastDay.getDate();
  let startOffset = firstDay.getDay() - 1;
  if (startOffset < 0) startOffset = 6;

  const today = new Date();
  const todayStr = today.toISOString().slice(0, 10);

  const days = [];
  const prevMonthLast = new Date(year, monthNum - 1, 0).getDate();
  for (let i = startOffset - 1; i >= 0; i--) {
    days.push({ day: prevMonthLast - i, otherMonth: true, dateStr: null });
  }
  for (let d = 1; d <= daysInMonth; d++) {
    const dateStr = `${year}-${String(monthNum).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
    days.push({ day: d, otherMonth: false, dateStr });
  }
  const remaining = 42 - days.length;
  for (let d = 1; d <= remaining; d++) {
    days.push({ day: d, otherMonth: true, dateStr: null });
  }

  const plannedExpenses = expenses.filter(e => e.is_planned);

  const expensesByDate = {};
  plannedExpenses.forEach(exp => {
    if (!expensesByDate[exp.date]) expensesByDate[exp.date] = [];
    expensesByDate[exp.date].push(exp);
  });

  const dayExpenses = selectedDay ? (expensesByDate[selectedDay] || []) : [];
  const dayTotal = dayExpenses.reduce((s, e) => s + e.amount, 0);

  const handleAddPlanned = async (e) => {
    e.preventDefault();
    if (!newName || !newAmount || !selectedDay) return;
    try {
      await api.createExpense({
        name: newName,
        amount: parseFloat(newAmount),
        date: selectedDay,
        is_planned: true
      });
      setNewName('');
      setNewAmount('');
      loadCalendar();
    } catch (err) {
      console.error(err);
    }
  };

  const handleDeleteExpense = async (id) => {
    try {
      await api.deleteExpense(id);
      loadCalendar();
    } catch (err) {
      console.error(err);
    }
  };

  const openEditExp = (exp) => {
    setEditExp(exp);
    setEditExpName(exp.name);
    setEditExpAmount(String(exp.amount));
  };

  const handleUpdateExp = async () => {
    if (!editExpName || !editExpAmount || !editExp) return;
    try {
      await api.updateExpense(editExp.id, { name: editExpName, amount: parseFloat(editExpAmount) });
      setEditExp(null);
      loadCalendar();
    } catch (err) {
      console.error(err);
    }
  };

  const totalPlanned = plannedExpenses.reduce((s, e) => s + e.amount, 0);

  return (
    <div {...swipeHandlers}>
      <div className="month-selector">
        <button onClick={() => setMonth(m => changeMonth(m, -1))}>&#9664;</button>
        <span className="month-label" style={{textTransform:'capitalize'}}>{getMonthLabel(month)}</span>
        <button onClick={() => setMonth(m => changeMonth(m, 1))}>&#9654;</button>
      </div>

      <div className="card" style={{marginBottom:'16px',textAlign:'center'}}>
        <div style={{fontSize:'12px',color:'var(--ink-soft)'}}>Total dépenses prévisionnelles</div>
        <div style={{fontSize:'22px',fontWeight:'700',color:'var(--warn)'}}>{formatCurrency(totalPlanned)}</div>
      </div>

      <div className="card">
        <div className="calendar-grid">
          {['Lun','Mar','Mer','Jeu','Ven','Sam','Dim'].map(d => (
            <div key={d} className="calendar-header-cell">{d}</div>
          ))}
          {days.map((d, i) => {
            const dayExps = d.dateStr ? (expensesByDate[d.dateStr] || []) : [];
            const dayTotal = dayExps.reduce((s, e) => s + e.amount, 0);
            return (
              <div
                key={i}
                className={`calendar-day ${d.otherMonth ? 'other-month' : ''} ${d.dateStr === todayStr ? 'today' : ''}`}
                onClick={() => !d.otherMonth && setSelectedDay(d.dateStr)}
              >
                <span>{d.day}</span>
                {dayTotal > 0 && !d.otherMonth && (
                  <span className="calendar-day-amount">{dayTotal.toFixed(0)}€</span>
                )}
                {dayExps.length > 0 && !d.otherMonth && (
                  <span className="calendar-day-dot planned" />
                )}
              </div>
            );
          })}
        </div>
      </div>

      {/* Day popup */}
      {selectedDay && (
        <Modal
          title={new Date(selectedDay + 'T00:00:00').toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long' })}
          onClose={() => setSelectedDay(null)}
        >
          {dayExpenses.length > 0 && (
            <div style={{marginBottom:'16px'}}>
              {dayExpenses.map(exp => (
                <div key={exp.id} className="expense-item">
                  {editExp && editExp.id === exp.id ? (
                    <div style={{display:'flex',gap:'8px',flex:1,alignItems:'center'}}>
                      <input className="form-input" value={editExpName} onChange={e => setEditExpName(e.target.value)} style={{flex:1,padding:'8px 12px',fontSize:'14px'}} />
                      <input className="form-input" type="number" step="0.01" value={editExpAmount} onChange={e => setEditExpAmount(e.target.value)} style={{width:'110px',padding:'8px 12px',fontSize:'14px'}} />
                      <button className="btn btn-primary btn-sm" onClick={handleUpdateExp} style={{padding:'10px 16px',fontSize:'14px'}}>OK</button>
                      <button className="btn btn-ghost btn-sm" onClick={() => setEditExp(null)} style={{padding:'10px 16px',fontSize:'14px'}}>✕</button>
                    </div>
                  ) : (
                    <>
                      <div className="expense-info">
                        <div className="expense-name">{exp.name}</div>
                      </div>
                      <span className="expense-amount">-{formatCurrency(exp.amount)}</span>
                      <button className="btn-icon" onClick={() => openEditExp(exp)} title="Modifier" aria-label={`Modifier ${exp.name}`}><Icon name="edit" size={18} /></button>
                      <button className="btn-icon" onClick={() => handleDeleteExpense(exp.id)} title="Supprimer" aria-label={`Supprimer ${exp.name}`}><Icon name="trash" size={18} /></button>
                    </>
                  )}
                </div>
              ))}
              {dayExpenses.length > 1 && (
                <div style={{textAlign:'right',fontWeight:'700',paddingTop:'8px',borderTop:'1px solid #d4e3ed',marginTop:'8px'}}>
                  Total: {formatCurrency(dayTotal)}
                </div>
              )}
            </div>
          )}

          <div style={{borderTop: dayExpenses.length ? '1px solid #d4e3ed' : 'none', paddingTop: dayExpenses.length ? '16px' : '0'}}>
            <div style={{fontWeight:'600',marginBottom:'12px',fontSize:'14px'}}>Ajouter une dépense prévisionnelle</div>
            <form onSubmit={handleAddPlanned}>
              <div className="form-group">
                <input className="form-input" placeholder="Nom de la dépense" value={newName} onChange={e => setNewName(e.target.value)} required />
              </div>
              <div className="form-group">
                <input className="form-input" type="number" step="0.01" placeholder="Montant en €" value={newAmount} onChange={e => setNewAmount(e.target.value)} required />
              </div>
              <button className="btn btn-primary" type="submit">Ajouter au calendrier</button>
            </form>
          </div>
        </Modal>
      )}
    </div>
  );
}

// ============================================
// BANK ACCOUNTS PAGE
// ============================================
function BankAccountsPage() {
  const [bankAccounts, setBankAccounts] = useState([]);
  const [showLinkModal, setShowLinkModal] = useState(null);
  const [modalCharges, setModalCharges] = useState([]);
  const [selectedChargeIds, setSelectedChargeIds] = useState([]);
  const [showAddModal, setShowAddModal] = useState(false);
  const [editAccount, setEditAccount] = useState(null);
  const [accountName, setAccountName] = useState('');
  const [loading, setLoading] = useState(true);

  const loadBankAccounts = useCallback(async () => {
    try {
      const data = await api.getBankAccounts();
      setBankAccounts(data);
    } catch (err) {
      console.error(err);
    }
    setLoading(false);
  }, []);

  useEffect(() => { loadBankAccounts(); }, [loadBankAccounts]);

  const openLinkModal = async (account) => {
    try {
      const data = await api.getBankAccountCharges(account.id);
      setModalCharges(data.charges);
      setSelectedChargeIds(data.charges.filter(c => c.is_linked).map(c => c.id));
      setShowLinkModal({ accountId: account.id, accountName: account.name });
    } catch (err) {
      console.error(err);
    }
  };

  const toggleChargeSelection = (chargeId) => {
    setSelectedChargeIds(prev => {
      if (prev.includes(chargeId)) {
        return prev.filter(id => id !== chargeId);
      } else {
        return [...prev, chargeId];
      }
    });
  };

  const handleSaveLinks = async () => {
    if (!showLinkModal) return;
    try {
      await api.updateBankAccountCharges(showLinkModal.accountId, { charge_ids: selectedChargeIds });
      setShowLinkModal(null);
      loadBankAccounts();
    } catch (err) {
      console.error(err);
    }
  };

  const handleAddAccount = async (e) => {
    e.preventDefault();
    if (!accountName.trim()) return;
    try {
      await api.createBankAccount({ name: accountName });
      setAccountName('');
      setShowAddModal(false);
      loadBankAccounts();
    } catch (err) {
      console.error(err);
    }
  };

  const openEditAccount = (account) => {
    setEditAccount(account);
    setAccountName(account.name);
  };

  const handleUpdateAccount = async (id, name) => {
    if (!name.trim() || name === editAccount.name) {
      setEditAccount(null);
      return;
    }
    try {
      await api.updateBankAccount(id, { name });
      setEditAccount(null);
      loadBankAccounts();
    } catch (err) {
      console.error(err);
    }
  };

  const handleDeleteAccount = async (id) => {
    if (!window.confirm('Supprimer ce compte bancaire ?')) return;
    try {
      await api.deleteBankAccount(id);
      loadBankAccounts();
    } catch (err) {
      console.error(err);
    }
  };

  const selectedTotal = modalCharges
    .filter(c => selectedChargeIds.includes(c.id))
    .reduce((sum, c) => sum + c.amount, 0);

  if (loading) return <div style={{textAlign:'center',padding:'40px',color:'var(--ink-soft)'}}>Chargement...</div>;

  return (
    <div>
      <div className="card" style={{marginBottom:'16px',display:'flex',alignItems:'center',justifyContent:'space-between'}}>
        <div>
          <div style={{fontSize:'12px',color:'var(--ink-soft)'}}>Comptes bancaires</div>
          <div style={{fontSize:'18px',fontWeight:'700'}}>{bankAccounts.length} compte{bankAccounts.length > 1 ? 's' : ''}</div>
        </div>
        <button className="btn btn-primary btn-sm" onClick={() => setShowAddModal(true)}>+ Compte</button>
      </div>

      <div style={{display:'flex',flexDirection:'column',gap:'12px'}}>
        {bankAccounts.map(account => (
          <div key={account.id} className="card" style={{padding:'16px'}}>
            {editAccount && editAccount.id === account.id ? (
              <div style={{display:'flex',gap:'8px',marginBottom:'12px'}}>
                <input
                  className="form-input"
                  value={accountName}
                  onChange={e => setAccountName(e.target.value)}
                  style={{flex:1,padding:'8px 12px',fontSize:'14px'}}
                  autoFocus
                />
                <button className="btn btn-primary btn-sm" onClick={() => handleUpdateAccount(account.id, accountName)}>OK</button>
                <button className="btn btn-ghost btn-sm" onClick={() => setEditAccount(null)}>X</button>
              </div>
            ) : (
              <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:'12px'}}>
                <div>
                  <div style={{fontSize:'16px',fontWeight:'700'}}>{account.name}</div>
                  <div style={{fontSize:'13px',color:'var(--ink-soft)'}}>Total: {formatCurrency(account.total)}</div>
                </div>
              </div>
            )}

            <div style={{display:'flex',gap:'8px',flexWrap:'wrap',alignItems:'center'}}>
              <button className="btn btn-primary btn-sm" onClick={() => openLinkModal(account)}>
                Gérer les charges
              </button>
              <button className="btn btn-ghost btn-sm" onClick={() => openEditAccount(account)}>
                Renommer
              </button>
              <button className="btn-icon" onClick={() => handleDeleteAccount(account.id)} title="Supprimer" aria-label={`Supprimer ${account.name}`}>
                <Icon name="trash" size={18} />
              </button>
            </div>
          </div>
        ))}
      </div>

      {/* Link charges modal */}
      {showLinkModal && (
        <Modal
          title={`Gérer les charges - ${showLinkModal.accountName}`}
          onClose={() => setShowLinkModal(null)}
        >
          <div style={{maxHeight:'400px',overflowY:'auto',marginBottom:'16px'}}>
            {modalCharges.map(charge => {
              const isSelected = selectedChargeIds.includes(charge.id);
              return (
                <div
                  key={charge.id}
                  className="charge-item"
                  onClick={() => toggleChargeSelection(charge.id)}
                  style={{
                    cursor:'pointer',
                    display:'flex',
                    alignItems:'center',
                    gap:'12px',
                    padding:'12px',
                    borderRadius:'8px',
                    marginBottom:'8px',
                    background: isSelected ? 'var(--brand-soft)' : 'transparent',
                    border: isSelected ? '2px solid #2872A1' : '2px solid transparent',
                    transition:'all 0.2s'
                  }}
                >
                  <input
                    type="checkbox"
                    checked={isSelected}
                    onChange={() => {}}
                    style={{width:'18px',height:'18px',cursor:'pointer'}}
                  />
                  <div style={{flex:1}}>
                    <div style={{fontWeight:'600',fontSize:'14px'}}>{charge.name}</div>
                    <div style={{fontSize:'12px',color:'var(--ink-soft)',display:'flex',alignItems:'center',gap:'4px',marginTop:'2px'}}>
                      <span>{charge.category_icon}</span>
                      <span style={{color:charge.category_color}}>{charge.category_name}</span>
                    </div>
                  </div>
                  <div style={{fontWeight:'700',fontSize:'14px'}}>{formatCurrency(charge.amount)}</div>
                </div>
              );
            })}
          </div>

          <div style={{borderTop:'1px solid #d4e3ed',paddingTop:'16px'}}>
            <div style={{textAlign:'center',marginBottom:'12px'}}>
              <div style={{fontSize:'12px',color:'var(--ink-soft)'}}>Total sélectionné</div>
              <div style={{fontSize:'18px',fontWeight:'700',color:'var(--brand)'}}>{formatCurrency(selectedTotal)}</div>
            </div>
            <button className="btn btn-primary" onClick={handleSaveLinks}>
              Enregistrer ({selectedChargeIds.length} charge{selectedChargeIds.length > 1 ? 's' : ''})
            </button>
          </div>
        </Modal>
      )}

      {/* Add account modal */}
      {showAddModal && (
        <Modal
          title="Nouveau compte bancaire"
          onClose={() => {
            setShowAddModal(false);
            setAccountName('');
          }}
        >
          <form onSubmit={handleAddAccount}>
            <div className="form-group">
              <label>Nom du compte</label>
              <input
                className="form-input"
                placeholder="Ex: Crédit Agricole"
                value={accountName}
                onChange={e => setAccountName(e.target.value)}
                required
                autoFocus
              />
            </div>
            <button className="btn btn-primary" type="submit">Créer le compte</button>
          </form>
        </Modal>
      )}
    </div>
  );
}

// ============================================
// SETTINGS PAGE (with username change + delete account)
// ============================================
function SettingsPage({ user, onLogout, onUpdateUser }) {
  const [darkMode, setDarkMode] = useState(user?.dark_mode || false);

  // Modal states
  const [showUsernameModal, setShowUsernameModal] = useState(false);
  const [showEmailModal, setShowEmailModal] = useState(false);
  const [showPasswordModal, setShowPasswordModal] = useState(false);
  const [showDeleteModal, setShowDeleteModal] = useState(false);

  // Username modal states
  const [newUsername, setNewUsername] = useState('');
  const [usernameError, setUsernameError] = useState('');
  const [usernameSuccess, setUsernameSuccess] = useState('');

  // Email modal states
  const [newEmail, setNewEmail] = useState('');
  const [emailError, setEmailError] = useState('');
  const [emailSuccess, setEmailSuccess] = useState('');

  // Password modal states
  const [currentPassword, setCurrentPassword] = useState('');
  const [newPassword, setNewPassword] = useState('');
  const [passwordError, setPasswordError] = useState('');
  const [passwordSuccess, setPasswordSuccess] = useState('');

  // Delete account states
  const [deletePassword, setDeletePassword] = useState('');
  const [deleteError, setDeleteError] = useState('');

  const toggleDarkMode = async () => {
    const newVal = !darkMode;
    setDarkMode(newVal);
    document.body.classList.toggle('dark-mode', newVal);
    try {
      await api.updateProfile({ dark_mode: newVal });
      onUpdateUser({ ...user, dark_mode: newVal ? 1 : 0 });
    } catch (err) {
      console.error(err);
    }
  };

  const handleChangeUsername = async (e) => {
    e.preventDefault();
    setUsernameError('');
    setUsernameSuccess('');
    if (!newUsername || newUsername === user?.username) {
      setUsernameError("Le nom d'utilisateur n'a pas changé");
      return;
    }
    try {
      await api.updateProfile({ username: newUsername });
      onUpdateUser({ ...user, username: newUsername });
      setUsernameSuccess("Nom d'utilisateur modifié avec succès");
      setTimeout(() => {
        setShowUsernameModal(false);
        setNewUsername('');
        setUsernameSuccess('');
      }, 1500);
    } catch (err) {
      setUsernameError(err.message);
    }
  };

  const handleChangeEmail = async (e) => {
    e.preventDefault();
    setEmailError('');
    setEmailSuccess('');
    if (!newEmail || newEmail === user?.email) {
      setEmailError("L'adresse email n'a pas changé");
      return;
    }
    try {
      await api.updateProfile({ email: newEmail });
      onUpdateUser({ ...user, email: newEmail.toLowerCase() });
      setEmailSuccess("Adresse email modifiée avec succès");
      setTimeout(() => {
        setShowEmailModal(false);
        setNewEmail('');
        setEmailSuccess('');
      }, 1500);
    } catch (err) {
      setEmailError(err.message);
    }
  };

  const handleChangePassword = async (e) => {
    e.preventDefault();
    setPasswordError('');
    setPasswordSuccess('');
    if (newPassword.length < 6) {
      setPasswordError('Le mot de passe doit contenir au moins 6 caractères');
      return;
    }
    try {
      await api.updateProfile({ current_password: currentPassword, new_password: newPassword });
      setPasswordSuccess('Mot de passe modifié avec succès');
      setTimeout(() => {
        setShowPasswordModal(false);
        setCurrentPassword('');
        setNewPassword('');
        setPasswordSuccess('');
      }, 1500);
    } catch (err) {
      setPasswordError(err.message);
    }
  };

  const handleDeleteAccount = async () => {
    setDeleteError('');
    if (!deletePassword) {
      setDeleteError('Entrez votre mot de passe');
      return;
    }
    try {
      await api.deleteAccount({ password: deletePassword });
      onLogout();
    } catch (err) {
      setDeleteError(err.message);
    }
  };

  const openUsernameModal = () => {
    setNewUsername(user?.username || '');
    setUsernameError('');
    setUsernameSuccess('');
    setShowUsernameModal(true);
  };

  const openEmailModal = () => {
    setNewEmail(user?.email || '');
    setEmailError('');
    setEmailSuccess('');
    setShowEmailModal(true);
  };

  const openPasswordModal = () => {
    setCurrentPassword('');
    setNewPassword('');
    setPasswordError('');
    setPasswordSuccess('');
    setShowPasswordModal(true);
  };

  return (
    <div>
      <div className="card">
        <div className="card-title" style={{marginBottom:'16px'}}>Compte</div>

        <div className="settings-item" onClick={openUsernameModal} style={{cursor:'pointer'}}>
          <div>
            <div className="settings-label">Nom d'utilisateur</div>
            <div className="settings-desc">{user?.username}</div>
          </div>
          <span style={{fontSize:'20px',color:'var(--ink-soft)'}}>›</span>
        </div>

        <div className="settings-item" onClick={openEmailModal} style={{cursor:'pointer'}}>
          <div>
            <div className="settings-label">Adresse email</div>
            <div className="settings-desc">{user?.email}</div>
          </div>
          <span style={{fontSize:'20px',color:'var(--ink-soft)'}}>›</span>
        </div>

        <div className="settings-item" onClick={openPasswordModal} style={{cursor:'pointer'}}>
          <div>
            <div className="settings-label">Mot de passe</div>
            <div className="settings-desc">••••••••</div>
          </div>
          <span style={{fontSize:'20px',color:'var(--ink-soft)'}}>›</span>
        </div>
      </div>

      <div className="card">
        <div className="card-title" style={{marginBottom:'16px'}}>Préférences</div>
        <div className="settings-item">
          <div>
            <div className="settings-label">Mode sombre</div>
            <div className="settings-desc">Interface en thème foncé</div>
          </div>
          <button className={`toggle ${darkMode ? 'active' : ''}`} onClick={toggleDarkMode} />
        </div>
      </div>

      <div className="card">
        <button className="btn btn-danger" onClick={onLogout} style={{marginBottom:'12px'}}>Se déconnecter</button>
        <button className="btn btn-outline-danger" onClick={() => { setShowDeleteModal(true); setDeletePassword(''); setDeleteError(''); }}>Supprimer mon compte</button>
      </div>

      {/* Username modal */}
      {showUsernameModal && (
        <Modal title="Modifier le nom d'utilisateur" onClose={() => setShowUsernameModal(false)}>
          {usernameSuccess && <div style={{background:'var(--ok-soft)',color:'var(--ok)',padding:'10px 16px',borderRadius:'10px',marginBottom:'12px',fontSize:'13px'}}>{usernameSuccess}</div>}
          {usernameError && <div className="error-msg">{usernameError}</div>}
          <form onSubmit={handleChangeUsername}>
            <div className="form-group">
              <label>Nouveau nom d'utilisateur</label>
              <input
                className="form-input"
                type="text"
                value={newUsername}
                onChange={e => setNewUsername(e.target.value)}
                placeholder="Votre nouveau nom d'utilisateur"
                required
                autoFocus
              />
            </div>
            <button className="btn btn-primary" type="submit">Enregistrer</button>
          </form>
        </Modal>
      )}

      {/* Email modal */}
      {showEmailModal && (
        <Modal title="Modifier l'adresse email" onClose={() => setShowEmailModal(false)}>
          {emailSuccess && <div style={{background:'var(--ok-soft)',color:'var(--ok)',padding:'10px 16px',borderRadius:'10px',marginBottom:'12px',fontSize:'13px'}}>{emailSuccess}</div>}
          {emailError && <div className="error-msg">{emailError}</div>}
          <form onSubmit={handleChangeEmail}>
            <div className="form-group">
              <label>Nouvelle adresse email</label>
              <input
                className="form-input"
                type="email"
                value={newEmail}
                onChange={e => setNewEmail(e.target.value)}
                placeholder="votre@email.com"
                required
                autoFocus
              />
            </div>
            <button className="btn btn-primary" type="submit">Enregistrer</button>
          </form>
        </Modal>
      )}

      {/* Password modal */}
      {showPasswordModal && (
        <Modal title="Modifier le mot de passe" onClose={() => setShowPasswordModal(false)}>
          {passwordSuccess && <div style={{background:'var(--ok-soft)',color:'var(--ok)',padding:'10px 16px',borderRadius:'10px',marginBottom:'12px',fontSize:'13px'}}>{passwordSuccess}</div>}
          {passwordError && <div className="error-msg">{passwordError}</div>}
          <form onSubmit={handleChangePassword}>
            <div className="form-group">
              <label>Mot de passe actuel</label>
              <input
                className="form-input"
                type="password"
                value={currentPassword}
                onChange={e => setCurrentPassword(e.target.value)}
                placeholder="Votre mot de passe actuel"
                required
                autoFocus
              />
            </div>
            <div className="form-group">
              <label>Nouveau mot de passe</label>
              <input
                className="form-input"
                type="password"
                value={newPassword}
                onChange={e => setNewPassword(e.target.value)}
                placeholder="Minimum 6 caractères"
                required
                minLength={6}
              />
            </div>
            <button className="btn btn-primary" type="submit">Enregistrer</button>
          </form>
        </Modal>
      )}

      {/* Delete account modal */}
      {showDeleteModal && (
        <Modal title="Supprimer le compte" onClose={() => setShowDeleteModal(false)}>
          <p style={{fontSize:'14px',color:'var(--ink-soft)',marginBottom:'16px'}}>
            Cette action est irréversible. Toutes vos données seront supprimées définitivement.
          </p>
          {deleteError && <div className="error-msg">{deleteError}</div>}
          <div className="form-group">
            <label>Confirmez avec votre mot de passe</label>
            <input className="form-input" type="password" value={deletePassword} onChange={e => setDeletePassword(e.target.value)} placeholder="Votre mot de passe" />
          </div>
          <button className="btn btn-danger" onClick={handleDeleteAccount}>Supprimer définitivement</button>
        </Modal>
      )}
    </div>
  );
}

// ============================================
// REFRESH ICON (SVG)
// ============================================
function RefreshIcon() {
  return (
    <svg className="ico" width="20" height="20" viewBox="0 0 24 24" aria-hidden="true">
      <polyline points="23 4 23 10 17 10" />
      <polyline points="1 20 1 14 7 14" />
      <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
    </svg>
  );
}

// ============================================
// APP
// ============================================
function App() {
  const [user, setUser] = useState(null);
  const [page, setPage] = useState('dashboard');
  const [authPage, setAuthPage] = useState('login');
  const [refreshing, setRefreshing] = useState(false);
  const [refreshKey, setRefreshKey] = useState(0);
  // Le mois affiché vit ici : il survit à l'actualisation et sert à l'ajout rapide
  const [dashMonth, setDashMonth] = useState(getCurrentMonth());
  const [showAdd, setShowAdd] = useState(false);
  const [showMonthPicker, setShowMonthPicker] = useState(false);

  useEffect(() => {
    const token = localStorage.getItem('token');
    const savedUser = localStorage.getItem('user');
    if (token && savedUser) {
      const u = JSON.parse(savedUser);
      setUser(u);
      if (u.dark_mode) document.body.classList.add('dark-mode');
    }
  }, []);

  const handleLogin = (data) => {
    localStorage.setItem('token', data.token);
    localStorage.setItem('user', JSON.stringify(data.user));
    setUser(data.user);
    if (data.user.dark_mode) document.body.classList.add('dark-mode');
  };

  const handleLogout = () => {
    localStorage.removeItem('token');
    localStorage.removeItem('user');
    setUser(null);
    setPage('dashboard');
    document.body.classList.remove('dark-mode');
  };

  const handleUpdateUser = (updatedUser) => {
    setUser(updatedUser);
    localStorage.setItem('user', JSON.stringify(updatedUser));
  };

  const handleRefresh = () => {
    setRefreshing(true);
    setRefreshKey(prev => prev + 1);
    setTimeout(() => {
      setRefreshing(false);
    }, 600);
  };

  if (!user) {
    return <LoginPage onLogin={handleLogin} onSwitch={() => {}} />;
  }

  const titles = { charges: 'Charges', accounts: 'Comptes', calendar: 'Calendrier', bilan: 'Bilan', settings: 'Réglages' };
  const navItems = [
    { id: 'dashboard', icon: 'budget', label: 'Budget' },
    { id: 'charges', icon: 'charges', label: 'Charges' },
    { id: 'accounts', icon: 'accounts', label: 'Comptes' },
    { id: 'bilan', icon: 'bilan', label: 'Bilan' }
  ];
  const navButton = (item) => (
    <button
      key={item.id}
      className={`nav-item ${page === item.id ? 'active' : ''}`}
      onClick={() => setPage(item.id)}
      aria-current={page === item.id ? 'page' : undefined}
    >
      <Icon name={item.icon} size={24} />
      {item.label}
    </button>
  );
  const initial = (user.username || '?').trim().charAt(0).toUpperCase();

  return (
    <div className="app-layout">
      <header className="app-header">
        {page === 'dashboard' ? (
          <div className="month-switch">
            <button className="icon-btn" onClick={() => setDashMonth(m => changeMonth(m, -1))} aria-label="Mois précédent"><Icon name="chevronLeft" /></button>
            <button className="month-name" onClick={() => setShowMonthPicker(true)} aria-label={`Choisir un mois (actuellement ${getMonthLabel(dashMonth)})`}>
              {getMonthLabel(dashMonth)}<Icon name="chevronDown" size={16} />
            </button>
            <button className="icon-btn" onClick={() => setDashMonth(m => changeMonth(m, 1))} aria-label="Mois suivant"><Icon name="chevronRight" /></button>
          </div>
        ) : (
          <h1 className="header-title">{titles[page]}</h1>
        )}
        <div className="header-actions">
          <button className={`refresh-btn ${refreshing ? 'spinning' : ''}`} onClick={handleRefresh} title="Actualiser" aria-label="Actualiser">
            <RefreshIcon />
          </button>
          <button className={`avatar-btn ${page === 'settings' ? 'is-active' : ''}`} onClick={() => setPage('settings')} title="Réglages" aria-label="Réglages">
            {initial}
          </button>
        </div>
      </header>

      <main className="app-content">
        {page === 'dashboard' && <DashboardPage key={refreshKey} month={dashMonth} setMonth={setDashMonth} />}
        {page === 'charges' && <ChargesPage key={refreshKey} />}
        {page === 'accounts' && <BankAccountsPage key={refreshKey} />}
        {page === 'calendar' && <CalendarPage key={refreshKey} />}
        {page === 'bilan' && <BilanPage key={refreshKey} onOpenMonth={(m) => { setDashMonth(m); setPage('dashboard'); }} />}
        {page === 'settings' && <SettingsPage key={refreshKey} user={user} onLogout={handleLogout} onUpdateUser={handleUpdateUser} />}
      </main>

      <nav className="bottom-nav">
        {navItems.slice(0, 2).map(navButton)}
        <button className="nav-add" onClick={() => setShowAdd(true)} aria-label="Ajouter une dépense"><Icon name="plus" size={26} /></button>
        {navItems.slice(2).map(navButton)}
      </nav>

      {showMonthPicker && (
        <MonthPickerSheet
          value={dashMonth}
          onPick={(m) => { setDashMonth(m); setShowMonthPicker(false); }}
          onClose={() => setShowMonthPicker(false)}
        />
      )}

      {showAdd && (
        <QuickAddSheet
          month={page === 'dashboard' ? dashMonth : getCurrentMonth()}
          onClose={() => setShowAdd(false)}
          onAdded={() => { setShowAdd(false); setRefreshKey(k => k + 1); }}
        />
      )}
    </div>
  );
}

// ============================================
// RENDER
// ============================================
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

// Service Worker Registration
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').catch(() => {});
}
  
