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

// ---------- Iconos SVG inline (sustituyen a lucide-react para no depender de un CDN externo) ----------
function makeIcon(paths) {
  return function Icon({ size = 20, className = '', style = {} }) {
    return (
      <svg
        width={size} height={size} viewBox="0 0 24 24"
        fill="none" stroke="currentColor" strokeWidth="2"
        strokeLinecap="round" strokeLinejoin="round"
        className={className} style={style}
      >
        {paths}
      </svg>
    );
  };
}

const PlusCircle = makeIcon(<>
  <circle cx="12" cy="12" r="10" />
  <line x1="12" y1="8" x2="12" y2="16" />
  <line x1="8" y1="12" x2="16" y2="12" />
</>);

const Users = makeIcon(<>
  <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
  <circle cx="9" cy="7" r="4" />
  <path d="M23 21v-2a4 4 0 0 0-3-3.87" />
  <path d="M16 3.13a4 4 0 0 1 0 7.75" />
</>);

const Wallet = makeIcon(<>
  <path d="M21 12V7H5a2 2 0 0 1 0-4h14v4" />
  <path d="M3 5v14a2 2 0 0 0 2 2h16v-5" />
  <path d="M18 12a2 2 0 0 0 0 4h4v-4Z" />
</>);

const PartyPopper = makeIcon(<>
  <path d="M5.8 11.3 2 22l10.7-3.79" />
  <path d="M4 3h.01" /><path d="M22 8h.01" /><path d="M15 2h.01" /><path d="M22 20h.01" />
  <path d="m22 2-2.24.75a2.9 2.9 0 0 0-1.96 3.12c.1.86-.57 1.63-1.45 1.63h-.38c-.86 0-1.6.6-1.76 1.44L14 9" />
  <path d="m22 13-.82-.33c-.86-.34-1.82.2-1.98 1.11c-.11.7-.72 1.22-1.43 1.22H17" />
  <path d="m11 2 .33.82c.34.86-.2 1.82-1.11 1.98C9.52 4.91 9 5.52 9 6.23V7" />
</>);

const TrendingUp = makeIcon(<>
  <polyline points="22 7 13.5 15.5 8.5 10.5 2 17" />
  <polyline points="16 7 22 7 22 13" />
</>);

const TrendingDown = makeIcon(<>
  <polyline points="22 17 13.5 8.5 8.5 13.5 2 7" />
  <polyline points="16 17 22 17 22 11" />
</>);

const Trash2 = makeIcon(<>
  <polyline points="3 6 5 6 21 6" />
  <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
  <line x1="10" y1="11" x2="10" y2="17" />
  <line x1="14" y1="11" x2="14" y2="17" />
</>);

const X = makeIcon(<><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></>);

const Check = makeIcon(<polyline points="20 6 9 17 4 12" />);

const AlertCircle = makeIcon(<>
  <circle cx="12" cy="12" r="10" />
  <line x1="12" y1="8" x2="12" y2="12" />
  <line x1="12" y1="16" x2="12.01" y2="16" />
</>);

const Euro = makeIcon(<>
  <path d="M4 10h12" /><path d="M4 14h9" />
  <path d="M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12a7.9 7.9 0 0 0 7.8 8 7.7 7.7 0 0 0 5.2-2" />
</>);

const Calendar = makeIcon(<>
  <rect x="3" y="4" width="18" height="18" rx="2" />
  <line x1="16" y1="2" x2="16" y2="6" />
  <line x1="8" y1="2" x2="8" y2="6" />
  <line x1="3" y1="10" x2="21" y2="10" />
</>);

const ChevronRight = makeIcon(<polyline points="9 18 15 12 9 6" />);
const ChevronDown = makeIcon(<polyline points="6 9 12 15 18 9" />);

const Download = makeIcon(<>
  <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
  <polyline points="7 10 12 15 17 10" />
  <line x1="12" y1="15" x2="12" y2="3" />
</>);

const Copy = makeIcon(<>
  <rect x="9" y="9" width="13" height="13" rx="2" />
  <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</>);

const Receipt = makeIcon(<>
  <path d="M4 2v20l2-1 2 1 2-1 2 1 2-1 2 1 2-1 2 1V2l-2 1-2-1-2 1-2-1-2 1-2-1-2 1Z" />
  <path d="M8 7h8" /><path d="M8 11h8" /><path d="M8 15h5" />
</>);

const Upload = makeIcon(<>
  <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
  <polyline points="17 8 12 3 7 8" />
  <line x1="12" y1="3" x2="12" y2="15" />
</>);

const FileText = makeIcon(<>
  <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2Z" />
  <polyline points="14 2 14 8 20 8" />
  <line x1="16" y1="13" x2="8" y2="13" />
  <line x1="16" y1="17" x2="8" y2="17" />
</>);

const ImageIcon = makeIcon(<>
  <rect x="3" y="3" width="18" height="18" rx="2" />
  <circle cx="8.5" cy="8.5" r="1.5" />
  <polyline points="21 15 16 10 5 21" />
</>);

const Printer = makeIcon(<>
  <polyline points="6 9 6 2 18 2 18 9" />
  <path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" />
  <rect x="6" y="14" width="12" height="8" />
</>);

const Link2 = makeIcon(<>
  <path d="M9 17H7A5 5 0 0 1 7 7h2" />
  <path d="M15 7h2a5 5 0 1 1 0 10h-2" />
  <line x1="8" y1="12" x2="16" y2="12" />
</>);

const Pencil = makeIcon(<>
  <path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" />
</>);

// ---------- Utilidades ----------
const uid = () => Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
const fmt = (n) => (Number(n) || 0).toLocaleString('es-ES', { style: 'currency', currency: 'EUR' });
const today = () => new Date().toISOString().slice(0, 10);
const fmtDate = (d) => {
  if (!d) return '';
  const dt = new Date(d + 'T00:00:00');
  return dt.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' });
};

const CATS_INGRESO = ['Cuota socio', 'Donativo', 'Venta', 'Subvención', 'Otro ingreso'];
const CATS_GASTO = ['Material', 'Alquiler', 'Comida/Bebida', 'Transporte', 'Decoración', 'Música/Sonido', 'Otro gasto'];

// ---------- Exportación CSV (para Google Sheets) ----------
function csvEscape(val) {
  const s = String(val ?? '');
  if (s.includes(',') || s.includes('"') || s.includes('\n')) {
    return '"' + s.replace(/"/g, '""') + '"';
  }
  return s;
}

function rowsToCsv(rows) {
  return rows.map(r => r.map(csvEscape).join(',')).join('\n');
}

function buildExportSections(data) {
  const socioNombre = (id) => data.socios.find(s => s.id === id)?.nombre || '(desconocido)';
  const eventoNombre = (id) => data.eventos.find(e => e.id === id)?.nombre || '';

  const socios = [
    ['Número', 'Nombre', 'Tipo', 'Cuota anual (€)'],
    ...data.socios
      .slice()
      .sort((a, b) => (Number(a.numero) || 0) - (Number(b.numero) || 0))
      .map(s => [s.numero ?? '', s.nombre, s.tipo === 'infantil' ? 'Infantil' : 'Adulto', s.cuotaAnual]),
  ];

  const cuotas = [
    ['Socio', 'Año', 'Pagado', 'Fecha de pago', 'Importe (€)'],
    ...data.cuotas.map(c => [socioNombre(c.socioId), c.año, c.pagado ? 'Sí' : 'No', c.fecha || '', c.importe]),
  ];

  const movimientos = [
    ['Fecha', 'Tipo', 'Concepto', 'Categoría', 'Importe (€)', 'Evento'],
    ...data.movimientos
      .slice()
      .sort((a, b) => a.fecha.localeCompare(b.fecha))
      .map(m => [m.fecha, m.tipo === 'ingreso' ? 'Ingreso' : 'Gasto', m.concepto, m.categoria, m.importe, m.eventoId ? eventoNombre(m.eventoId) : '']),
  ];

  const eventos = [
    ['Nombre', 'Fecha', 'Ingresos (€)', 'Gastos (€)', 'Balance (€)', 'Notas'],
    ...data.eventos.map(ev => {
      const movs = data.movimientos.filter(m => m.eventoId === ev.id);
      const ingresos = movs.filter(m => m.tipo === 'ingreso').reduce((s, m) => s + Number(m.importe), 0);
      const gastos = movs.filter(m => m.tipo === 'gasto').reduce((s, m) => s + Number(m.importe), 0);
      return [ev.nombre, ev.fecha, ingresos, gastos, ingresos - gastos, ev.notas || ''];
    }),
  ];

  return { socios, cuotas, movimientos, eventos };
}

function buildFullCsv(data) {
  const { socios, cuotas, movimientos, eventos } = buildExportSections(data);
  const parts = [
    '=== SOCIOS ===', rowsToCsv(socios),
    '', '=== CUOTAS ===', rowsToCsv(cuotas),
    '', '=== MOVIMIENTOS ===', rowsToCsv(movimientos),
    '', '=== EVENTOS (balance) ===', rowsToCsv(eventos),
  ];
  return parts.join('\n');
}

const DEFAULT_DATA = {
  socios: [],
  cuotas: [],
  movimientos: [],
  eventos: [],
  facturas: [],
};

// ---------- Cliente API (backend Express + SQLite en el NAS) ----------
const API_BASE = '/api';

async function apiFetch(path, options = {}) {
  const res = await fetch(API_BASE + path, {
    credentials: 'include',
    headers: options.body && !(options.body instanceof FormData) ? { 'Content-Type': 'application/json' } : undefined,
    ...options,
  });
  if (res.status === 401) {
    const err = new Error('No autenticado');
    err.unauthorized = true;
    throw err;
  }
  if (!res.ok) {
    let msg = 'Error del servidor';
    try { msg = (await res.json()).error || msg; } catch (e) {}
    throw new Error(msg);
  }
  if (res.status === 204) return null;
  return res.json();
}

const api = {
  me: () => apiFetch('/auth/me'),
  login: (usuario, password) => apiFetch('/auth/login', { method: 'POST', body: JSON.stringify({ usuario, password }) }),
  logout: () => apiFetch('/auth/logout', { method: 'POST' }),
  cambiarPassword: (passwordActual, passwordNueva) => apiFetch('/auth/cambiar-password', { method: 'POST', body: JSON.stringify({ passwordActual, passwordNueva }) }),

  getData: () => apiFetch('/data'),

  crearSocio: (payload) => apiFetch('/socios', { method: 'POST', body: JSON.stringify(payload) }),
  editarSocio: (id, payload) => apiFetch(`/socios/${id}`, { method: 'PUT', body: JSON.stringify(payload) }),
  eliminarSocio: (id) => apiFetch(`/socios/${id}`, { method: 'DELETE' }),

  toggleCuota: (id) => apiFetch(`/cuotas/${id}/toggle`, { method: 'POST' }),
  editarFechaCuota: (id, fecha) => apiFetch(`/cuotas/${id}/fecha`, { method: 'PUT', body: JSON.stringify({ fecha }) }),

  crearMovimiento: (payload) => apiFetch('/movimientos', { method: 'POST', body: JSON.stringify(payload) }),
  eliminarMovimiento: (id) => apiFetch(`/movimientos/${id}`, { method: 'DELETE' }),

  crearEvento: (payload) => apiFetch('/eventos', { method: 'POST', body: JSON.stringify(payload) }),
  eliminarEvento: (id) => apiFetch(`/eventos/${id}`, { method: 'DELETE' }),

  subirFactura: (file, { movimientoId, nombreOriginal }) => {
    const fd = new FormData();
    fd.append('archivo', file);
    if (movimientoId) fd.append('movimientoId', movimientoId);
    if (nombreOriginal) fd.append('nombreOriginal', nombreOriginal);
    return apiFetch('/facturas', { method: 'POST', body: fd });
  },
  eliminarFactura: (id) => apiFetch(`/facturas/${id}`, { method: 'DELETE' }),
  urlFactura: (id) => `${API_BASE}/facturas/${id}/archivo`,

  listarUsuarios: () => apiFetch('/usuarios'),
  crearUsuario: (payload) => apiFetch('/usuarios', { method: 'POST', body: JSON.stringify(payload) }),
  eliminarUsuario: (id) => apiFetch(`/usuarios/${id}`, { method: 'DELETE' }),
};

// Hook principal: carga los datos del backend y expone un "refresh" para recargarlos
// tras cualquier acción (crear, editar, borrar...). Al ser multiusuario, no hay estado
// local que mutar directamente: cada acción llama a la API y luego se refresca.
function useStore() {
  const [data, setData] = useState(DEFAULT_DATA);
  const [loaded, setLoaded] = useState(false);
  const [error, setError] = useState(null);

  const refresh = useCallback(async () => {
    try {
      const next = await api.getData();
      setData({ ...DEFAULT_DATA, ...next });
      setError(null);
    } catch (e) {
      if (!e.unauthorized) setError(e.message);
      throw e;
    } finally {
      setLoaded(true);
    }
  }, []);

  useEffect(() => { refresh().catch(() => {}); }, [refresh]);

  return [data, refresh, loaded, error];
}


// ---------- UI helpers ----------
function Card({ children, className = '' }) {
  return <div className={`bg-white rounded-2xl shadow-sm border border-stone-200 ${className}`}>{children}</div>;
}

function StatCard({ icon: Icon, label, value, tone = 'stone' }) {
  const tones = {
    green: 'text-emerald-600 bg-emerald-50',
    red: 'text-rose-600 bg-rose-50',
    blue: 'text-sky-600 bg-sky-50',
    stone: 'text-stone-600 bg-stone-100',
  };
  return (
    <Card className="p-4 flex items-center gap-3">
      <div className={`p-2.5 rounded-xl ${tones[tone]}`}>
        <Icon size={20} />
      </div>
      <div className="min-w-0">
        <p className="text-xs text-stone-500 font-medium truncate">{label}</p>
        <p className="text-lg font-bold text-stone-800 truncate">{value}</p>
      </div>
    </Card>
  );
}

function Modal({ open, onClose, title, children }) {
  if (!open) return null;
  return (
    <div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/40 backdrop-blur-sm p-0 sm:p-4">
      <div className="bg-white w-full sm:max-w-md rounded-t-3xl sm:rounded-2xl shadow-xl max-h-[90vh] overflow-y-auto">
        <div className="flex items-center justify-between px-5 py-4 border-b border-stone-100 sticky top-0 bg-white rounded-t-3xl sm:rounded-t-2xl">
          <h3 className="font-bold text-stone-800">{title}</h3>
          <button onClick={onClose} className="p-1.5 rounded-full hover:bg-stone-100 text-stone-500">
            <X size={18} />
          </button>
        </div>
        <div className="p-5">{children}</div>
      </div>
    </div>
  );
}

function Field({ label, children }) {
  return (
    <label className="block mb-3">
      <span className="block text-xs font-semibold text-stone-500 mb-1">{label}</span>
      {children}
    </label>
  );
}

const inputCls = "w-full px-3 py-2.5 rounded-xl border border-stone-200 bg-stone-50 text-stone-800 text-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent";

function Button({ children, onClick, variant = 'primary', className = '', type = 'button', disabled }) {
  const variants = {
    primary: 'bg-green-600 hover:bg-green-700 text-white',
    ghost: 'bg-stone-100 hover:bg-stone-200 text-stone-700',
    danger: 'bg-rose-50 hover:bg-rose-100 text-rose-600',
  };
  return (
    <button
      type={type}
      disabled={disabled}
      onClick={onClick}
      className={`px-4 py-2.5 rounded-xl font-semibold text-sm transition-colors disabled:opacity-50 ${variants[variant]} ${className}`}
    >
      {children}
    </button>
  );
}

function Pill({ children, tone = 'stone' }) {
  const tones = {
    green: 'bg-green-50 text-green-700',
    red: 'bg-rose-50 text-rose-700',
    amber: 'bg-green-100 text-green-800',
    stone: 'bg-stone-100 text-stone-600',
  };
  return <span className={`px-2 py-0.5 rounded-full text-xs font-semibold ${tones[tone]}`}>{children}</span>;
}

function EmptyState({ icon: Icon, text }) {
  return (
    <div className="flex flex-col items-center justify-center py-10 text-stone-400">
      <Icon size={32} className="mb-2" />
      <p className="text-sm font-medium">{text}</p>
    </div>
  );
}

// ---------- Pantalla de login ----------
function LoginScreen({ onLoggedIn }) {
  const [usuario, setUsuario] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!usuario || !password) return;
    setLoading(true);
    setError('');
    try {
      const user = await api.login(usuario, password);
      onLoggedIn(user);
    } catch (err) {
      setError(err.message || 'Error al iniciar sesión');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="min-h-screen bg-stone-50 flex items-center justify-center p-5" style={{ fontFamily: 'system-ui, -apple-system, sans-serif' }}>
      <div className="w-full max-w-sm">
        <div className="text-center mb-6">
          <div className="w-14 h-14 rounded-2xl bg-gradient-to-br from-green-700 to-green-500 flex items-center justify-center mx-auto mb-3">
            <PartyPopper size={26} className="text-white" />
          </div>
          <h1 className="text-lg font-bold text-stone-800">Contabilidad de la peña</h1>
          <p className="text-xs text-stone-400">Inicia sesión para continuar</p>
        </div>
        <form onSubmit={handleSubmit} className="bg-white rounded-2xl shadow-sm border border-stone-200 p-5">
          <Field label="Usuario">
            <input className={inputCls} value={usuario} onChange={e => setUsuario(e.target.value)} autoComplete="username" />
          </Field>
          <Field label="Contraseña">
            <input type="password" className={inputCls} value={password} onChange={e => setPassword(e.target.value)} autoComplete="current-password" />
          </Field>
          {error && <p className="text-xs text-rose-600 font-semibold mb-3">{error}</p>}
          <Button type="submit" disabled={loading} className="w-full">
            {loading ? 'Entrando...' : 'Entrar'}
          </Button>
        </form>
      </div>
    </div>
  );
}

// ---------- App ----------
function App() {
  const [user, setUser] = useState(null);
  const [checkingSession, setCheckingSession] = useState(true);
  const [data, refresh, loaded] = useStore();
  const [tab, setTab] = useState('resumen');
  const [exportOpen, setExportOpen] = useState(false);
  const [accountOpen, setAccountOpen] = useState(false);

  useEffect(() => {
    api.me().then(setUser).catch(() => setUser(null)).finally(() => setCheckingSession(false));
  }, []);

  const handleLogout = async () => {
    await api.logout().catch(() => {});
    setUser(null);
  };

  // ----- Cálculos derivados -----
  const totales = useMemo(() => {
    // Las cuotas pagadas ya generan su propio movimiento de ingreso (ver toggleCuota),
    // así que el saldo se calcula solo a partir de los movimientos para no contar dos veces.
    const ingresos = data.movimientos.filter(m => m.tipo === 'ingreso').reduce((s, m) => s + Number(m.importe), 0);
    const gastos = data.movimientos.filter(m => m.tipo === 'gasto').reduce((s, m) => s + Number(m.importe), 0);
    const saldo = ingresos - gastos;
    return { ingresos, gastos, saldo };
  }, [data]);

  const currentYear = new Date().getFullYear();

  const cuotasStats = useMemo(() => {
    const delAño = data.cuotas.filter(c => c.año === currentYear);
    const pagadas = delAño.filter(c => c.pagado).length;
    const pendientes = delAño.filter(c => !c.pagado).length;
    return { pagadas, pendientes, total: delAño.length };
  }, [data.cuotas, currentYear]);

  const balancesEventos = useMemo(() => {
    return data.eventos.map(ev => {
      const movs = data.movimientos.filter(m => m.eventoId === ev.id);
      const ingresos = movs.filter(m => m.tipo === 'ingreso').reduce((s, m) => s + Number(m.importe), 0);
      const gastos = movs.filter(m => m.tipo === 'gasto').reduce((s, m) => s + Number(m.importe), 0);
      return { ...ev, ingresos, gastos, balance: ingresos - gastos, numMovs: movs.length };
    });
  }, [data.eventos, data.movimientos]);

  if (checkingSession) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-stone-50">
        <p className="text-stone-400 text-sm">Comprobando sesión...</p>
      </div>
    );
  }

  if (!user) {
    return <LoginScreen onLoggedIn={(u) => { setUser(u); refresh().catch(() => {}); }} />;
  }

  if (!loaded) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-stone-50">
        <p className="text-stone-400 text-sm">Cargando datos...</p>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-stone-50 pb-20" style={{ fontFamily: 'system-ui, -apple-system, sans-serif' }}>
      {/* Header */}
      <div className="bg-gradient-to-br from-green-700 to-green-500 px-5 pt-6 pb-8 rounded-b-3xl shadow-sm relative">
        <div className="absolute top-6 right-5 flex items-center gap-2">
          <button
            onClick={() => setAccountOpen(true)}
            className="p-2 rounded-xl bg-white/15 hover:bg-white/25 text-white transition-colors"
            title={user.nombre}
          >
            <Users size={18} />
          </button>
          <button
            onClick={() => setExportOpen(true)}
            className="p-2 rounded-xl bg-white/15 hover:bg-white/25 text-white transition-colors"
            title="Exportar datos"
          >
            <Download size={18} />
          </button>
        </div>
        <div className="flex items-center gap-2 text-white/90 mb-1">
          <PartyPopper size={18} />
          <span className="text-xs font-semibold uppercase tracking-wide">Contabilidad de la peña</span>
        </div>
        <p className="text-3xl font-extrabold text-white">{fmt(totales.saldo)}</p>
        <p className="text-white/80 text-xs mt-1">Saldo actual en caja · {user.nombre}</p>
      </div>

      <div className="px-4 -mt-5">
        <div className="grid grid-cols-2 gap-3 mb-4">
          <StatCard icon={TrendingUp} label="Ingresos totales" value={fmt(totales.ingresos)} tone="green" />
          <StatCard icon={TrendingDown} label="Gastos totales" value={fmt(totales.gastos)} tone="red" />
        </div>

        {/* Tabs */}
        <div className="flex gap-1 bg-white rounded-xl p-1 mb-4 shadow-sm border border-stone-200 overflow-x-auto">
          {[
            { id: 'resumen', label: 'Resumen', icon: Wallet },
            { id: 'socios', label: 'Socios', icon: Users },
            { id: 'movimientos', label: 'Movimientos', icon: Euro },
            { id: 'eventos', label: 'Eventos', icon: PartyPopper },
            { id: 'facturas', label: 'Facturas', icon: Receipt },
          ].map(t => (
            <button
              key={t.id}
              onClick={() => setTab(t.id)}
              className={`flex-1 flex items-center justify-center gap-1.5 py-2 px-2 rounded-lg text-xs font-semibold whitespace-nowrap transition-colors ${
                tab === t.id ? 'bg-green-600 text-white' : 'text-stone-500 hover:bg-stone-50'
              }`}
            >
              <t.icon size={14} />
              {t.label}
            </button>
          ))}
        </div>

        {tab === 'resumen' && <ResumenTab data={data} totales={totales} cuotasStats={cuotasStats} balancesEventos={balancesEventos} currentYear={currentYear} />}
        {tab === 'socios' && <SociosTab data={data} refresh={refresh} currentYear={currentYear} />}
        {tab === 'movimientos' && <MovimientosTab data={data} refresh={refresh} />}
        {tab === 'eventos' && <EventosTab data={data} refresh={refresh} balancesEventos={balancesEventos} />}
        {tab === 'facturas' && <FacturasTab data={data} refresh={refresh} />}
      </div>

      <ExportModal open={exportOpen} onClose={() => setExportOpen(false)} data={data} user={user} refresh={refresh} />
      <AccountModal open={accountOpen} onClose={() => setAccountOpen(false)} user={user} onLogout={handleLogout} />
    </div>
  );
}

// ---------- Modal de cuenta (cambiar contraseña, cerrar sesión, gestión de usuarios) ----------
function AccountModal({ open, onClose, user, onLogout }) {
  const [passwordActual, setPasswordActual] = useState('');
  const [passwordNueva, setPasswordNueva] = useState('');
  const [msg, setMsg] = useState('');
  const [usuarios, setUsuarios] = useState(null);
  const [nuevoUsuario, setNuevoUsuario] = useState({ usuario: '', password: '', nombre: '', rol: 'directiva' });

  useEffect(() => {
    if (open && user.rol === 'admin') {
      api.listarUsuarios().then(setUsuarios).catch(() => {});
    }
  }, [open, user.rol]);

  const cambiarPassword = async () => {
    setMsg('');
    try {
      await api.cambiarPassword(passwordActual, passwordNueva);
      setMsg('✓ Contraseña actualizada.');
      setPasswordActual('');
      setPasswordNueva('');
    } catch (e) {
      setMsg('✗ ' + e.message);
    }
  };

  const crearUsuario = async () => {
    if (!nuevoUsuario.usuario || !nuevoUsuario.password || !nuevoUsuario.nombre) return;
    try {
      await api.crearUsuario(nuevoUsuario);
      setNuevoUsuario({ usuario: '', password: '', nombre: '', rol: 'directiva' });
      const list = await api.listarUsuarios();
      setUsuarios(list);
    } catch (e) {
      setMsg('✗ ' + e.message);
    }
  };

  const eliminarUsuario = async (id) => {
    await api.eliminarUsuario(id).catch(() => {});
    const list = await api.listarUsuarios();
    setUsuarios(list);
  };

  return (
    <Modal open={open} onClose={onClose} title={`Cuenta de ${user.nombre}`}>
      <div className="mb-4">
        <p className="text-xs text-stone-500 mb-3">Conectado como <span className="font-semibold text-stone-700">{user.usuario}</span> ({user.rol === 'admin' ? 'administrador' : 'directiva'})</p>
        <Field label="Contraseña actual">
          <input type="password" className={inputCls} value={passwordActual} onChange={e => setPasswordActual(e.target.value)} />
        </Field>
        <Field label="Nueva contraseña">
          <input type="password" className={inputCls} value={passwordNueva} onChange={e => setPasswordNueva(e.target.value)} />
        </Field>
        {msg && <p className="text-xs font-semibold mb-2 text-stone-600">{msg}</p>}
        <Button onClick={cambiarPassword} variant="ghost" className="w-full mb-2">Cambiar contraseña</Button>
        <Button onClick={onLogout} variant="danger" className="w-full">Cerrar sesión</Button>
      </div>

      {user.rol === 'admin' && (
        <div className="border-t border-stone-100 pt-4">
          <h4 className="font-bold text-stone-800 text-sm mb-3">Usuarios de la directiva</h4>
          {usuarios === null ? (
            <p className="text-xs text-stone-400">Cargando...</p>
          ) : (
            <div className="space-y-2 mb-4">
              {usuarios.map(u => (
                <div key={u.id} className="flex items-center justify-between text-sm">
                  <div>
                    <p className="font-semibold text-stone-700">{u.nombre}</p>
                    <p className="text-xs text-stone-400">{u.usuario} · {u.rol}</p>
                  </div>
                  {u.id !== user.id && (
                    <button onClick={() => eliminarUsuario(u.id)} className="p-1.5 text-stone-300 hover:text-rose-500">
                      <Trash2 size={15} />
                    </button>
                  )}
                </div>
              ))}
            </div>
          )}
          <p className="text-xs font-semibold text-stone-500 mb-2">Añadir nuevo usuario</p>
          <Field label="Nombre">
            <input className={inputCls} value={nuevoUsuario.nombre} onChange={e => setNuevoUsuario({ ...nuevoUsuario, nombre: e.target.value })} />
          </Field>
          <Field label="Usuario (para entrar)">
            <input className={inputCls} value={nuevoUsuario.usuario} onChange={e => setNuevoUsuario({ ...nuevoUsuario, usuario: e.target.value })} />
          </Field>
          <Field label="Contraseña inicial">
            <input type="password" className={inputCls} value={nuevoUsuario.password} onChange={e => setNuevoUsuario({ ...nuevoUsuario, password: e.target.value })} />
          </Field>
          <Field label="Rol">
            <select className={inputCls} value={nuevoUsuario.rol} onChange={e => setNuevoUsuario({ ...nuevoUsuario, rol: e.target.value })}>
              <option value="directiva">Directiva</option>
              <option value="admin">Administrador</option>
            </select>
          </Field>
          <Button onClick={crearUsuario} className="w-full">Crear usuario</Button>
        </div>
      )}
    </Modal>
  );
}

// ---------- Modal de exportación ----------
function ExportModal({ open, onClose, data, user, refresh }) {
  const [copied, setCopied] = useState(false);
  const [copiedBackup, setCopiedBackup] = useState(false);
  const [importMsg, setImportMsg] = useState(null); // { ok, texto }
  const [importing, setImporting] = useState(false);
  const fileInputRef = React.useRef(null);
  const csv = useMemo(() => (open ? buildFullCsv(data) : ''), [open, data]);

  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(csv);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch (e) {
      // fallback: seleccionar el texto para copia manual
    }
  };

  const handleDownload = () => {
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `contabilidad-pena-${today()}.csv`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const handleBackupDownload = () => {
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `copia-seguridad-pena-${today()}.json`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const handleCopyBackupForDrive = async () => {
    const payload = JSON.stringify(data, null, 2);
    try {
      await navigator.clipboard.writeText(payload);
      setCopiedBackup(true);
      setTimeout(() => setCopiedBackup(false), 3000);
    } catch (e) {
      // el textarea de abajo sirve como respaldo para copiar a mano
    }
  };

  const handleImportFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    setImporting(true);
    setImportMsg(null);
    const reader = new FileReader();
    reader.onload = async () => {
      try {
        const parsed = JSON.parse(reader.result);
        const res = await apiFetch('/import', { method: 'POST', body: JSON.stringify(parsed) });
        await refresh();
        const r = res.resumen;
        let texto = `✓ Importado: ${r.socios} socios, ${r.cuotas} cuotas, ${r.movimientos} movimientos, ${r.eventos} eventos.`;
        if (r.errores?.length) texto += ` (${r.errores.length} filas con error, revisadas y omitidas)`;
        setImportMsg({ ok: true, texto });
      } catch (err) {
        setImportMsg({ ok: false, texto: '✗ ' + err.message });
      } finally {
        setImporting(false);
      }
    };
    reader.readAsText(file);
    e.target.value = '';
  };

  return (
    <Modal open={open} onClose={onClose} title="Exportar y copia de seguridad">
      <p className="text-sm text-stone-600 mb-3">
        Los datos se guardan de forma centralizada en el servidor del NAS. Aun así, es buena idea
        descargar una copia de vez en cuando (o pedir al administrador que respalde el archivo de la
        base de datos en el propio NAS).
      </p>

      <div className="flex gap-2 mb-4">
        <Button onClick={handleBackupDownload} className="flex-1 flex items-center justify-center gap-2">
          <Download size={15} /> Copia de seguridad (.json)
        </Button>
      </div>

      {user?.rol === 'admin' && (
        <div className="bg-stone-50 border border-stone-200 rounded-xl p-3 mb-4">
          <p className="text-xs text-stone-600 mb-2">
            <span className="font-bold">Importar copia de seguridad:</span> sube un archivo .json
            (de esta misma app, del HTML de escritorio o de la versión de Claude.ai) para cargar sus
            socios, cuotas, movimientos y eventos en esta base de datos. Los registros con el mismo id
            se sobrescriben; el resto se añade.
          </p>
          <Button onClick={() => fileInputRef.current?.click()} variant="ghost" disabled={importing} className="w-full flex items-center justify-center gap-2">
            <Upload size={15} /> {importing ? 'Importando...' : 'Importar copia de seguridad (.json)'}
          </Button>
          <input ref={fileInputRef} type="file" accept="application/json" onChange={handleImportFile} className="hidden" />
          {importMsg && (
            <p className={`text-xs font-semibold text-center mt-2 ${importMsg.ok ? 'text-green-700' : 'text-rose-600'}`}>
              {importMsg.texto}
            </p>
          )}
        </div>
      )}

      <div className="bg-green-50 border border-green-200 rounded-xl p-3 mb-4">
        <p className="text-xs text-green-800 mb-2">
          <span className="font-bold">Guardar también en Google Drive:</span> pulsa para copiar los datos, pégalos
          aquí en el chat con Claude y pídele "sube esto a Drive como copia de seguridad".
        </p>
        <Button onClick={handleCopyBackupForDrive} className="w-full flex items-center justify-center gap-2">
          {copiedBackup ? <Check size={15} /> : <Copy size={15} />} {copiedBackup ? 'Copiado, pégalo en el chat' : 'Copiar copia de seguridad'}
        </Button>
      </div>

      <div className="border-t border-stone-100 pt-4 mt-2">
        <p className="text-sm text-stone-600 mb-3">
          Para exportar a Google Sheets: descarga o copia el texto y pégaselo a Claude pidiendo
          <span className="font-semibold"> "sube esto a una Google Sheet"</span>, o impórtalo tú con
          <span className="font-semibold"> Archivo → Importar</span> en Sheets.
        </p>
        <div className="flex gap-2 mb-3">
          <Button onClick={handleDownload} variant="ghost" className="flex-1 flex items-center justify-center gap-2">
            <Download size={15} /> Descargar CSV
          </Button>
          <Button onClick={handleCopy} variant="ghost" className="flex-1 flex items-center justify-center gap-2">
            {copied ? <Check size={15} /> : <Copy size={15} />} {copied ? 'Copiado' : 'Copiar texto'}
          </Button>
        </div>
        <textarea
          readOnly
          value={csv}
          className="w-full h-32 text-xs font-mono p-2.5 rounded-xl border border-stone-200 bg-stone-50 text-stone-600"
          onFocus={e => e.target.select()}
        />
      </div>
    </Modal>
  );
}

// ---------- Tab Resumen ----------
function ResumenTab({ data, totales, cuotasStats, balancesEventos, currentYear }) {
  const ultimosMovs = [...data.movimientos].sort((a, b) => b.fecha.localeCompare(a.fecha)).slice(0, 5);

  return (
    <div className="space-y-4">
      <Card className="p-4">
        <div className="flex items-center justify-between mb-3">
          <h3 className="font-bold text-stone-800 text-sm">Cuotas {currentYear}</h3>
          <Pill tone={cuotasStats.pendientes > 0 ? 'amber' : 'green'}>
            {cuotasStats.pagadas}/{cuotasStats.total} pagadas
          </Pill>
        </div>
        {cuotasStats.total === 0 ? (
          <p className="text-xs text-stone-400">Aún no hay socios con cuotas registradas este año.</p>
        ) : (
          <div className="w-full bg-stone-100 rounded-full h-2.5 overflow-hidden">
            <div
              className="bg-emerald-500 h-full rounded-full transition-all"
              style={{ width: `${(cuotasStats.pagadas / cuotasStats.total) * 100}%` }}
            />
          </div>
        )}
        {cuotasStats.pendientes > 0 && (
          <p className="text-xs text-green-700 mt-2 font-medium flex items-center gap-1">
            <AlertCircle size={12} /> {cuotasStats.pendientes} socio(s) con cuota pendiente
          </p>
        )}
      </Card>

      {balancesEventos.length > 0 && (
        <Card className="p-4">
          <h3 className="font-bold text-stone-800 text-sm mb-3">Balance por evento</h3>
          <div className="space-y-2">
            {balancesEventos.map(ev => (
              <div key={ev.id} className="flex items-center justify-between text-sm">
                <span className="text-stone-600 truncate flex-1">{ev.nombre}</span>
                <span className={`font-bold ${ev.balance >= 0 ? 'text-emerald-600' : 'text-rose-600'}`}>
                  {fmt(ev.balance)}
                </span>
              </div>
            ))}
          </div>
        </Card>
      )}

      <Card className="p-4">
        <h3 className="font-bold text-stone-800 text-sm mb-3">Últimos movimientos</h3>
        {ultimosMovs.length === 0 ? (
          <EmptyState icon={Euro} text="Todavía no hay movimientos" />
        ) : (
          <div className="space-y-2">
            {ultimosMovs.map(m => (
              <div key={m.id} className="flex items-center justify-between text-sm py-1.5 border-b border-stone-50 last:border-0">
                <div className="min-w-0 flex-1">
                  <p className="text-stone-700 font-medium truncate">{m.concepto}</p>
                  <p className="text-stone-400 text-xs">{fmtDate(m.fecha)}</p>
                </div>
                <span className={`font-bold ml-2 ${m.tipo === 'ingreso' ? 'text-emerald-600' : 'text-rose-600'}`}>
                  {m.tipo === 'ingreso' ? '+' : '-'}{fmt(m.importe)}
                </span>
              </div>
            ))}
          </div>
        )}
      </Card>
    </div>
  );
}

// ---------- Tab Socios ----------
function SociosTab({ data, refresh, currentYear }) {
  const [modalOpen, setModalOpen] = useState(false);
  const [reciboSocio, setReciboSocio] = useState(null); // { socio, cuota }
  const [editSocio, setEditSocio] = useState(null);
  const [orden, setOrden] = useState('numero'); // 'numero' | 'nombre'
  const [form, setForm] = useState({ numero: '', nombre: '', cuotaAnual: '', tipo: 'adulto' });
  const [error, setError] = useState('');

  const siguienteNumero = useMemo(() => {
    const max = data.socios.reduce((m, s) => Math.max(m, Number(s.numero) || 0), 0);
    return max + 1;
  }, [data.socios]);

  const openAddModal = () => {
    setForm({ numero: String(siguienteNumero), nombre: '', cuotaAnual: '', tipo: 'adulto' });
    setError('');
    setModalOpen(true);
  };

  const addSocio = async () => {
    if (!form.nombre.trim()) return;
    try {
      await api.crearSocio({
        numero: form.numero ? Number(form.numero) : siguienteNumero,
        nombre: form.nombre.trim(),
        cuotaAnual: Number(form.cuotaAnual) || 0,
        tipo: form.tipo === 'infantil' ? 'infantil' : 'adulto',
      });
      await refresh();
      setForm({ numero: '', nombre: '', cuotaAnual: '', tipo: 'adulto' });
      setModalOpen(false);
    } catch (e) {
      setError(e.message);
    }
  };

  const openEditModal = (s) => {
    setEditSocio({
      id: s.id,
      numero: String(s.numero ?? ''),
      nombre: s.nombre,
      cuotaAnual: String(s.cuotaAnual ?? ''),
      tipo: s.tipo === 'infantil' ? 'infantil' : 'adulto',
    });
    setError('');
  };

  const saveEditSocio = async () => {
    if (!editSocio || !editSocio.nombre.trim()) return;
    try {
      await api.editarSocio(editSocio.id, {
        numero: Number(editSocio.numero) || 0,
        nombre: editSocio.nombre.trim(),
        cuotaAnual: Number(editSocio.cuotaAnual) || 0,
        tipo: editSocio.tipo === 'infantil' ? 'infantil' : 'adulto',
      });
      await refresh();
      setEditSocio(null);
    } catch (e) {
      setError(e.message);
    }
  };

  const sociosOrdenados = useMemo(() => {
    const arr = [...data.socios];
    if (orden === 'numero') {
      arr.sort((a, b) => (Number(a.numero) || 0) - (Number(b.numero) || 0));
    } else {
      arr.sort((a, b) => a.nombre.localeCompare(b.nombre, 'es'));
    }
    return arr;
  }, [data.socios, orden]);

  const toggleCuota = async (socio) => {
    const cuotaActual = data.cuotas.find(c => c.socioId === socio.id && c.año === currentYear);
    if (!cuotaActual) return;
    const seVaAPagar = !cuotaActual.pagado;
    const actualizada = await api.toggleCuota(cuotaActual.id);
    await refresh();
    if (seVaAPagar) {
      setReciboSocio({ socio, cuotaId: actualizada.id, importe: actualizada.importe, fecha: actualizada.fecha, año: currentYear });
    }
  };

  const updateFechaCuota = async (cuotaId, nuevaFecha) => {
    await api.editarFechaCuota(cuotaId, nuevaFecha);
    await refresh();
    setReciboSocio(r => r && r.cuotaId === cuotaId ? { ...r, fecha: nuevaFecha } : r);
  };

  const deleteSocio = async (id) => {
    await api.eliminarSocio(id);
    await refresh();
  };

  const getCuota = (socioId) => data.cuotas.find(c => c.socioId === socioId && c.año === currentYear);

  return (
    <div>
      <Button onClick={openAddModal} className="w-full mb-3 flex items-center justify-center gap-2">
        <PlusCircle size={16} /> Añadir socio
      </Button>

      {data.socios.length > 1 && (
        <div className="flex gap-2 mb-4">
          {[
            { id: 'numero', label: 'Ordenar por número' },
            { id: 'nombre', label: 'Ordenar por nombre' },
          ].map(o => (
            <button
              key={o.id}
              onClick={() => setOrden(o.id)}
              className={`px-3 py-1.5 rounded-lg text-xs font-semibold ${orden === o.id ? 'bg-stone-800 text-white' : 'bg-white text-stone-500 border border-stone-200'}`}
            >
              {o.label}
            </button>
          ))}
        </div>
      )}

      {data.socios.length === 0 ? (
        <Card className="p-4"><EmptyState icon={Users} text="Todavía no hay socios registrados" /></Card>
      ) : (
        <div className="space-y-2">
          {sociosOrdenados.map(s => {
            const cuota = getCuota(s.id);
            return (
              <Card key={s.id} className="p-4 flex items-center justify-between">
                <button onClick={() => openEditModal(s)} className="flex items-center gap-3 min-w-0 flex-1 text-left">
                  <div className="shrink-0 w-9 h-9 rounded-full bg-green-50 text-green-700 font-bold text-xs flex items-center justify-center">
                    {s.numero ?? '–'}
                  </div>
                  <div className="min-w-0 flex-1">
                    <p className="font-semibold text-stone-800 truncate">{s.nombre}</p>
                    <div className="flex items-center gap-1.5 mt-0.5">
                      <span className="text-xs text-stone-400">{fmt(s.cuotaAnual)}/año</span>
                      <Pill tone={s.tipo === 'infantil' ? 'amber' : 'stone'}>
                        {s.tipo === 'infantil' ? 'Infantil' : 'Adulto'}
                      </Pill>
                    </div>
                  </div>
                </button>
                <div className="flex items-center gap-2 shrink-0">
                  {cuota?.pagado && (
                    <button
                      onClick={() => setReciboSocio({ socio: s, cuotaId: cuota.id, importe: cuota.importe, fecha: cuota.fecha, año: currentYear })}
                      className="p-2 text-green-600 hover:bg-green-50 rounded-lg"
                      title="Ver recibo"
                    >
                      <Receipt size={16} />
                    </button>
                  )}
                  <button
                    onClick={() => toggleCuota(s)}
                    className={`px-3 py-1.5 rounded-lg text-xs font-bold flex items-center gap-1 ${
                      cuota?.pagado ? 'bg-green-600 text-white' : 'bg-white text-green-800 border border-green-200'
                    }`}
                  >
                    {cuota?.pagado ? <Check size={12} /> : <AlertCircle size={12} />}
                    {cuota?.pagado ? 'Pagada' : 'Pendiente'}
                  </button>
                  <button onClick={() => deleteSocio(s.id)} className="p-2 text-stone-300 hover:text-rose-500">
                    <Trash2 size={16} />
                  </button>
                </div>
              </Card>
            );
          })}
        </div>
      )}

      <Modal open={modalOpen} onClose={() => setModalOpen(false)} title="Nuevo socio">
        <Field label="Número de socio">
          <input type="number" className={inputCls} value={form.numero} onChange={e => setForm({ ...form, numero: e.target.value })} placeholder={String(siguienteNumero)} />
        </Field>
        <Field label="Nombre">
          <input className={inputCls} value={form.nombre} onChange={e => setForm({ ...form, nombre: e.target.value })} placeholder="Nombre del socio" />
        </Field>
        <Field label="Cuota anual (€)">
          <input type="number" className={inputCls} value={form.cuotaAnual} onChange={e => setForm({ ...form, cuotaAnual: e.target.value })} placeholder="0" />
        </Field>
        <Field label="Tipo de socio">
          <div className="flex gap-2">
            {[{ id: 'adulto', label: 'Adulto' }, { id: 'infantil', label: 'Infantil' }].map(t => (
              <button
                key={t.id}
                type="button"
                onClick={() => setForm({ ...form, tipo: t.id })}
                className={`flex-1 py-2 rounded-xl text-sm font-bold ${form.tipo === t.id ? 'bg-green-600 text-white' : 'bg-stone-100 text-stone-500'}`}
              >
                {t.label}
              </button>
            ))}
          </div>
        </Field>
        {error && <p className="text-xs text-rose-600 font-semibold mb-2">{error}</p>}
        <Button onClick={addSocio} className="w-full mt-2">Guardar socio</Button>
      </Modal>

      <Modal open={!!editSocio} onClose={() => setEditSocio(null)} title="Editar socio">
        {editSocio && (
          <>
            <Field label="Número de socio">
              <input
                type="number"
                className={inputCls}
                value={editSocio.numero}
                onChange={e => setEditSocio({ ...editSocio, numero: e.target.value })}
                placeholder="0"
              />
            </Field>
            <Field label="Nombre">
              <input
                className={inputCls}
                value={editSocio.nombre}
                onChange={e => setEditSocio({ ...editSocio, nombre: e.target.value })}
                placeholder="Nombre del socio"
              />
            </Field>
            <Field label="Cuota anual (€)">
              <input
                type="number"
                className={inputCls}
                value={editSocio.cuotaAnual}
                onChange={e => setEditSocio({ ...editSocio, cuotaAnual: e.target.value })}
                placeholder="0"
              />
            </Field>
            <Field label="Tipo de socio">
              <div className="flex gap-2">
                {[{ id: 'adulto', label: 'Adulto' }, { id: 'infantil', label: 'Infantil' }].map(t => (
                  <button
                    key={t.id}
                    type="button"
                    onClick={() => setEditSocio({ ...editSocio, tipo: t.id })}
                    className={`flex-1 py-2 rounded-xl text-sm font-bold ${editSocio.tipo === t.id ? 'bg-green-600 text-white' : 'bg-stone-100 text-stone-500'}`}
                  >
                    {t.label}
                  </button>
                ))}
              </div>
            </Field>
            <p className="text-xs text-stone-400 mb-3">
              Si cambias la cuota anual, se actualizará también la cuota pendiente de {currentYear} (si aún no está pagada).
            </p>
            {error && <p className="text-xs text-rose-600 font-semibold mb-2">{error}</p>}
            <Button onClick={saveEditSocio} className="w-full mt-2">Guardar cambios</Button>
          </>
        )}
      </Modal>

      <ReciboModal recibo={reciboSocio} onClose={() => setReciboSocio(null)} onUpdateFecha={updateFechaCuota} />
    </div>
  );
}

// ---------- Modal de recibo ----------
function ReciboModal({ recibo, onClose, onUpdateFecha }) {
  const [editandoFecha, setEditandoFecha] = useState(false);
  const [fechaTmp, setFechaTmp] = useState('');

  useEffect(() => {
    if (recibo) {
      setFechaTmp(recibo.fecha || today());
      setEditandoFecha(false);
    }
  }, [recibo?.cuotaId]);

  if (!recibo) return null;
  const { socio, importe, fecha, año, cuotaId } = recibo;
  const numRecibo = `${año}-${socio.id.slice(0, 6).toUpperCase()}`;

  const guardarFecha = () => {
    if (fechaTmp && onUpdateFecha) onUpdateFecha(cuotaId, fechaTmp);
    setEditandoFecha(false);
  };

  const handlePrint = () => {
    const w = window.open('', '_blank');
    w.document.write(`
      <html>
        <head>
          <title>Recibo ${numRecibo}</title>
          <style>
            body { font-family: system-ui, sans-serif; padding: 40px; color: #292524; }
            .box { max-width: 480px; margin: 0 auto; border: 2px solid #16a34a; border-radius: 16px; padding: 32px; }
            h1 { color: #15803d; font-size: 20px; margin-bottom: 4px; }
            .muted { color: #78716c; font-size: 13px; }
            .row { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid #f0f0f0; }
            .total { font-size: 24px; font-weight: bold; color: #15803d; text-align: right; margin-top: 16px; }
          </style>
        </head>
        <body>
          <div class="box">
            <h1>Recibo de pago de cuota</h1>
            <p class="muted">Nº ${numRecibo}</p>
            <div style="height:20px"></div>
            <div class="row"><span>Socio</span><b>${socio.nombre}</b></div>
            <div class="row"><span>Concepto</span><b>Cuota anual ${año}</b></div>
            <div class="row"><span>Fecha de pago</span><b>${fmtDate(fecha)}</b></div>
            <div class="total">${fmt(importe)}</div>
          </div>
        </body>
      </html>
    `);
    w.document.close();
    w.print();
  };

  return (
    <Modal open={!!recibo} onClose={onClose} title="Recibo generado">
      <div className="border-2 border-green-600 rounded-2xl p-5 mb-4">
        <p className="text-xs text-stone-400 mb-1">Nº {numRecibo}</p>
        <h4 className="font-bold text-green-700 mb-3">Recibo de pago de cuota</h4>
        <div className="space-y-1.5 text-sm">
          <div className="flex justify-between"><span className="text-stone-500">Socio</span><span className="font-semibold text-stone-800">{socio.nombre}</span></div>
          <div className="flex justify-between"><span className="text-stone-500">Concepto</span><span className="font-semibold text-stone-800">Cuota anual {año}</span></div>
          <div className="flex justify-between items-center">
            <span className="text-stone-500">Fecha de pago</span>
            {editandoFecha ? (
              <div className="flex items-center gap-1.5">
                <input
                  type="date"
                  value={fechaTmp}
                  onChange={e => setFechaTmp(e.target.value)}
                  className="px-2 py-1 rounded-lg border border-stone-200 text-xs"
                />
                <button onClick={guardarFecha} className="p-1.5 rounded-lg bg-green-600 text-white"><Check size={12} /></button>
              </div>
            ) : (
              <button onClick={() => setEditandoFecha(true)} className="font-semibold text-stone-800 flex items-center gap-1 hover:text-green-700">
                {fmtDate(fecha)} <Pencil size={11} className="text-stone-400" />
              </button>
            )}
          </div>
        </div>
        <p className="text-right text-2xl font-extrabold text-green-700 mt-3">{fmt(importe)}</p>
      </div>
      <Button onClick={handlePrint} className="w-full flex items-center justify-center gap-2">
        <Printer size={15} /> Imprimir / Guardar como PDF
      </Button>
    </Modal>
  );
}

// ---------- Tab Movimientos ----------
function MovimientosTab({ data, refresh }) {
  const [modalOpen, setModalOpen] = useState(false);
  const [filtro, setFiltro] = useState('todos');
  const [error, setError] = useState('');
  const [form, setForm] = useState({
    tipo: 'ingreso', concepto: '', categoria: CATS_INGRESO[0], importe: '', fecha: today(), eventoId: '',
  });

  const addMovimiento = async () => {
    if (!form.concepto.trim() || !form.importe) return;
    try {
      await api.crearMovimiento({
        tipo: form.tipo,
        concepto: form.concepto.trim(),
        categoria: form.categoria,
        importe: Number(form.importe),
        fecha: form.fecha,
        eventoId: form.eventoId || null,
      });
      await refresh();
      setForm({ tipo: 'ingreso', concepto: '', categoria: CATS_INGRESO[0], importe: '', fecha: today(), eventoId: '' });
      setModalOpen(false);
    } catch (e) {
      setError(e.message);
    }
  };

  const deleteMov = async (id) => {
    await api.eliminarMovimiento(id);
    await refresh();
  };

  const lista = useMemo(() => {
    let arr = [...data.movimientos].sort((a, b) => b.fecha.localeCompare(a.fecha));
    if (filtro !== 'todos') arr = arr.filter(m => m.tipo === filtro);
    return arr;
  }, [data.movimientos, filtro]);

  return (
    <div>
      <Button onClick={() => setModalOpen(true)} className="w-full mb-3 flex items-center justify-center gap-2">
        <PlusCircle size={16} /> Añadir movimiento
      </Button>

      <div className="flex gap-2 mb-4">
        {[
          { id: 'todos', label: 'Todos' },
          { id: 'ingreso', label: 'Ingresos' },
          { id: 'gasto', label: 'Gastos' },
        ].map(f => (
          <button
            key={f.id}
            onClick={() => setFiltro(f.id)}
            className={`px-3 py-1.5 rounded-lg text-xs font-semibold ${filtro === f.id ? 'bg-stone-800 text-white' : 'bg-white text-stone-500 border border-stone-200'}`}
          >
            {f.label}
          </button>
        ))}
      </div>

      {lista.length === 0 ? (
        <Card className="p-4"><EmptyState icon={Euro} text="No hay movimientos" /></Card>
      ) : (
        <div className="space-y-2">
          {lista.map(m => {
            const evento = data.eventos.find(e => e.id === m.eventoId);
            return (
              <Card key={m.id} className="p-4 flex items-center justify-between">
                <div className="min-w-0 flex-1">
                  <p className="font-semibold text-stone-800 truncate">{m.concepto}</p>
                  <div className="flex items-center gap-1.5 flex-wrap mt-0.5">
                    <span className="text-xs text-stone-400">{fmtDate(m.fecha)}</span>
                    <Pill tone="stone">{m.categoria}</Pill>
                    {evento && <Pill tone="amber">{evento.nombre}</Pill>}
                    {m.facturaId && <Pill tone="green"><Receipt size={10} className="inline -mt-0.5 mr-0.5" />Factura</Pill>}
                  </div>
                </div>
                <div className="flex items-center gap-2 shrink-0">
                  <span className={`font-bold ${m.tipo === 'ingreso' ? 'text-emerald-600' : 'text-rose-600'}`}>
                    {m.tipo === 'ingreso' ? '+' : '-'}{fmt(m.importe)}
                  </span>
                  <button onClick={() => deleteMov(m.id)} className="p-1.5 text-stone-300 hover:text-rose-500">
                    <Trash2 size={15} />
                  </button>
                </div>
              </Card>
            );
          })}
        </div>
      )}

      <Modal open={modalOpen} onClose={() => setModalOpen(false)} title="Nuevo movimiento">
        <div className="flex gap-2 mb-3">
          {['ingreso', 'gasto'].map(tipo => (
            <button
              key={tipo}
              onClick={() => setForm({ ...form, tipo, categoria: tipo === 'ingreso' ? CATS_INGRESO[0] : CATS_GASTO[0] })}
              className={`flex-1 py-2 rounded-xl text-sm font-bold ${
                form.tipo === tipo
                  ? tipo === 'ingreso' ? 'bg-emerald-500 text-white' : 'bg-rose-500 text-white'
                  : 'bg-stone-100 text-stone-500'
              }`}
            >
              {tipo === 'ingreso' ? '+ Ingreso' : '− Gasto'}
            </button>
          ))}
        </div>
        <Field label="Concepto">
          <input className={inputCls} value={form.concepto} onChange={e => setForm({ ...form, concepto: e.target.value })} placeholder="Ej: Compra de bebidas" />
        </Field>
        <Field label="Importe (€)">
          <input type="number" className={inputCls} value={form.importe} onChange={e => setForm({ ...form, importe: e.target.value })} placeholder="0.00" />
        </Field>
        <Field label="Categoría">
          <select className={inputCls} value={form.categoria} onChange={e => setForm({ ...form, categoria: e.target.value })}>
            {(form.tipo === 'ingreso' ? CATS_INGRESO : CATS_GASTO).map(c => <option key={c} value={c}>{c}</option>)}
          </select>
        </Field>
        <Field label="Fecha">
          <input type="date" className={inputCls} value={form.fecha} onChange={e => setForm({ ...form, fecha: e.target.value })} />
        </Field>
        <Field label="Evento asociado (opcional)">
          <select className={inputCls} value={form.eventoId} onChange={e => setForm({ ...form, eventoId: e.target.value })}>
            <option value="">Ninguno / general</option>
            {data.eventos.map(ev => <option key={ev.id} value={ev.id}>{ev.nombre}</option>)}
          </select>
        </Field>
        {error && <p className="text-xs text-rose-600 font-semibold mb-2">{error}</p>}
        <Button onClick={addMovimiento} className="w-full mt-2">Guardar movimiento</Button>
      </Modal>
    </div>
  );
}

// ---------- Tab Facturas ----------
function FacturasTab({ data, refresh }) {
  const [modalOpen, setModalOpen] = useState(false);
  const [previewFactura, setPreviewFactura] = useState(null);
  const [form, setForm] = useState({
    file: null, fileName: '', filePreviewUrl: '', fileMime: '',
    modo: 'nuevo', // 'nuevo' | 'existente' | 'ninguno'
    movimientoExistenteId: '',
    concepto: '', categoria: CATS_GASTO[0], importe: '', fecha: today(), eventoId: '',
  });
  const [uploading, setUploading] = useState(false);
  const [error, setError] = useState('');

  const gastosSinFactura = useMemo(
    () => data.movimientos.filter(m => m.tipo === 'gasto' && !m.facturaId),
    [data.movimientos]
  );

  const handleFileChange = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const previewUrl = file.type.startsWith('image/') ? URL.createObjectURL(file) : '';
    setForm(f => ({ ...f, file, fileName: file.name, filePreviewUrl: previewUrl, fileMime: file.type }));
  };

  const resetForm = () => setForm({
    file: null, fileName: '', filePreviewUrl: '', fileMime: '',
    modo: 'nuevo', movimientoExistenteId: '',
    concepto: '', categoria: CATS_GASTO[0], importe: '', fecha: today(), eventoId: '',
  });

  const handleUpload = async () => {
    if (!form.file) return;
    if (form.modo === 'nuevo' && (!form.concepto.trim() || !form.importe)) return;
    if (form.modo === 'existente' && !form.movimientoExistenteId) return;

    setUploading(true);
    setError('');
    try {
      let movimientoId = null;
      if (form.modo === 'nuevo') {
        const mov = await api.crearMovimiento({
          tipo: 'gasto',
          concepto: form.concepto.trim(),
          categoria: form.categoria,
          importe: Number(form.importe),
          fecha: form.fecha,
          eventoId: form.eventoId || null,
        });
        movimientoId = mov.id;
      } else if (form.modo === 'existente') {
        movimientoId = form.movimientoExistenteId;
      }

      await api.subirFactura(form.file, { movimientoId, nombreOriginal: form.fileName });
      await refresh();
      resetForm();
      setModalOpen(false);
    } catch (e) {
      setError(e.message);
    } finally {
      setUploading(false);
    }
  };

  const openPreview = (factura) => setPreviewFactura(factura);

  const deleteFactura = async (factura) => {
    await api.eliminarFactura(factura.id);
    await refresh();
    setPreviewFactura(null);
  };

  const movNombre = (id) => {
    const m = data.movimientos.find(mv => mv.id === id);
    return m ? m.concepto : null;
  };

  return (
    <div>
      <Button onClick={() => setModalOpen(true)} className="w-full mb-4 flex items-center justify-center gap-2">
        <Upload size={16} /> Subir factura o ticket
      </Button>

      {data.facturas.length === 0 ? (
        <Card className="p-4"><EmptyState icon={Receipt} text="Todavía no hay facturas subidas" /></Card>
      ) : (
        <div className="grid grid-cols-2 gap-3">
          {data.facturas.map(f => (
            <Card key={f.id} className="p-3 cursor-pointer" >
              <button onClick={() => openPreview(f)} className="w-full text-left">
                <div className="w-full aspect-square rounded-xl bg-stone-100 flex items-center justify-center mb-2 overflow-hidden">
                  {f.nombre.toLowerCase().endsWith('.pdf') ? (
                    <FileText size={28} className="text-stone-400" />
                  ) : (
                    <img src={api.urlFactura(f.id)} alt={f.nombre} className="w-full h-full object-cover" />
                  )}
                </div>
                <p className="text-xs font-semibold text-stone-700 truncate">{f.nombre}</p>
                <p className="text-[11px] text-stone-400">{fmtDate(f.fecha)}</p>
                {f.movimientoId && movNombre(f.movimientoId) && (
                  <p className="text-[11px] text-green-700 flex items-center gap-1 mt-1 truncate">
                    <Link2 size={10} /> {movNombre(f.movimientoId)}
                  </p>
                )}
              </button>
            </Card>
          ))}
        </div>
      )}

      {/* Modal de subida */}
      <Modal open={modalOpen} onClose={() => { setModalOpen(false); resetForm(); }} title="Subir factura o ticket">
        <Field label="Archivo (foto o PDF)">
          <input type="file" accept="image/*,application/pdf" onChange={handleFileChange} className={inputCls} />
        </Field>
        {form.filePreviewUrl && (
          <img src={form.filePreviewUrl} alt="preview" className="w-full rounded-xl mb-3 max-h-48 object-contain bg-stone-50" />
        )}

        <div className="flex gap-2 mb-3">
          {[
            { id: 'nuevo', label: 'Nuevo gasto' },
            { id: 'existente', label: 'Vincular a gasto' },
            { id: 'ninguno', label: 'Solo guardar' },
          ].map(opt => (
            <button
              key={opt.id}
              onClick={() => setForm({ ...form, modo: opt.id })}
              className={`flex-1 py-2 rounded-xl text-xs font-bold ${form.modo === opt.id ? 'bg-green-600 text-white' : 'bg-stone-100 text-stone-500'}`}
            >
              {opt.label}
            </button>
          ))}
        </div>

        {form.modo === 'nuevo' && (
          <>
            <Field label="Concepto">
              <input className={inputCls} value={form.concepto} onChange={e => setForm({ ...form, concepto: e.target.value })} placeholder="Ej: Factura del bar" />
            </Field>
            <Field label="Importe (€)">
              <input type="number" className={inputCls} value={form.importe} onChange={e => setForm({ ...form, importe: e.target.value })} placeholder="0.00" />
            </Field>
            <Field label="Categoría">
              <select className={inputCls} value={form.categoria} onChange={e => setForm({ ...form, categoria: e.target.value })}>
                {CATS_GASTO.map(c => <option key={c} value={c}>{c}</option>)}
              </select>
            </Field>
            <Field label="Fecha">
              <input type="date" className={inputCls} value={form.fecha} onChange={e => setForm({ ...form, fecha: e.target.value })} />
            </Field>
            <Field label="Evento asociado (opcional)">
              <select className={inputCls} value={form.eventoId} onChange={e => setForm({ ...form, eventoId: e.target.value })}>
                <option value="">Ninguno / general</option>
                {data.eventos.map(ev => <option key={ev.id} value={ev.id}>{ev.nombre}</option>)}
              </select>
            </Field>
          </>
        )}

        {form.modo === 'existente' && (
          <Field label="Gasto a vincular">
            <select className={inputCls} value={form.movimientoExistenteId} onChange={e => setForm({ ...form, movimientoExistenteId: e.target.value })}>
              <option value="">Selecciona un gasto...</option>
              {gastosSinFactura.map(m => (
                <option key={m.id} value={m.id}>{m.concepto} · {fmt(m.importe)} · {fmtDate(m.fecha)}</option>
              ))}
            </select>
            {gastosSinFactura.length === 0 && <p className="text-xs text-stone-400 mt-1">No hay gastos sin factura vinculada.</p>}
          </Field>
        )}

        {error && <p className="text-xs text-rose-600 font-semibold mb-2">{error}</p>}
        <Button onClick={handleUpload} disabled={uploading || !form.file} className="w-full mt-2">
          {uploading ? 'Guardando...' : 'Guardar factura'}
        </Button>
      </Modal>

      {/* Modal de previsualización */}
      <Modal open={!!previewFactura} onClose={() => setPreviewFactura(null)} title={previewFactura?.nombre || 'Factura'}>
        {previewFactura && (
          <>
            {previewFactura.nombre.toLowerCase().endsWith('.pdf') ? (
              <a
                href={api.urlFactura(previewFactura.id)}
                target="_blank" rel="noreferrer"
                className="flex items-center gap-2 text-green-700 font-semibold text-sm mb-4"
              >
                <FileText size={16} /> Abrir / descargar PDF
              </a>
            ) : (
              <img
                src={api.urlFactura(previewFactura.id)}
                alt={previewFactura.nombre}
                className="w-full rounded-xl mb-4"
              />
            )}
            {previewFactura.movimientoId && movNombre(previewFactura.movimientoId) && (
              <p className="text-xs text-stone-500 mb-3">Vinculada a: <span className="font-semibold text-stone-700">{movNombre(previewFactura.movimientoId)}</span></p>
            )}
            <Button variant="danger" onClick={() => deleteFactura(previewFactura)} className="w-full flex items-center justify-center gap-2">
              <Trash2 size={14} /> Eliminar factura
            </Button>
          </>
        )}
      </Modal>
    </div>
  );
}

// ---------- Tab Eventos ----------
function EventosTab({ data, refresh, balancesEventos }) {
  const [modalOpen, setModalOpen] = useState(false);
  const [expanded, setExpanded] = useState(null);
  const [form, setForm] = useState({ nombre: '', fecha: today(), notas: '' });
  const [error, setError] = useState('');

  const addEvento = async () => {
    if (!form.nombre.trim()) return;
    try {
      await api.crearEvento({ nombre: form.nombre.trim(), fecha: form.fecha, notas: form.notas });
      await refresh();
      setForm({ nombre: '', fecha: today(), notas: '' });
      setModalOpen(false);
    } catch (e) {
      setError(e.message);
    }
  };

  const deleteEvento = async (id) => {
    await api.eliminarEvento(id);
    await refresh();
  };

  return (
    <div>
      <Button onClick={() => setModalOpen(true)} className="w-full mb-4 flex items-center justify-center gap-2">
        <PlusCircle size={16} /> Nuevo evento
      </Button>

      {balancesEventos.length === 0 ? (
        <Card className="p-4"><EmptyState icon={PartyPopper} text="Todavía no hay eventos" /></Card>
      ) : (
        <div className="space-y-2">
          {balancesEventos.map(ev => {
            const isOpen = expanded === ev.id;
            const movs = data.movimientos.filter(m => m.eventoId === ev.id);
            return (
              <Card key={ev.id} className="overflow-hidden">
                <button
                  onClick={() => setExpanded(isOpen ? null : ev.id)}
                  className="w-full p-4 flex items-center justify-between"
                >
                  <div className="min-w-0 flex-1 text-left">
                    <p className="font-semibold text-stone-800 truncate">{ev.nombre}</p>
                    <p className="text-xs text-stone-400 flex items-center gap-1"><Calendar size={11} />{fmtDate(ev.fecha)} · {ev.numMovs} mov.</p>
                  </div>
                  <div className="flex items-center gap-2 shrink-0">
                    <span className={`font-bold text-sm ${ev.balance >= 0 ? 'text-emerald-600' : 'text-rose-600'}`}>{fmt(ev.balance)}</span>
                    {isOpen ? <ChevronDown size={16} className="text-stone-400" /> : <ChevronRight size={16} className="text-stone-400" />}
                  </div>
                </button>
                {isOpen && (
                  <div className="px-4 pb-4 border-t border-stone-100 pt-3">
                    <div className="flex justify-between text-xs text-stone-500 mb-2">
                      <span>Ingresos: <b className="text-emerald-600">{fmt(ev.ingresos)}</b></span>
                      <span>Gastos: <b className="text-rose-600">{fmt(ev.gastos)}</b></span>
                    </div>
                    {ev.notas && <p className="text-xs text-stone-400 italic mb-2">{ev.notas}</p>}
                    {movs.length === 0 ? (
                      <p className="text-xs text-stone-400">Sin movimientos asociados aún.</p>
                    ) : (
                      <div className="space-y-1.5">
                        {movs.map(m => (
                          <div key={m.id} className="flex justify-between text-xs">
                            <span className="text-stone-600 truncate">{m.concepto}</span>
                            <span className={m.tipo === 'ingreso' ? 'text-emerald-600 font-semibold' : 'text-rose-600 font-semibold'}>
                              {m.tipo === 'ingreso' ? '+' : '-'}{fmt(m.importe)}
                            </span>
                          </div>
                        ))}
                      </div>
                    )}
                    <button onClick={() => deleteEvento(ev.id)} className="text-rose-500 text-xs font-semibold mt-3 flex items-center gap-1">
                      <Trash2 size={12} /> Eliminar evento
                    </button>
                  </div>
                )}
              </Card>
            );
          })}
        </div>
      )}

      <Modal open={modalOpen} onClose={() => setModalOpen(false)} title="Nuevo evento">
        <Field label="Nombre del evento">
          <input className={inputCls} value={form.nombre} onChange={e => setForm({ ...form, nombre: e.target.value })} placeholder="Ej: Cena de Navidad" />
        </Field>
        <Field label="Fecha">
          <input type="date" className={inputCls} value={form.fecha} onChange={e => setForm({ ...form, fecha: e.target.value })} />
        </Field>
        <Field label="Notas (opcional)">
          <input className={inputCls} value={form.notas} onChange={e => setForm({ ...form, notas: e.target.value })} placeholder="Lugar, detalles..." />
        </Field>
        {error && <p className="text-xs text-rose-600 font-semibold mb-2">{error}</p>}
        <Button onClick={addEvento} className="w-full mt-2">Crear evento</Button>
      </Modal>
    </div>
  );
}

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

