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

class ErrorBoundary extends React.Component {
  constructor(props) { super(props); this.state = { err: null }; }
  static getDerivedStateFromError(e) { return { err: e }; }
  render() {
    if (!this.state.err) return this.props.children;
    return (
      <div style={{ padding: 32, fontFamily: 'monospace', color: '#c00' }}>
        <strong>Error del portal (reportar a soporte):</strong>
        <pre style={{ whiteSpace: 'pre-wrap', marginTop: 8 }}>{String(this.state.err)}</pre>
      </div>
    );
  }
}

const STATUS_LABELS = {
  recibida: 'Recibida',
  en_revision: 'En revisión',
  cotizada: 'Cotizada',
  aceptada: 'Aceptada',
  rechazada: 'Rechazada',
  archivada: 'Archivada'
};

const CTRL_CHARS_RE = /[\x00-\x1F\x7F]/g;
const RIF_RE = /^[JGVEPjgvep]-?\d{8}-?\d$/;

function stripCtrl(s) {
  return (s == null ? '' : String(s)).replace(CTRL_CHARS_RE, '');
}

function normalizeRif(raw) {
  const cleaned = String(raw || '').replace(/\s+/g, '').toUpperCase();
  if (!RIF_RE.test(cleaned)) return null;
  const noHyphens = cleaned.replace(/-/g, '');
  return noHyphens[0] + '-' + noHyphens.slice(1, 9) + '-' + noHyphens.slice(9);
}

function sanitizeFilename(name) {
  const base = String(name || 'archivo').replace(/[\\/]/g, '_').replace(CTRL_CHARS_RE, '');
  return base.slice(0, 200);
}

function formatBytes(n) {
  if (!n && n !== 0) return '';
  if (n < 1024) return n + ' B';
  if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
  return (n / (1024 * 1024)).toFixed(1) + ' MB';
}

function formatDate(iso) {
  if (!iso) return '';
  try {
    const d = new Date(iso);
    return d.toLocaleDateString('es-VE', { year: 'numeric', month: 'short', day: '2-digit' });
  } catch (_) { return iso; }
}

function formatDateTime(iso) {
  if (!iso) return '';
  try {
    const d = new Date(iso);
    return d.toLocaleString('es-VE', { year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' });
  } catch (_) { return iso; }
}

function todayLocalISO() {
  const d = new Date();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return d.getFullYear() + '-' + m + '-' + day;
}

function whatsappLink(message) {
  const num = String(window.__IHV_WHATSAPP__ || '').replace(/[^0-9]/g, '');
  if (!num) return null;
  return 'https://wa.me/' + num + '?text=' + encodeURIComponent(message || '');
}

const URGENCY_LABELS = { normal: 'Normal', prioritaria: 'Prioritaria', urgente: 'Urgente' };

function formatAmount(amount, currency) {
  if (amount == null) return '—';
  const n = Number(amount);
  if (Number.isNaN(n)) return '—';
  return n.toLocaleString('es-VE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' ' + (currency || 'USD');
}

function isExpired(validUntil) {
  if (!validUntil) return false;
  try { return validUntil < todayLocalISO(); } catch (_) { return false; }
}

const PHONE_COUNTRY_CODES = [
  { code: '+58', label: 'Venezuela (+58)' },
  { code: '+1', label: 'Estados Unidos (+1)' },
  { code: '+57', label: 'Colombia (+57)' },
  { code: '+507', label: 'Panamá (+507)' },
  { code: '+34', label: 'España (+34)' }
];

const E164_RE = /^\+[1-9][0-9]{6,14}$/;

function splitE164(value) {
  const v = String(value || '');
  const codes = PHONE_COUNTRY_CODES.map((c) => c.code).sort((a, b) => b.length - a.length);
  for (let i = 0; i < codes.length; i++) {
    if (v.indexOf(codes[i]) === 0) return { cc: codes[i], local: v.slice(codes[i].length) };
  }
  return { cc: '+58', local: v.replace(/^\+/, '') };
}

function composeE164(cc, local) {
  const digits = String(local || '').replace(/[^0-9]/g, '');
  if (!digits) return null;
  const value = cc + digits;
  return E164_RE.test(value) ? value : false;
}

function translateAuthError(err) {
  const msg = (err && err.message) ? String(err.message) : '';
  if (/Invalid login credentials/i.test(msg)) return 'Correo o contraseña incorrectos.';
  if (/Email not confirmed/i.test(msg)) return 'Confirma tu correo antes de iniciar sesión.';
  if (/User already registered/i.test(msg)) return 'Ya existe una cuenta con este correo. Inicia sesión.';
  if (/Password should be at least/i.test(msg)) return 'La contraseña debe tener al menos 8 caracteres.';
  if (/rate limit/i.test(msg)) return 'Demasiados intentos. Espera unos minutos.';
  if (/captcha/i.test(msg)) return 'El captcha no se validó. Intenta de nuevo.';
  return 'No pudimos completar la acción. Inténtalo de nuevo.';
}

function translateRpcError(err) {
  const msg = (err && err.message) ? String(err.message) : '';
  const map = {
    not_authenticated: 'Tu sesión expiró. Vuelve a iniciar sesión.',
    email_not_verified: 'Confirma tu correo antes de enviar una solicitud.',
    invalid_request_type: 'Tipo de solicitud no válido.',
    invalid_currency: 'Moneda no válida.',
    notes_too_long: 'Las notas exceden el máximo de 2.000 caracteres.',
    invalid_supply_items_count: 'Debes incluir entre 1 y 100 artículos.',
    missing_works_details: 'Faltan los datos del proyecto de obra.',
    invalid_scope_description: 'El alcance debe tener entre 1 y 5.000 caracteres.',
    invalid_item_description: 'Cada artículo debe tener descripción de 1 a 500 caracteres.',
    invalid_item_quantity: 'La cantidad de cada artículo debe ser mayor a cero y menor a 1.000.000.',
    invalid_desired_delivery: 'La fecha de entrega deseada no puede ser anterior a hoy.',
    invalid_status_for_delete: 'Esta solicitud ya fue procesada por IHV y no puede eliminarse.',
    not_found: 'No encontramos esa solicitud.'
  };
  for (const key in map) {
    if (msg.indexOf(key) !== -1) return map[key];
  }
  // Errores de operaciones staff (Ola 6.2)
  if (msg.indexOf('forbidden') !== -1) return 'No tienes permiso para esta acción.';
  if (msg.indexOf('not_visible') !== -1) return 'Esta cotización no está en tus segmentos.';
  if (msg.indexOf('already_assigned') !== -1) return 'La cotización ya está asignada.';
  if (msg.indexOf('approval_required') !== -1) return 'Requiere aprobación: el monto supera el umbral. Pide que la aprueben primero.';
  if (msg.indexOf('invalid_assignee') !== -1) return 'El miembro seleccionado no es válido.';
  if (msg.indexOf('invalid_status') !== -1) return 'Estado no válido.';
  if (msg.indexOf('invalid_currency') !== -1) return 'Moneda no válida.';
  const detail = (err && (err.details || err.hint)) ? ' (' + (err.details || err.hint) + ')' : '';
  return 'No pudimos guardar tu solicitud: ' + (msg || 'error desconocido') + detail;
}

function parseHash() {
  const raw = window.location.hash || '#/';
  const path = raw.startsWith('#') ? raw.slice(1) : raw;
  return path || '/';
}

function navigate(to) {
  if (window.location.hash !== '#' + to) {
    window.location.hash = '#' + to;
  }
}

function useHashRoute() {
  const [path, setPath] = useState(parseHash());
  useEffect(() => {
    const onHash = () => setPath(parseHash());
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);

  return useMemo(() => {
    const segs = path.split('/').filter(Boolean);
    const params = {};
    let name = 'home';
    if (segs.length === 0) name = 'home';
    else if (segs[0] === 'login') name = 'login';
    else if (segs[0] === 'signup') name = 'signup';
    else if (segs[0] === 'verify-email') name = 'verify-email';
    else if (segs[0] === 'callback') name = 'callback';
    else if (segs[0] === 'reset-password' || segs[0] === 'forgot') name = 'reset-password';
    else if (segs[0] === 'nueva') {
      name = 'nueva';
      if (segs[1] === 'suministros' || segs[1] === 'obras') params.tab = segs[1];
    }
    else if (segs[0] === 'cotizaciones' && segs.length === 1) name = 'cotizaciones';
    else if (segs[0] === 'cotizaciones' && segs.length === 2) { name = 'cotizacion-detalle'; params.id = segs[1]; }
    else if (segs[0] === 'perfil') name = 'perfil';
    else if (segs[0] === 'staff') {
      if (segs.length === 1)            name = 'staff-home';
      else if (segs[1] === 'login')     name = 'staff-login';
      else if (segs[1] === '2fa')       name = 'staff-2fa';
      else if (segs[1] === 'equipo')    name = 'staff-equipo';
      else if (segs[1] === 'segmentos') name = 'staff-segmentos';
      else if (segs[1] === 'cotizaciones' && segs.length === 2) name = 'staff-quotes';
      else if (segs[1] === 'cotizaciones' && segs.length === 3) {
        name = 'staff-quote-detalle'; params.id = segs[2];
      }
      else if (segs[1] === 'analytics')  name = 'staff-analytics';
      else if (segs[1] === 'clientes' && segs.length === 2) name = 'staff-clientes';
      else if (segs[1] === 'clientes' && segs.length === 3) { name = 'staff-cliente-detalle'; params.id = segs[2]; }
      else if (segs[1] === 'auditoria')  name = 'staff-auditoria';
      else if (segs[1] === 'hoja')       name = 'staff-hoja';
      else                              name = 'not-found';
    }
    else name = 'not-found';
    return { name, params, path };
  }, [path]);
}

function useStaffCapabilities(session) {
  const [caps, setCaps] = React.useState(null);

  React.useEffect(() => {
    if (!session) { setCaps(null); return; }
    const supabase = window.__supabaseClient;
    if (!supabase) return;
    let cancelled = false;

    async function load() {
      const uid = session.user.id;
      const [{ data: profile }, { data: overrides }] = await Promise.all([
        supabase.from('staff_profiles').select('role').eq('user_id', uid).single(),
        supabase.from('staff_capability_overrides')
          .select('capability_key, granted').eq('user_id', uid),
      ]);
      if (cancelled || !profile) return;

      const { data: roleCaps } = await supabase
        .from('role_capabilities').select('capability_key').eq('role_key', profile.role);
      if (cancelled) return;

      const effective = {};
      for (const rc of roleCaps || []) effective[rc.capability_key] = true;
      for (const ov of overrides || []) effective[ov.capability_key] = ov.granted;
      setCaps(effective);
    }

    load();
    return () => { cancelled = true; };
  }, [session]);

  return caps;
}

function hasCap(caps, key) { return !!(caps && caps[key]); }

function useSession() {
  const [session, setSession] = useState(null);
  const [ready, setReady] = useState(false);

  useEffect(() => {
    const sb = window.ihvSupabase;
    if (!sb) { setReady(true); return; }
    let unsub = null;
    sb.auth.getSession().then(({ data }) => {
      setSession(data && data.session ? data.session : null);
      setReady(true);
    });
    const { data: listener } = sb.auth.onAuthStateChange((_event, sess) => {
      setSession(sess || null);
    });
    unsub = listener && listener.subscription ? listener.subscription : null;
    return () => { if (unsub) unsub.unsubscribe(); };
  }, []);

  const role = useMemo(() => {
    if (!session || !session.user) return null;
    const r = session.user.app_metadata && session.user.app_metadata.role;
    if (r === 'staff' || r === 'admin') return 'staff';
    return 'client';
  }, [session]);

  const signOut = useCallback(async () => {
    if (window.ihvSupabase) await window.ihvSupabase.auth.signOut();
    navigate('/login');
  }, []);

  return { session, role, ready, signOut };
}

function NotificationBell({ session }) {
  const [items, setItems] = useState([]);
  const [open, setOpen] = useState(false);
  const sb = window.ihvSupabase;
  const uid = session && session.user ? session.user.id : null;

  const load = useCallback(async () => {
    if (!sb || !uid) return;
    const { data } = await sb.from('notifications')
      .select('id, kind, title, body, quote_id, read_at, created_at')
      .order('created_at', { ascending: false })
      .limit(20);
    setItems(data || []);
  }, [sb, uid]);

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

  useEffect(() => {
    if (!sb || !uid) return;
    const channel = sb.channel('notif:' + uid)
      .on('postgres_changes',
        { event: 'INSERT', schema: 'public', table: 'notifications', filter: 'user_id=eq.' + uid },
        (payload) => { setItems((prev) => [payload.new, ...prev].slice(0, 20)); })
      .subscribe();
    return () => { try { sb.removeChannel(channel); } catch (_) {} };
  }, [sb, uid]);

  const unread = items.filter((n) => !n.read_at).length;

  const onOpen = async () => {
    const next = !open;
    setOpen(next);
  };

  const onMarkAll = async () => {
    await sb.rpc('mark_all_notifications_read');
    setItems((prev) => prev.map((n) => ({ ...n, read_at: n.read_at || new Date().toISOString() })));
  };

  const onItemClick = async (n) => {
    if (!n.read_at) {
      await sb.rpc('mark_notification_read', { p_id: n.id });
      setItems((prev) => prev.map((x) => x.id === n.id ? { ...x, read_at: new Date().toISOString() } : x));
    }
    if (n.quote_id) { setOpen(false); navigate('/staff/cotizaciones/' + n.quote_id); }
  };

  return (
    <div className="ihv-portal-bell">
      <button type="button" className="ihv-portal-bell__btn" aria-label="Notificaciones"
        aria-expanded={open} onClick={onOpen}>
        Avisos
        {unread > 0 && <span className="ihv-portal-bell__badge">{unread}</span>}
      </button>
      {open && (
        <div className="ihv-portal-bell__panel" role="menu">
          <div className="ihv-portal-bell__head">
            <span>Notificaciones</span>
            {unread > 0 && (
              <button type="button" className="ihv-portal-bell__markall" onClick={onMarkAll}>
                Marcar todo leído
              </button>
            )}
          </div>
          {items.length === 0 && <p className="ihv-portal-bell__empty">Sin avisos.</p>}
          <ul className="ihv-portal-bell__list">
            {items.map((n) => (
              <li key={n.id}
                  className={'ihv-portal-bell__item' + (n.read_at ? '' : ' ihv-portal-bell__item--unread')}
                  onClick={() => onItemClick(n)}>
                <div className="ihv-portal-bell__title">{n.title}</div>
                {n.body && <div className="ihv-portal-bell__body">{n.body}</div>}
                <div className="ihv-portal-bell__time">{formatDateTime(n.created_at)}</div>
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

function Topbar({ session, role, onSignOut }) {
  const email = session && session.user ? session.user.email : null;
  const isClient = !!email && role !== 'staff';
  const waHref = isClient
    ? whatsappLink('Hola, escribo desde el portal IHV. Mi correo es ' + email + '.')
    : null;
  return (
    <header className="ihv-portal-topbar">
      <div className="ihv-portal-topbar__brand">Portal IHV</div>
      {isClient && (
        <nav className="ihv-portal-topbar__nav" aria-label="Navegación del portal">
          <a className="ihv-portal-topbar__nav-link" href="#/cotizaciones">Mis cotizaciones</a>
          <a className="ihv-portal-topbar__nav-link" href="#/nueva">Nueva cotización</a>
          <a className="ihv-portal-topbar__nav-link" href="#/perfil">Mi cuenta</a>
          {waHref && (
            <a className="ihv-portal-btn ihv-portal-btn--whatsapp" href={waHref} target="_blank" rel="noopener">
              WhatsApp
            </a>
          )}
        </nav>
      )}
      {role === 'staff' && (
        <>
          <a className="ihv-portal-topbar__nav-link" href="#/staff">Mi trabajo</a>
          <a className="ihv-portal-topbar__nav-link" href="#/staff/cotizaciones">Cotizaciones</a>
          <NotificationBell session={session} />
        </>
      )}
      <div className="ihv-portal-topbar__status">
        {email && (
          <>
            <span className="ihv-portal-topbar__role">{role === 'staff' ? 'Equipo IHV' : 'Cliente'}</span>
            <span className="ihv-portal-topbar__status-text">{email}</span>
            <button className="ihv-portal-btn ihv-portal-btn--ghost" type="button" onClick={onSignOut}>
              Cerrar sesión
            </button>
          </>
        )}
      </div>
    </header>
  );
}

function FieldError({ children }) {
  if (!children) return null;
  return <p className="ihv-portal-form__error" role="alert">{children}</p>;
}

function StatusBadge({ status }) {
  const label = STATUS_LABELS[status] || status;
  return <span className={'ihv-portal-status ihv-portal-status--' + status}>{label}</span>;
}

function WhatsappField({ idPrefix, cc, local, onCcChange, onLocalChange, optional }) {
  return (
    <div className="ihv-portal-form__field">
      <label className="ihv-portal-form__label" htmlFor={idPrefix + '-wa-num'}>
        WhatsApp{optional ? ' (opcional)' : ''}
      </label>
      <div className="ihv-portal-phone">
        <select className="ihv-portal-form__select ihv-portal-phone__cc" aria-label="Código de país"
          value={cc} onChange={(e) => onCcChange(e.target.value)}>
          {PHONE_COUNTRY_CODES.map((c) => (
            <option key={c.code} value={c.code}>{c.label}</option>
          ))}
        </select>
        <input id={idPrefix + '-wa-num'} className="ihv-portal-form__input" type="tel"
          placeholder="4121234567" value={local}
          onChange={(e) => onLocalChange(e.target.value)} />
      </div>
    </div>
  );
}

function useTurnstile(action) {
  const siteKey = window.__IHV_TURNSTILE_SITE_KEY__ || '';
  const captchaConfigured = !!siteKey && siteKey !== '<TURNSTILE_SITE_KEY_PENDING>';
  const allowNoCaptcha = !!window.__IHV_ALLOW_NO_CAPTCHA__;
  const [captcha, setCaptcha] = useState(null);
  const [ready, setReady] = useState(false);
  const ref = useRef(null);
  const widgetIdRef = useRef(null);

  useEffect(() => {
    if (!captchaConfigured) return;
    let cancelled = false;
    let elapsed = 0;
    const poll = setInterval(() => {
      elapsed += 100;
      if (window.turnstile) { if (!cancelled) setReady(true); clearInterval(poll); }
      else if (elapsed >= 5000) clearInterval(poll);
    }, 100);
    return () => { cancelled = true; clearInterval(poll); };
  }, [captchaConfigured]);

  useEffect(() => {
    if (!ready || !ref.current) return;
    try {
      widgetIdRef.current = window.turnstile.render(ref.current, {
        sitekey: siteKey,
        callback: (t) => setCaptcha(t),
        'expired-callback': () => setCaptcha(null),
        'error-callback': () => setCaptcha(null),
        action,
        theme: 'light'
      });
    } catch (_) {}
    return () => {
      try { if (widgetIdRef.current) window.turnstile.remove(widgetIdRef.current); } catch (_) {}
      widgetIdRef.current = null;
    };
  }, [ready, siteKey, action]);

  const reset = () => {
    if (window.turnstile && widgetIdRef.current) {
      try { window.turnstile.reset(widgetIdRef.current); } catch (_) {}
    }
    setCaptcha(null);
  };

  return { siteKey, captchaConfigured, allowNoCaptcha, captcha, ready, ref, reset };
}

function LoginView() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState(null);
  const [busy, setBusy] = useState(false);
  const t = useTurnstile('login');

  const onSubmit = async (e) => {
    e.preventDefault();
    setError(null);
    if (!email || !password) { setError('Ingresa correo y contraseña.'); return; }
    if (t.captchaConfigured && !t.captcha && !t.allowNoCaptcha) { setError('Completa el captcha.'); return; }
    setBusy(true);
    const opts = t.captcha ? { captchaToken: t.captcha } : undefined;
    const { error: err } = await window.ihvSupabase.auth.signInWithPassword({
      email: email.trim(), password, options: opts
    });
    setBusy(false);
    if (err) { setError(translateAuthError(err)); t.reset(); return; }
  };

  const onGoogle = async () => {
    setError(null);
    if (t.captchaConfigured && !t.captcha && !t.allowNoCaptcha) { setError('Completa el captcha.'); return; }
    const redirectTo = window.location.origin + '/portal/#/callback';
    const options = { redirectTo };
    if (t.captcha) options.captchaToken = t.captcha;
    const { error: err } = await window.ihvSupabase.auth.signInWithOAuth({
      provider: 'google',
      options
    });
    if (err) { setError(translateAuthError(err)); t.reset(); }
  };

  return (
    <div className="ihv-portal-auth">
      <div className="ihv-portal-auth__panel">
        <h1 className="ihv-portal-auth__title">Inicia sesión</h1>
        <form className="ihv-portal-form" onSubmit={onSubmit} noValidate>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="login-email">Correo</label>
            <input id="login-email" className="ihv-portal-form__input" type="email" autoComplete="email"
              value={email} onChange={(e) => setEmail(e.target.value)} required />
          </div>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="login-pass">Contraseña</label>
            <input id="login-pass" className="ihv-portal-form__input" type="password" autoComplete="current-password"
              value={password} onChange={(e) => setPassword(e.target.value)} required />
          </div>
          {t.captchaConfigured && (
            <div className="ihv-portal-form__field">
              <div className="ihv-portal-captcha" ref={t.ref} />
              {!t.ready && <p className="ihv-portal-form__hint">Cargando verificación.</p>}
            </div>
          )}
          <FieldError>{error}</FieldError>
          <button className="ihv-portal-btn ihv-portal-btn--primary" type="submit"
            disabled={busy || (t.captchaConfigured && !t.captcha && !t.allowNoCaptcha)}>
            {busy ? 'Iniciando' : 'Iniciar sesión'}
          </button>
        </form>
        <div className="ihv-portal-auth__separator"><span>o continúa con</span></div>
        <button className="ihv-portal-btn ihv-portal-btn--google" type="button" onClick={onGoogle}>
          Continuar con Google
        </button>
        <div className="ihv-portal-auth__links">
          <a href="#/signup">Primera vez. Crea tu cuenta</a>
          <a href="#/reset-password">Olvidé mi contraseña</a>
        </div>
      </div>
    </div>
  );
}

function SignupView() {
  const siteKey = window.__IHV_TURNSTILE_SITE_KEY__ || '';
  const allowNoCaptcha = !!window.__IHV_ALLOW_NO_CAPTCHA__;
  const captchaConfigured = siteKey && siteKey !== '<TURNSTILE_SITE_KEY_PENDING>';

  const [fullName, setFullName] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [confirm, setConfirm] = useState('');
  const [captcha, setCaptcha] = useState(null);
  const [error, setError] = useState(null);
  const [busy, setBusy] = useState(false);
  const [done, setDone] = useState(false);
  const [turnstileReady, setTurnstileReady] = useState(false);
  const captchaRef = useRef(null);
  const widgetIdRef = useRef(null);

  useEffect(() => {
    if (!captchaConfigured) return;
    let cancelled = false;
    let elapsed = 0;
    const poll = setInterval(() => {
      elapsed += 100;
      if (window.turnstile) { if (!cancelled) setTurnstileReady(true); clearInterval(poll); }
      else if (elapsed >= 5000) { clearInterval(poll); }
    }, 100);
    return () => { cancelled = true; clearInterval(poll); };
  }, [captchaConfigured]);

  useEffect(() => {
    if (!turnstileReady || !captchaRef.current || done) return;
    try {
      widgetIdRef.current = window.turnstile.render(captchaRef.current, {
        sitekey: siteKey,
        callback: (token) => setCaptcha(token),
        'expired-callback': () => setCaptcha(null),
        'error-callback': () => setCaptcha(null),
        action: 'signup',
        theme: 'light'
      });
    } catch (_) {}
    return () => {
      try { if (widgetIdRef.current) window.turnstile.remove(widgetIdRef.current); } catch (_) {}
      widgetIdRef.current = null;
    };
  }, [turnstileReady, siteKey, done]);

  const onSubmit = async (e) => {
    e.preventDefault();
    setError(null);
    const name = stripCtrl(fullName).trim();
    if (name.length < 2 || name.length > 120) { setError('Ingresa tu nombre completo (2 a 120 caracteres).'); return; }
    if (!email.trim()) { setError('Ingresa tu correo.'); return; }
    if (password.length < 8) { setError('La contraseña debe tener al menos 8 caracteres.'); return; }
    if (password !== confirm) { setError('Las contraseñas no coinciden.'); return; }
    if (captchaConfigured && !captcha && !allowNoCaptcha) { setError('Completa el captcha.'); return; }

    setBusy(true);
    const opts = {
      data: { full_name: name },
      emailRedirectTo: window.location.origin + '/portal/#/callback'
    };
    if (captcha) opts.captchaToken = captcha;
    const { error: err } = await window.ihvSupabase.auth.signUp({
      email: email.trim(), password, options: opts
    });
    setBusy(false);
    if (err) {
      setError(translateAuthError(err));
      if (window.turnstile && widgetIdRef.current) {
        try { window.turnstile.reset(widgetIdRef.current); } catch (_) {}
        setCaptcha(null);
      }
      return;
    }
    setDone(true);
  };

  if (done) {
    return <SignupConfirmView email={email} siteKey={siteKey} captchaConfigured={captchaConfigured} />;
  }

  return (
    <div className="ihv-portal-auth">
      <div className="ihv-portal-auth__panel">
        <h1 className="ihv-portal-auth__title">Crea tu cuenta</h1>
        <form className="ihv-portal-form" onSubmit={onSubmit} noValidate>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="su-name">Nombre completo</label>
            <input id="su-name" className="ihv-portal-form__input" type="text" autoComplete="name"
              value={fullName} onChange={(e) => setFullName(e.target.value)} required />
          </div>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="su-email">Correo corporativo</label>
            <input id="su-email" className="ihv-portal-form__input" type="email" autoComplete="email"
              value={email} onChange={(e) => setEmail(e.target.value)} required />
          </div>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="su-pass">Contraseña</label>
            <input id="su-pass" className="ihv-portal-form__input" type="password" autoComplete="new-password"
              value={password} onChange={(e) => setPassword(e.target.value)} required minLength={8} />
            <p className="ihv-portal-form__hint">Mínimo 8 caracteres.</p>
          </div>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="su-pass2">Confirma la contraseña</label>
            <input id="su-pass2" className="ihv-portal-form__input" type="password" autoComplete="new-password"
              value={confirm} onChange={(e) => setConfirm(e.target.value)} required minLength={8} />
          </div>
          {captchaConfigured ? (
            <div className="ihv-portal-form__field">
              <div className="ihv-portal-captcha" ref={captchaRef} />
              {!turnstileReady && <p className="ihv-portal-form__hint">Cargando verificación.</p>}
            </div>
          ) : (
            <p className="ihv-portal-form__hint">Captcha en configuración.</p>
          )}
          <FieldError>{error}</FieldError>
          <button className="ihv-portal-btn ihv-portal-btn--primary" type="submit"
            disabled={busy || (captchaConfigured && !captcha && !allowNoCaptcha)}>
            {busy ? 'Creando cuenta' : 'Crear cuenta'}
          </button>
        </form>
        <div className="ihv-portal-auth__links">
          <a href="#/login">Ya tengo cuenta</a>
        </div>
      </div>
    </div>
  );
}

function SignupConfirmView({ email, siteKey, captchaConfigured }) {
  const [busy, setBusy] = useState(false);
  const [sent, setSent] = useState(false);
  const [error, setError] = useState(null);
  const [captcha, setCaptcha] = useState(null);
  const [ready, setReady] = useState(false);
  const ref = useRef(null);
  const widgetIdRef = useRef(null);

  useEffect(() => {
    if (!captchaConfigured) return;
    let elapsed = 0;
    const poll = setInterval(() => {
      elapsed += 100;
      if (window.turnstile) { setReady(true); clearInterval(poll); }
      else if (elapsed >= 5000) clearInterval(poll);
    }, 100);
    return () => clearInterval(poll);
  }, [captchaConfigured]);

  useEffect(() => {
    if (!ready || !ref.current) return;
    try {
      widgetIdRef.current = window.turnstile.render(ref.current, {
        sitekey: siteKey,
        callback: (t) => setCaptcha(t),
        'expired-callback': () => setCaptcha(null),
        'error-callback': () => setCaptcha(null),
        action: 'signup-resend',
        theme: 'light'
      });
    } catch (_) {}
    return () => { try { if (widgetIdRef.current) window.turnstile.remove(widgetIdRef.current); } catch (_) {} };
  }, [ready, siteKey]);

  const onResend = async () => {
    setError(null);
    if (captchaConfigured && !captcha) { setError('Completa el captcha para reenviar.'); return; }
    setBusy(true);
    const opts = captcha ? { captchaToken: captcha } : undefined;
    const { error: err } = await window.ihvSupabase.auth.resend({ type: 'signup', email, options: opts });
    setBusy(false);
    if (err) { setError(translateAuthError(err)); return; }
    setSent(true);
  };

  return (
    <div className="ihv-portal-auth">
      <div className="ihv-portal-auth__panel">
        <h1 className="ihv-portal-auth__title">Revisa tu correo</h1>
        <p>Te enviamos un enlace de confirmación a <strong>{email}</strong>. Ábrelo desde el mismo dispositivo para activar tu cuenta. El enlace caduca en 24 horas.</p>
        {captchaConfigured && <div className="ihv-portal-captcha" ref={ref} />}
        <FieldError>{error}</FieldError>
        {sent ? <p className="ihv-portal-form__hint">Reenviado.</p> : (
          <button className="ihv-portal-btn ihv-portal-btn--secondary" type="button" onClick={onResend}
            disabled={busy || (captchaConfigured && !captcha)}>
            {busy ? 'Enviando' : 'Reenviar correo de confirmación'}
          </button>
        )}
        <div className="ihv-portal-auth__links">
          <a href="#/login">Volver al inicio de sesión</a>
        </div>
      </div>
    </div>
  );
}

function ResetPasswordView() {
  const [email, setEmail] = useState('');
  const [busy, setBusy] = useState(false);
  const [sent, setSent] = useState(false);
  const [error, setError] = useState(null);
  const t = useTurnstile('reset-password');

  const onSubmit = async (e) => {
    e.preventDefault();
    setError(null);
    if (!email.trim()) { setError('Ingresa tu correo.'); return; }
    if (t.captchaConfigured && !t.captcha && !t.allowNoCaptcha) { setError('Completa el captcha.'); return; }
    setBusy(true);
    const redirectTo = window.location.origin + '/portal/#/login';
    const opts = { redirectTo };
    if (t.captcha) opts.captchaToken = t.captcha;
    const { error: err } = await window.ihvSupabase.auth.resetPasswordForEmail(email.trim(), opts);
    setBusy(false);
    if (err) { setError(translateAuthError(err)); t.reset(); return; }
    setSent(true);
  };

  return (
    <div className="ihv-portal-auth">
      <div className="ihv-portal-auth__panel">
        <h1 className="ihv-portal-auth__title">Restablece tu contraseña</h1>
        {sent ? (
          <>
            <p>Te enviamos un enlace para restablecer la contraseña a <strong>{email}</strong>.</p>
            <div className="ihv-portal-auth__links"><a href="#/login">Volver</a></div>
          </>
        ) : (
          <form className="ihv-portal-form" onSubmit={onSubmit} noValidate>
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="rp-email">Correo</label>
              <input id="rp-email" className="ihv-portal-form__input" type="email" autoComplete="email"
                value={email} onChange={(e) => setEmail(e.target.value)} required />
            </div>
            {t.captchaConfigured && (
              <div className="ihv-portal-form__field">
                <div className="ihv-portal-captcha" ref={t.ref} />
                {!t.ready && <p className="ihv-portal-form__hint">Cargando verificación.</p>}
              </div>
            )}
            <FieldError>{error}</FieldError>
            <button className="ihv-portal-btn ihv-portal-btn--primary" type="submit"
              disabled={busy || (t.captchaConfigured && !t.captcha && !t.allowNoCaptcha)}>
              {busy ? 'Enviando' : 'Enviar enlace'}
            </button>
            <div className="ihv-portal-auth__links"><a href="#/login">Volver</a></div>
          </form>
        )}
      </div>
    </div>
  );
}

function CallbackView() {
  return (
    <div className="ihv-portal-auth">
      <div className="ihv-portal-auth__panel">
        <h1 className="ihv-portal-auth__title">Procesando inicio de sesión</h1>
        <p>Estamos verificando tu identidad. En unos segundos te llevaremos al portal.</p>
      </div>
    </div>
  );
}

function VerifyEmailView({ session, role }) {
  if (!session) {
    return (
      <div className="ihv-portal-auth">
        <div className="ihv-portal-auth__panel">
          <h1 className="ihv-portal-auth__title">Confirmando tu correo</h1>
          <p>Estamos confirmando tu correo. Si esta pantalla no avanza en unos segundos, abre el enlace desde el dispositivo original.</p>
        </div>
      </div>
    );
  }
  const home = role === 'staff' ? '/staff' : '/nueva';
  return (
    <div className="ihv-portal-auth">
      <div className="ihv-portal-auth__panel">
        <h1 className="ihv-portal-auth__title">Correo confirmado</h1>
        <p>Tu cuenta quedó activa.</p>
        <button className="ihv-portal-btn ihv-portal-btn--primary" type="button" onClick={() => navigate(home)}>
          Ir al portal
        </button>
      </div>
    </div>
  );
}

function NotFoundView() {
  return (
    <div className="ihv-portal-card">
      <h1 className="ihv-portal-card__title">Página no encontrada</h1>
      <div className="ihv-portal-card__body">
        <p>La ruta solicitada no existe.</p>
        <a href="#/">Volver al inicio</a>
      </div>
    </div>
  );
}

function StaffLoginView() {
  const EDGE_URL = window.__IHV_SUPABASE__ && window.__IHV_SUPABASE__.url
    ? window.__IHV_SUPABASE__.url + '/functions/v1/staff-pin-login'
    : null;

  const [step, setStep] = React.useState('pin'); // 'pin' | 'totp'
  const [email, setEmail] = React.useState('');
  const [pin, setPin]     = React.useState('');
  const [totp, setTotp]   = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const [error, setError]     = React.useState('');

  const ERRORS = {
    invalid_credentials: 'Credenciales incorrectas.',
    totp_invalid:        'Código TOTP incorrecto.',
    rate_limited:        'Demasiados intentos. Intenta más tarde.',
    server_error:        'Error del servidor. Intenta más tarde.',
    invalid_request:     'Solicitud inválida.',
  };

  async function handlePin(e) {
    e.preventDefault();
    setError(''); setLoading(true);
    try {
      const res = await fetch(EDGE_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, pin }),
      });
      const data = await res.json();
      if (data.action_link) {
        window.location.href = data.action_link;
        return;
      }
      if (data.error === 'totp_required') {
        if (data.enrolled === false) {
          try { sessionStorage.setItem('ihv:enrollEmail', email); } catch (_) {}
          window.location.hash = '/staff/2fa';
          return;
        }
        setStep('totp');
        return;
      }
      setError(ERRORS[data.error] || 'Error desconocido.');
    } catch (_) {
      setError('No se pudo conectar con el servidor.');
    } finally {
      setLoading(false);
    }
  }

  async function handleTotp(e) {
    e.preventDefault();
    setError(''); setLoading(true);
    try {
      const res = await fetch(EDGE_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, pin, token: totp }),
      });
      const data = await res.json();
      if (data.action_link) {
        window.location.href = data.action_link;
        return;
      }
      setError(ERRORS[data.error] || 'Error desconocido.');
    } catch (_) {
      setError('No se pudo conectar con el servidor.');
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="ihv-portal-card ihv-staff-login">
      <h1 className="ihv-portal-card__title">Acceso del equipo</h1>
      <div className="ihv-portal-card__body">
        {step === 'pin' ? (
          <form onSubmit={handlePin} className="ihv-portal-form">
            <div className="ihv-portal-form__group">
              <label htmlFor="sl-email" className="ihv-portal-form__label">Correo</label>
              <input id="sl-email" type="email" required autoComplete="username"
                className="ihv-portal-form__input"
                value={email} onChange={e => setEmail(e.target.value)} />
            </div>
            <div className="ihv-portal-form__group">
              <label htmlFor="sl-pin" className="ihv-portal-form__label">PIN</label>
              <input id="sl-pin" type="password" required autoComplete="current-password"
                className="ihv-portal-form__input" maxLength={8} inputMode="numeric"
                value={pin} onChange={e => setPin(e.target.value)} />
            </div>
            {error && <p className="ihv-portal-form__error">{error}</p>}
            <button type="submit" disabled={loading} className="ihv-portal-btn ihv-portal-btn--primary">
              {loading ? 'Verificando…' : 'Ingresar'}
            </button>
          </form>
        ) : (
          <form onSubmit={handleTotp} className="ihv-portal-form">
            <p className="ihv-portal-form__hint">Ingresa el código de tu aplicación autenticadora.</p>
            <div className="ihv-portal-form__group">
              <label htmlFor="sl-totp" className="ihv-portal-form__label">Código TOTP (6 dígitos)</label>
              <input id="sl-totp" type="text" required autoComplete="one-time-code"
                className="ihv-portal-form__input ihv-staff-login__totp-input"
                maxLength={6} inputMode="numeric" pattern="[0-9]{6}"
                value={totp} onChange={e => setTotp(e.target.value)} />
            </div>
            {error && <p className="ihv-portal-form__error">{error}</p>}
            <button type="submit" disabled={loading} className="ihv-portal-btn ihv-portal-btn--primary">
              {loading ? 'Verificando…' : 'Confirmar'}
            </button>
            <button type="button" className="ihv-portal-btn ihv-portal-btn--ghost"
              onClick={() => { setStep('pin'); setError(''); }}>
              Volver
            </button>
          </form>
        )}
      </div>
    </div>
  );
}

function StaffTotpEnrollView() {
  const EDGE_URL = window.__IHV_SUPABASE__ && window.__IHV_SUPABASE__.url
    ? window.__IHV_SUPABASE__.url + '/functions/v1/staff-pin-login'
    : null;

  const [email] = React.useState(() => {
    try { return sessionStorage.getItem('ihv:enrollEmail') || ''; } catch (_) { return ''; }
  });
  const [secret] = React.useState(() => generateTotpSecret());
  const [pin, setPin]   = React.useState('');
  const [token, setToken] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const [error, setError]     = React.useState('');

  const otpauthUri = `otpauth://totp/IHV%20Staff:${encodeURIComponent(email)}`
    + `?secret=${secret}&issuer=IHV&period=30&digits=6&algorithm=SHA1`;

  async function handleEnroll(e) {
    e.preventDefault();
    setError(''); setLoading(true);
    try {
      const res = await fetch(EDGE_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'enroll', email, pin, secret, token }),
      });
      const data = await res.json();
      if (data.action_link) {
        try { sessionStorage.removeItem('ihv:enrollEmail'); } catch (_) {}
        window.location.href = data.action_link;
        return;
      }
      const ERRORS = {
        invalid_credentials: 'PIN incorrecto.',
        totp_invalid:        'Código incorrecto. Verifica que el secret fue guardado correctamente.',
        already_enrolled:    'Este usuario ya tiene TOTP configurado.',
        server_error:        'Error del servidor.',
      };
      setError(ERRORS[data.error] || 'Error desconocido.');
    } catch (_) {
      setError('No se pudo conectar con el servidor.');
    } finally {
      setLoading(false);
    }
  }

  if (!email) {
    return (
      <div className="ihv-portal-card">
        <div className="ihv-portal-card__body">
          <p>Accede primero desde <a href="#/staff/login">la pantalla de acceso</a>.</p>
        </div>
      </div>
    );
  }

  return (
    <div className="ihv-portal-card ihv-staff-enroll">
      <h1 className="ihv-portal-card__title">Configurar autenticación de dos factores</h1>
      <div className="ihv-portal-card__body">
        <p className="ihv-portal-form__hint">
          Abre tu aplicación autenticadora (Google Authenticator, Authy, etc.) y agrega
          una nueva cuenta con el enlace o el secret a continuación.
        </p>

        <div className="ihv-staff-enroll__secret-block">
          <p className="ihv-staff-enroll__label">Secret (copia en tu app si no admite enlace):</p>
          <code className="ihv-staff-enroll__secret">{secret}</code>
        </div>

        <div className="ihv-staff-enroll__secret-block">
          <p className="ihv-staff-enroll__label">Enlace otpauth (pega en tu app o escanealo):</p>
          <code className="ihv-staff-enroll__secret" style={{wordBreak:'break-all', fontSize:'0.75rem'}}>
            {otpauthUri}
          </code>
        </div>

        <form onSubmit={handleEnroll} className="ihv-portal-form" style={{marginTop:'1.5rem'}}>
          <div className="ihv-portal-form__group">
            <label htmlFor="enroll-pin" className="ihv-portal-form__label">Tu PIN</label>
            <input id="enroll-pin" type="password" required
              className="ihv-portal-form__input" maxLength={8} inputMode="numeric"
              value={pin} onChange={e => setPin(e.target.value)} />
          </div>
          <div className="ihv-portal-form__group">
            <label htmlFor="enroll-code" className="ihv-portal-form__label">
              Código de verificación (6 dígitos generado por la app)
            </label>
            <input id="enroll-code" type="text" required autoComplete="one-time-code"
              className="ihv-portal-form__input" maxLength={6} inputMode="numeric" pattern="[0-9]{6}"
              value={token} onChange={e => setToken(e.target.value)} />
          </div>
          {error && <p className="ihv-portal-form__error">{error}</p>}
          <button type="submit" disabled={loading} className="ihv-portal-btn ihv-portal-btn--primary">
            {loading ? 'Guardando…' : 'Activar autenticación de dos factores'}
          </button>
        </form>
      </div>
    </div>
  );
}

function generateTotpSecret() {
  const B32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
  const bytes = crypto.getRandomValues(new Uint8Array(20));
  let s = '';
  for (let i = 0; i < 20; i += 5) {
    const b = [bytes[i]||0, bytes[i+1]||0, bytes[i+2]||0, bytes[i+3]||0, bytes[i+4]||0];
    s += B32[(b[0]>>3)&31];
    s += B32[((b[0]&7)<<2)|(b[1]>>6)];
    s += B32[(b[1]>>1)&31];
    s += B32[((b[1]&1)<<4)|(b[2]>>4)];
    s += B32[((b[2]&15)<<1)|(b[3]>>7)];
    s += B32[(b[3]>>2)&31];
    s += B32[((b[3]&3)<<3)|(b[4]>>5)];
    s += B32[b[4]&31];
  }
  return s;
}

function StaffHomeView({ session, caps }) {
  const sb = window.ihvSupabase;
  const uid = session && session.user ? session.user.id : null;
  const [stats, setStats] = useState(null);

  useEffect(() => {
    if (!sb || !uid) return;
    let cancelled = false;
    async function load() {
      // RLS ya restringe a las cotizaciones visibles del usuario.
      const [mine, unassigned, urgent] = await Promise.all([
        sb.from('quote_requests').select('id', { count: 'exact', head: true })
          .eq('assigned_to', uid).not('status', 'in', '(archivada,rechazada,aceptada)'),
        sb.from('quote_requests').select('id', { count: 'exact', head: true })
          .is('assigned_to', null).in('status', ['recibida', 'en_revision']),
        sb.from('quote_requests').select('id', { count: 'exact', head: true })
          .eq('urgency', 'urgente').not('status', 'in', '(archivada,rechazada,aceptada)'),
      ]);
      if (cancelled) return;
      setStats({
        mine: mine.count || 0,
        unassigned: unassigned.count || 0,
        urgent: urgent.count || 0,
      });
    }
    load();
    return () => { cancelled = true; };
  }, [sb, uid]);

  const go = (path) => navigate(path);

  return (
    <div className="ihv-portal-card ihv-staff-home">
      <div className="ihv-portal-card__header">
        <h1 className="ihv-portal-card__title">Mi trabajo</h1>
        <a className="ihv-portal-btn ihv-portal-btn--primary" href="#/staff/cotizaciones">Ver cotizaciones</a>
      </div>
      <div className="ihv-portal-card__body">
        {caps === null && <p className="ihv-portal-form__hint">Cargando permisos...</p>}

        <div className="ihv-staff-tiles">
          <button type="button" className="ihv-staff-tile" onClick={() => go('/staff/cotizaciones?mine=1')}>
            <span className="ihv-staff-tile__num">{stats ? stats.mine : '—'}</span>
            <span className="ihv-staff-tile__label">Asignadas a mí</span>
          </button>
          <button type="button" className="ihv-staff-tile" onClick={() => go('/staff/cotizaciones?unassigned=1')}>
            <span className="ihv-staff-tile__num">{stats ? stats.unassigned : '—'}</span>
            <span className="ihv-staff-tile__label">Sin asignar en mi pool</span>
          </button>
          <button type="button" className="ihv-staff-tile" onClick={() => go('/staff/cotizaciones?urgency=urgente')}>
            <span className="ihv-staff-tile__num">{stats ? stats.urgent : '—'}</span>
            <span className="ihv-staff-tile__label">Urgentes</span>
          </button>
        </div>

        <nav className="ihv-staff-home__nav">
          {hasCap(caps, 'staff.manage') && (
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary"
              onClick={() => go('/staff/equipo')}>Gestión de equipo</button>
          )}
          {hasCap(caps, 'segments.manage') && (
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary"
              onClick={() => go('/staff/segmentos')}>Segmentos</button>
          )}
          {hasCap(caps, 'analytics.view') && (
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" onClick={() => go('/staff/analytics')}>Analíticas</button>
          )}
          {hasCap(caps, 'clients.read') && (
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" onClick={() => go('/staff/clientes')}>Clientes</button>
          )}
          {hasCap(caps, 'sheet.read') && (
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" onClick={() => go('/staff/hoja')}>Hoja</button>
          )}
          {hasCap(caps, 'audit.view') && (
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" onClick={() => go('/staff/auditoria')}>Auditoría</button>
          )}
        </nav>
      </div>
    </div>
  );
}

function readHashQuery() {
  const hash = window.location.hash || '';
  const qIndex = hash.indexOf('?');
  const out = {};
  if (qIndex === -1) return out;
  const qs = hash.slice(qIndex + 1);
  for (const pair of qs.split('&')) {
    const [k, v] = pair.split('=');
    if (k) out[decodeURIComponent(k)] = decodeURIComponent(v || '');
  }
  return out;
}

function StaffQuotesListView({ session, caps }) {
  const sb = window.ihvSupabase;
  const preset = readHashQuery();
  const [rows, setRows] = useState(null);
  const [error, setError] = useState(null);
  const [search, setSearch] = useState('');
  const [filters, setFilters] = useState({
    status: '',
    request_type: '',
    urgency: preset.urgency || '',
    mine: preset.mine === '1',
    unassigned: preset.unassigned === '1',
  });
  const [selected, setSelected] = useState({});
  const [savedFilters, setSavedFilters] = useState([]);
  const [bulkBusy, setBulkBusy] = useState(false);
  const uid = session && session.user ? session.user.id : null;

  const loadSaved = useCallback(async () => {
    const { data } = await sb.from('staff_saved_filters')
      .select('id, name, filter').order('created_at', { ascending: false });
    setSavedFilters(data || []);
  }, [sb]);

  const load = useCallback(async () => {
    setError(null);
    if (search.trim()) {
      const { data, error: err } = await sb.rpc('staff_search_quotes', { p_term: search.trim() });
      if (err) { setError('No pudimos buscar.'); return; }
      setRows(data || []);
      return;
    }
    let q = sb.from('quote_requests')
      .select('id, reference_code, project_name, request_type, status, urgency, estimated_amount_usd, currency, assigned_to, manual_tag, valid_until, created_at')
      .order('created_at', { ascending: false })
      .limit(200);
    if (filters.status) q = q.eq('status', filters.status);
    if (filters.request_type) q = q.eq('request_type', filters.request_type);
    if (filters.urgency) q = q.eq('urgency', filters.urgency);
    if (filters.mine && uid) q = q.eq('assigned_to', uid);
    if (filters.unassigned) q = q.is('assigned_to', null);
    const { data, error: err } = await q;
    if (err) { setError('No pudimos cargar las cotizaciones.'); return; }
    setRows(data || []);
  }, [sb, search, filters, uid]);

  useEffect(() => { load(); }, [load]);
  useEffect(() => { loadSaved(); }, [loadSaved]);

  const setF = (k, v) => setFilters((prev) => ({ ...prev, [k]: v }));
  const clearFilters = () => { setSearch(''); setFilters({ status: '', request_type: '', urgency: '', mine: false, unassigned: false }); };

  const selectedIds = Object.keys(selected).filter((k) => selected[k]);
  const toggleSel = (id) => setSelected((prev) => ({ ...prev, [id]: !prev[id] }));

  const onSaveFilter = async () => {
    const name = window.prompt('Nombre del filtro guardado:');
    if (!name) return;
    await sb.from('staff_saved_filters').insert({
      user_id: uid, name: name.slice(0, 80),
      filter: { search, ...filters },
    });
    loadSaved();
  };

  const applySaved = (f) => {
    const v = f.filter || {};
    setSearch(v.search || '');
    setFilters({
      status: v.status || '', request_type: v.request_type || '',
      urgency: v.urgency || '', mine: !!v.mine, unassigned: !!v.unassigned,
    });
  };

  const onDeleteSaved = async (id) => {
    await sb.from('staff_saved_filters').delete().eq('id', id);
    loadSaved();
  };

  const onBulk = async (status) => {
    if (!selectedIds.length) return;
    setBulkBusy(true);
    const { error: err } = await sb.rpc('staff_bulk_update', {
      p_quote_ids: selectedIds, p_status: status, p_assignee: null,
    });
    setBulkBusy(false);
    if (err) { setError(translateRpcError(err)); return; }
    setSelected({});
    load();
  };

  const onClaimMine = async () => {
    if (!selectedIds.length) return;
    setBulkBusy(true);
    const { error: err } = await sb.rpc('staff_bulk_update', {
      p_quote_ids: selectedIds, p_status: null, p_assignee: uid,
    });
    setBulkBusy(false);
    if (err) { setError(translateRpcError(err)); return; }
    setSelected({});
    load();
  };

  const canBulk = hasCap(caps, 'quotes.bulk');

  return (
    <div className="ihv-portal-card">
      <div className="ihv-portal-card__header">
        <h1 className="ihv-portal-card__title">Cotizaciones</h1>
        <a className="ihv-portal-btn ihv-portal-btn--ghost" href="#/staff">Mi trabajo</a>
      </div>
      <div className="ihv-portal-card__body">
        <FieldError>{error}</FieldError>

        <div className="ihv-staff-toolbar">
          <input className="ihv-portal-form__input" type="search" placeholder="Buscar por referencia, empresa o RIF"
            value={search} onChange={(e) => setSearch(e.target.value)} />
          <select className="ihv-portal-form__input" value={filters.status} onChange={(e) => setF('status', e.target.value)}>
            <option value="">Todos los estados</option>
            {Object.keys(STATUS_LABELS).map((s) => <option key={s} value={s}>{STATUS_LABELS[s]}</option>)}
          </select>
          <select className="ihv-portal-form__input" value={filters.request_type} onChange={(e) => setF('request_type', e.target.value)}>
            <option value="">Todos los tipos</option>
            <option value="suministros">Suministros</option>
            <option value="obras">Obras</option>
          </select>
          <select className="ihv-portal-form__input" value={filters.urgency} onChange={(e) => setF('urgency', e.target.value)}>
            <option value="">Toda urgencia</option>
            {Object.keys(URGENCY_LABELS).map((u) => <option key={u} value={u}>{URGENCY_LABELS[u]}</option>)}
          </select>
          <label className="ihv-staff-toolbar__check">
            <input type="checkbox" checked={filters.mine} onChange={(e) => setF('mine', e.target.checked)} /> Mías
          </label>
          <label className="ihv-staff-toolbar__check">
            <input type="checkbox" checked={filters.unassigned} onChange={(e) => setF('unassigned', e.target.checked)} /> Sin asignar
          </label>
          <button type="button" className="ihv-portal-btn ihv-portal-btn--ghost" onClick={clearFilters}>Limpiar</button>
          <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" onClick={onSaveFilter}>Guardar filtro</button>
        </div>

        {savedFilters.length > 0 && (
          <div className="ihv-staff-saved">
            {savedFilters.map((f) => (
              <span key={f.id} className="ihv-staff-saved__chip">
                <button type="button" className="ihv-staff-saved__apply" onClick={() => applySaved(f)}>{f.name}</button>
                <button type="button" className="ihv-staff-saved__del" aria-label="Eliminar filtro" onClick={() => onDeleteSaved(f.id)}>×</button>
              </span>
            ))}
          </div>
        )}

        {canBulk && selectedIds.length > 0 && (
          <div className="ihv-staff-bulkbar">
            <span>{selectedIds.length} seleccionadas</span>
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" disabled={bulkBusy} onClick={onClaimMine}>Asignármelas</button>
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" disabled={bulkBusy} onClick={() => onBulk('en_revision')}>Marcar en revisión</button>
            <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" disabled={bulkBusy} onClick={() => onBulk('archivada')}>Archivar</button>
          </div>
        )}

        {rows === null ? <p>Cargando.</p> : rows.length === 0 ? (
          <div className="ihv-portal-empty"><p>No hay cotizaciones que coincidan.</p></div>
        ) : (
          <table className="ihv-portal-table">
            <thead>
              <tr className="ihv-portal-table__row">
                {canBulk && <th className="ihv-portal-table__cell"></th>}
                <th className="ihv-portal-table__cell">Referencia</th>
                <th className="ihv-portal-table__cell">Proyecto</th>
                <th className="ihv-portal-table__cell">Tipo</th>
                <th className="ihv-portal-table__cell">Urgencia</th>
                <th className="ihv-portal-table__cell">Estado</th>
                <th className="ihv-portal-table__cell">Monto</th>
                <th className="ihv-portal-table__cell">Asignada</th>
                <th className="ihv-portal-table__cell">Creada</th>
              </tr>
            </thead>
            <tbody>
              {rows.map((r) => (
                <tr key={r.id} className="ihv-portal-table__row">
                  {canBulk && (
                    <td className="ihv-portal-table__cell" onClick={(e) => e.stopPropagation()}>
                      <input type="checkbox" checked={!!selected[r.id]} onChange={() => toggleSel(r.id)} />
                    </td>
                  )}
                  <td className="ihv-portal-table__cell" style={{ fontFamily: 'var(--font-mono)' }}
                      onClick={() => navigate('/staff/cotizaciones/' + r.id)}>{r.reference_code}</td>
                  <td className="ihv-portal-table__cell" onClick={() => navigate('/staff/cotizaciones/' + r.id)}>{r.project_name || '—'}</td>
                  <td className="ihv-portal-table__cell">{r.request_type === 'obras' ? 'Obras' : 'Suministros'}</td>
                  <td className="ihv-portal-table__cell">{URGENCY_LABELS[r.urgency] || r.urgency}</td>
                  <td className="ihv-portal-table__cell"><StatusBadge status={r.status} /></td>
                  <td className="ihv-portal-table__cell">{formatAmount(r.estimated_amount_usd, r.currency)}</td>
                  <td className="ihv-portal-table__cell">{r.assigned_to ? (r.assigned_to === uid ? 'Yo' : 'Sí') : '—'}</td>
                  <td className="ihv-portal-table__cell">{formatDate(r.created_at)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

function generateQuotePdf(quote, items, works) {
  const JsPDF = window.jspdf && window.jspdf.jsPDF;
  if (!JsPDF) { alert('La librería de PDF no cargó. Reintenta.'); return; }
  const doc = new JsPDF({ unit: 'pt', format: 'a4' });
  doc.setFontSize(16);
  doc.text('Inversiones Hermanos Valero, C.A.', 40, 50);
  doc.setFontSize(11);
  doc.text('Cotización ' + (quote.reference_code || ''), 40, 72);
  doc.text('Estado: ' + (quote.status || ''), 40, 90);
  doc.text('Tipo: ' + (quote.request_type || ''), 40, 108);
  if (quote.estimated_amount_usd != null) doc.text('Monto estimado: US$ ' + Number(quote.estimated_amount_usd).toLocaleString('es-VE'), 40, 126);
  if (quote.valid_until) doc.text('Vigencia: ' + quote.valid_until, 40, 144);
  const startY = 170;
  if (quote.request_type === 'suministros' && items && items.length && doc.autoTable) {
    doc.autoTable({
      startY,
      head: [['Descripción', 'Cantidad', 'Unidad']],
      body: items.map((it) => [it.description || '', it.quantity != null ? String(it.quantity) : '', it.unit || '']),
      styles: { fontSize: 9 }, headStyles: { fillColor: [30, 79, 168] },
    });
  } else if (quote.request_type === 'obras' && works) {
    doc.setFontSize(10);
    doc.text('Alcance: ' + (works.scope_description || '—'), 40, startY, { maxWidth: 515 });
  }
  doc.save('cotizacion-' + (quote.reference_code || 'IHV') + '.pdf');
}

function StaffQuoteDetailView({ id, session, caps }) {
  const sb = window.ihvSupabase;
  const uid = session && session.user ? session.user.id : null;
  const [quote, setQuote] = useState(null);
  const [items, setItems] = useState([]);
  const [works, setWorks] = useState(null);
  const [history, setHistory] = useState([]);
  const [client, setClient] = useState(null);
  const [staffList, setStaffList] = useState([]);
  const [error, setError] = useState(null);
  const [actionError, setActionError] = useState(null);
  const [busy, setBusy] = useState(false);
  const [form, setForm] = useState(null);

  const load = useCallback(async () => {
    setError(null);
    const { data: q, error: qe } = await sb.from('quote_requests').select('*').eq('id', id).maybeSingle();
    if (qe || !q) { setError('No encontramos la cotización o no tienes acceso.'); return; }
    setQuote(q);
    setForm({
      status: q.status,
      estimated_amount_usd: q.estimated_amount_usd != null ? String(q.estimated_amount_usd) : '',
      currency: q.currency || 'USD',
      exchange_rate_to_usd: q.exchange_rate_to_usd != null ? String(q.exchange_rate_to_usd) : '',
      internal_notes: q.internal_notes || '',
      manual_tag: q.manual_tag || '',
      valid_until: q.valid_until || '',
    });
    const [it, wd, hi, cp, sl] = await Promise.all([
      q.request_type === 'suministros'
        ? sb.from('quote_supply_items').select('*').eq('quote_id', id) : Promise.resolve({ data: [] }),
      q.request_type === 'obras'
        ? sb.from('quote_works_details').select('*').eq('quote_id', id).maybeSingle() : Promise.resolve({ data: null }),
      sb.from('quote_status_history').select('*').eq('quote_id', id).order('changed_at', { ascending: true }),
      sb.from('client_profiles').select('full_name, company_name, company_rif, email, phone, whatsapp')
        .eq('user_id', q.client_user_id).maybeSingle(),
      hasCap(caps, 'quotes.assign')
        ? sb.from('staff_profiles').select('user_id, full_name').eq('is_active', true) : Promise.resolve({ data: [] }),
    ]);
    setItems(it.data || []);
    setWorks(wd.data || null);
    setHistory(hi.data || []);
    setClient(cp.data || null);
    setStaffList(sl.data || []);
  }, [sb, id, caps]);

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

  const setF = (k, v) => setForm((prev) => ({ ...prev, [k]: v }));

  const onSave = async () => {
    setActionError(null); setBusy(true);
    const { error: err } = await sb.rpc('staff_update_quote', {
      p_quote_id: id,
      p_status: form.status || null,
      p_estimated_amount_usd: form.estimated_amount_usd === '' ? null : Number(form.estimated_amount_usd),
      p_currency: form.currency || null,
      p_exchange_rate_to_usd: form.exchange_rate_to_usd === '' ? null : Number(form.exchange_rate_to_usd),
      p_internal_notes: form.internal_notes || null,
      p_manual_tag: form.manual_tag || null,
      p_valid_until: form.valid_until || null,
    });
    setBusy(false);
    if (err) { setActionError(translateRpcError(err)); return; }
    load();
  };

  const onClaim = async () => {
    setActionError(null); setBusy(true);
    const { error: err } = await sb.rpc('staff_claim_quote', { p_quote_id: id });
    setBusy(false);
    if (err) { setActionError(translateRpcError(err)); return; }
    load();
  };

  const onAssign = async (assignee) => {
    if (!assignee) return;
    setActionError(null); setBusy(true);
    const { error: err } = await sb.rpc('staff_assign_quote', { p_quote_id: id, p_assignee: assignee });
    setBusy(false);
    if (err) { setActionError(translateRpcError(err)); return; }
    load();
  };

  const onApprove = async () => {
    setActionError(null); setBusy(true);
    const { error: err } = await sb.rpc('staff_approve_quote', { p_quote_id: id });
    setBusy(false);
    if (err) { setActionError(translateRpcError(err)); return; }
    load();
  };

  if (!quote || !form) {
    return (
      <div className="ihv-portal-card">
        <div className="ihv-portal-card__header">
          <h1 className="ihv-portal-card__title">Cotización</h1>
          <a className="ihv-portal-btn ihv-portal-btn--ghost" href="#/staff/cotizaciones">Volver</a>
        </div>
        <div className="ihv-portal-card__body">{error ? <FieldError>{error}</FieldError> : <p>Cargando.</p>}</div>
      </div>
    );
  }

  const canEdit = hasCap(caps, 'quotes.edit');
  const canClaim = hasCap(caps, 'quotes.claim');
  const canAssign = hasCap(caps, 'quotes.assign');
  const canApprove = hasCap(caps, 'quotes.approve');

  return (
    <div className="ihv-portal-card ihv-staff-detail">
      <div className="ihv-portal-card__header">
        <h1 className="ihv-portal-card__title" style={{ fontFamily: 'var(--font-mono)' }}>{quote.reference_code}</h1>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary"
            onClick={() => generateQuotePdf(quote, items, works)}>Descargar PDF</button>
          <a className="ihv-portal-btn ihv-portal-btn--ghost" href="#/staff/cotizaciones">Volver al listado</a>
        </div>
      </div>
      <div className="ihv-portal-card__body">
        <FieldError>{actionError}</FieldError>

        <div className="ihv-staff-detail__grid">
          <div>
            <p><strong>Proyecto:</strong> {quote.project_name || '—'}</p>
            <p><strong>Tipo:</strong> {quote.request_type === 'obras' ? 'Obras' : 'Suministros'}</p>
            <p><strong>Urgencia:</strong> {URGENCY_LABELS[quote.urgency] || quote.urgency}</p>
            <p><strong>Estado:</strong> <StatusBadge status={quote.status} /></p>
            <p><strong>Asignada:</strong> {quote.assigned_to ? (quote.assigned_to === uid ? 'A mí' : 'A otro miembro') : 'Sin asignar'}</p>
            {quote.valid_until && (
              <p><strong>Vigencia:</strong> {formatDate(quote.valid_until)}{isExpired(quote.valid_until) && <span className="ihv-staff-expired"> · Vencida</span>}</p>
            )}
            {quote.approved_at && <p><strong>Aprobada:</strong> {formatDateTime(quote.approved_at)}</p>}
          </div>
          <div>
            <p><strong>Cliente:</strong> {client ? (client.company_name || client.full_name || '—') : '—'}</p>
            {client && client.company_rif && <p><strong>RIF:</strong> {client.company_rif}</p>}
            {client && client.email && <p><strong>Correo:</strong> {client.email}</p>}
            {client && (client.whatsapp || client.phone) && <p><strong>Tel:</strong> {client.whatsapp || client.phone}</p>}
          </div>
        </div>

        {quote.request_type === 'suministros' && items.length > 0 && (
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label">Artículos</label>
            <table className="ihv-portal-table">
              <thead><tr className="ihv-portal-table__row">
                <th className="ihv-portal-table__cell">Descripción</th>
                <th className="ihv-portal-table__cell">Cantidad</th>
                <th className="ihv-portal-table__cell">Unidad</th>
              </tr></thead>
              <tbody>
                {items.map((it) => (
                  <tr key={it.id} className="ihv-portal-table__row">
                    <td className="ihv-portal-table__cell">{it.description}</td>
                    <td className="ihv-portal-table__cell">{it.quantity}</td>
                    <td className="ihv-portal-table__cell">{it.unit || '—'}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}

        {quote.request_type === 'obras' && works && (
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label">Detalles de la obra</label>
            <p><strong>Alcance:</strong> {works.scope_description}</p>
            {works.work_type && <p><strong>Tipo:</strong> {works.work_type}</p>}
            {works.site_address && <p><strong>Dirección:</strong> {works.site_address}</p>}
            {works.estimated_area_m2 && <p><strong>Área:</strong> {works.estimated_area_m2} m²</p>}
            {works.budget_range && <p><strong>Presupuesto:</strong> {works.budget_range}</p>}
          </div>
        )}

        {canEdit && (
          <div className="ihv-staff-edit">
            <h2 className="ihv-staff-edit__title">Gestión</h2>
            <div className="ihv-staff-edit__grid">
              <label className="ihv-portal-form__field">
                <span className="ihv-portal-form__label">Estado</span>
                <select className="ihv-portal-form__input" value={form.status} onChange={(e) => setF('status', e.target.value)}>
                  {Object.keys(STATUS_LABELS).map((s) => <option key={s} value={s}>{STATUS_LABELS[s]}</option>)}
                </select>
              </label>
              <label className="ihv-portal-form__field">
                <span className="ihv-portal-form__label">Monto estimado (USD)</span>
                <input className="ihv-portal-form__input" type="number" min="0" step="0.01"
                  value={form.estimated_amount_usd} onChange={(e) => setF('estimated_amount_usd', e.target.value)} />
              </label>
              <label className="ihv-portal-form__field">
                <span className="ihv-portal-form__label">Moneda</span>
                <select className="ihv-portal-form__input" value={form.currency} onChange={(e) => setF('currency', e.target.value)}>
                  <option value="USD">USD</option>
                  <option value="VES">VES</option>
                </select>
              </label>
              {form.currency === 'VES' && (
                <label className="ihv-portal-form__field">
                  <span className="ihv-portal-form__label">Tasa a USD</span>
                  <input className="ihv-portal-form__input" type="number" min="0" step="0.000001"
                    value={form.exchange_rate_to_usd} onChange={(e) => setF('exchange_rate_to_usd', e.target.value)} />
                </label>
              )}
              <label className="ihv-portal-form__field">
                <span className="ihv-portal-form__label">Vigencia (hasta)</span>
                <input className="ihv-portal-form__input" type="date"
                  value={form.valid_until} onChange={(e) => setF('valid_until', e.target.value)} />
              </label>
              <label className="ihv-portal-form__field">
                <span className="ihv-portal-form__label">Etiqueta manual</span>
                <input className="ihv-portal-form__input" type="text" maxLength="100"
                  value={form.manual_tag} onChange={(e) => setF('manual_tag', e.target.value)} />
              </label>
            </div>
            <label className="ihv-portal-form__field">
              <span className="ihv-portal-form__label">Notas internas</span>
              <textarea className="ihv-portal-form__input" rows="3" maxLength="5000"
                value={form.internal_notes} onChange={(e) => setF('internal_notes', e.target.value)} />
            </label>
            <button type="button" className="ihv-portal-btn ihv-portal-btn--primary" disabled={busy} onClick={onSave}>
              {busy ? 'Guardando' : 'Guardar cambios'}
            </button>
          </div>
        )}

        <div className="ihv-portal-form__field">
          <label className="ihv-portal-form__label">Historial</label>
          <ol className="ihv-portal-timeline">
            {history.map((h) => (
              <li key={h.id} className="ihv-portal-timeline__item">
                <div className="ihv-portal-form__hint">{formatDateTime(h.changed_at)}</div>
                <div>
                  {(h.from_status ? (STATUS_LABELS[h.from_status] || h.from_status) + ' → ' : '')}
                  <strong>{STATUS_LABELS[h.to_status] || h.to_status}</strong>
                </div>
              </li>
            ))}
          </ol>
        </div>

        <div className="ihv-portal-card__footer ihv-portal-card__footer--split">
          <a href="#/staff/cotizaciones" className="ihv-portal-btn ihv-portal-btn--ghost">Volver al listado</a>
          <div className="ihv-portal-card__footer-actions">
            {canClaim && !quote.assigned_to && (
              <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" disabled={busy} onClick={onClaim}>Tomar</button>
            )}
            {canApprove && !quote.approved_at && (
              <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" disabled={busy} onClick={onApprove}>Aprobar</button>
            )}
            {canAssign && (
              <select className="ihv-portal-form__input" defaultValue="" disabled={busy}
                onChange={(e) => { onAssign(e.target.value); e.target.value = ''; }}>
                <option value="">Asignar a…</option>
                {staffList.map((s) => <option key={s.user_id} value={s.user_id}>{s.full_name}</option>)}
              </select>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

function ProfileGate({ session, profile, onSaved }) {
  const [fullName, setFullName] = useState(profile && profile.full_name ? profile.full_name : '');
  const [companyName, setCompanyName] = useState(profile && profile.company_name ? profile.company_name : '');
  const [rif, setRif] = useState(profile && profile.company_rif ? profile.company_rif : '');
  const [phone, setPhone] = useState(profile && profile.phone ? profile.phone : '');
  const initialWa = splitE164(profile && profile.whatsapp ? profile.whatsapp : '');
  const [waCc, setWaCc] = useState(initialWa.cc);
  const [waLocal, setWaLocal] = useState(initialWa.local);
  const [error, setError] = useState(null);
  const [busy, setBusy] = useState(false);

  const onSubmit = async (e) => {
    e.preventDefault();
    setError(null);
    const name = stripCtrl(fullName).trim();
    const empresa = stripCtrl(companyName).trim();
    const tel = stripCtrl(phone).trim();
    if (name.length < 2) { setError('Ingresa tu nombre completo.'); return; }
    if (empresa.length < 2) { setError('Ingresa el nombre de la empresa.'); return; }
    const normalRif = normalizeRif(rif);
    if (!normalRif) { setError('RIF no válido. Formato: J-12345678-9.'); return; }
    if (tel.length < 7) { setError('Ingresa un teléfono válido.'); return; }
    const wa = composeE164(waCc, waLocal);
    if (wa === false) { setError('Número de WhatsApp no válido. Revisa el código de país y los dígitos.'); return; }

    setBusy(true);
    const payload = {
      user_id: session.user.id,
      email: session.user.email,
      full_name: name,
      company_name: empresa,
      company_rif: normalRif,
      phone: tel,
      whatsapp: wa
    };
    const { error: err } = await window.ihvSupabase
      .from('client_profiles')
      .upsert(payload, { onConflict: 'user_id' });
    setBusy(false);
    if (err) { setError('No pudimos guardar el perfil. Inténtalo de nuevo.'); return; }
    onSaved(payload);
  };

  return (
    <div className="ihv-portal-card">
      <h1 className="ihv-portal-card__title">Completa los datos de tu empresa</h1>
      <div className="ihv-portal-card__body">
        <p>Antes de enviar una cotización, necesitamos algunos datos básicos de tu empresa.</p>
        <form className="ihv-portal-form" onSubmit={onSubmit} noValidate>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="pg-name">Nombre completo</label>
            <input id="pg-name" className="ihv-portal-form__input" type="text" value={fullName}
              onChange={(e) => setFullName(e.target.value)} required />
          </div>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="pg-empresa">Empresa</label>
            <input id="pg-empresa" className="ihv-portal-form__input" type="text" value={companyName}
              onChange={(e) => setCompanyName(e.target.value)} required />
          </div>
          <div className="ihv-portal-form__row">
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="pg-rif">RIF</label>
              <input id="pg-rif" className="ihv-portal-form__input" type="text" value={rif}
                onChange={(e) => setRif(e.target.value)} placeholder="J-12345678-9" required />
            </div>
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="pg-tel">Teléfono</label>
              <input id="pg-tel" className="ihv-portal-form__input" type="tel" value={phone}
                onChange={(e) => setPhone(e.target.value)} required />
            </div>
          </div>
          <WhatsappField idPrefix="pg" cc={waCc} local={waLocal}
            onCcChange={setWaCc} onLocalChange={setWaLocal} optional />
          <FieldError>{error}</FieldError>
          <button className="ihv-portal-btn ihv-portal-btn--primary" type="submit" disabled={busy}>
            {busy ? 'Guardando' : 'Guardar y continuar'}
          </button>
        </form>
      </div>
    </div>
  );
}

function emptySupplyItem() {
  return { description: '', quantity: '', unit: '', notes: '' };
}

function NuevaCotizacionView({ session, initialTab }) {
  const [profile, setProfile] = useState(null);
  const [profileLoaded, setProfileLoaded] = useState(false);
  const [tab, setTab] = useState(initialTab === 'obras' || initialTab === 'suministros' ? initialTab : 'suministros');
  const [projectName, setProjectName] = useState('');
  const [items, setItems] = useState([emptySupplyItem()]);
  const [scope, setScope] = useState('');
  const [siteAddress, setSiteAddress] = useState('');
  const [workType, setWorkType] = useState('');
  const [location, setLocation] = useState('');
  const [areaM2, setAreaM2] = useState('');
  const [budgetRange, setBudgetRange] = useState('');
  const [notes, setNotes] = useState('');
  const [urgency, setUrgency] = useState('normal');
  const [desiredDelivery, setDesiredDelivery] = useState('');
  const [currency, setCurrency] = useState('USD');
  const [exchangeRate, setExchangeRate] = useState('');
  const [rateInfo, setRateInfo] = useState(null);
  const [rateLoading, setRateLoading] = useState(false);
  const [attachments, setAttachments] = useState([]);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (currency !== 'VES') return;
    let cancelled = false;
    setRateLoading(true);
    window.ihvSupabase
      .from('exchange_rates')
      .select('date, rate_ves_per_usd, source')
      .order('date', { ascending: false })
      .limit(1)
      .maybeSingle()
      .then(({ data }) => {
        if (cancelled) return;
        setRateLoading(false);
        if (data && data.rate_ves_per_usd) {
          setRateInfo({ date: data.date, rate: data.rate_ves_per_usd, source: data.source });
          setExchangeRate(String(data.rate_ves_per_usd));
        } else {
          setRateInfo(null);
        }
      });
    return () => { cancelled = true; };
  }, [currency]);

  useEffect(() => {
    let cancelled = false;
    window.ihvSupabase
      .from('client_profiles')
      .select('full_name, email, phone, company_name, company_rif, whatsapp')
      .eq('user_id', session.user.id)
      .maybeSingle()
      .then(({ data }) => { if (!cancelled) { setProfile(data || {}); setProfileLoaded(true); } });
    return () => { cancelled = true; };
  }, [session.user.id]);

  if (!profileLoaded) {
    return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>Cargando.</p></div></div>;
  }

  const profileComplete = profile && profile.company_name && profile.company_rif && profile.phone;
  if (!profileComplete) {
    return <ProfileGate session={session} profile={profile} onSaved={(p) => setProfile(p)} />;
  }

  const updateItem = (idx, key, value) => {
    setItems((arr) => arr.map((it, i) => i === idx ? { ...it, [key]: value } : it));
  };
  const addItem = () => {
    if (items.length >= 100) return;
    setItems((arr) => [...arr, emptySupplyItem()]);
  };
  const removeItem = (idx) => {
    setItems((arr) => arr.length > 1 ? arr.filter((_, i) => i !== idx) : arr);
  };

  const onFiles = async (e) => {
    const files = Array.from(e.target.files || []);
    e.target.value = '';
    if (!files.length) return;
    const sb = window.ihvSupabase;
    for (const file of files) {
      const safeName = sanitizeFilename(file.name);
      const path = session.user.id + '/' + (crypto.randomUUID ? crypto.randomUUID() : Date.now() + '-' + Math.random().toString(36).slice(2)) + '-' + safeName;
      const entry = {
        key: path,
        path,
        original_filename: safeName,
        size_bytes: file.size,
        mime_type: file.type || 'application/octet-stream',
        state: 'subiendo'
      };
      setAttachments((arr) => [...arr, entry]);
      try {
        const { error: upErr } = await sb.storage.from('quote-attachments').upload(path, file, {
          contentType: entry.mime_type, upsert: false
        });
        if (upErr) throw upErr;
        setAttachments((arr) => arr.map((a) => a.key === path ? { ...a, state: 'subido' } : a));
      } catch (_) {
        setAttachments((arr) => arr.map((a) => a.key === path ? { ...a, state: 'error' } : a));
      }
    }
  };

  const removeAttachment = (key) => {
    setAttachments((arr) => arr.filter((a) => a.key !== key));
  };

  const onSubmit = async (e) => {
    e.preventDefault();
    setError(null);

    const cleanNotes = stripCtrl(notes).trim();
    if (cleanNotes.length > 2000) { setError('Las notas exceden 2.000 caracteres.'); return; }

    const cleanProjectName = stripCtrl(projectName).trim();
    if (cleanProjectName.length < 1) {
      setError(tab === 'obras' ? 'Ingresa un nombre para el proyecto u obra.' : 'Ingresa un nombre para el suministro.');
      return;
    }
    if (cleanProjectName.length > 200) { setError('El nombre no puede exceder 200 caracteres.'); return; }

    if (tab === 'suministros') {
      if (items.length < 1 || items.length > 100) { setError('Incluye entre 1 y 100 artículos.'); return; }
      for (let i = 0; i < items.length; i++) {
        const it = items[i];
        const desc = stripCtrl(it.description).trim();
        const qty = Number(it.quantity);
        if (desc.length < 1 || desc.length > 500) { setError('El artículo ' + (i + 1) + ' necesita descripción de 1 a 500 caracteres.'); return; }
        if (!isFinite(qty) || qty <= 0 || qty > 1e6) { setError('La cantidad del artículo ' + (i + 1) + ' debe ser mayor a cero.'); return; }
      }
    } else {
      const s = stripCtrl(scope).trim();
      if (s.length < 1 || s.length > 5000) { setError('El alcance debe tener entre 1 y 5.000 caracteres.'); return; }
    }

    if (currency === 'VES' && exchangeRate) {
      const r = Number(exchangeRate);
      if (!isFinite(r) || r <= 0) { setError('Tasa de cambio no válida.'); return; }
    }

    if (desiredDelivery && desiredDelivery < todayLocalISO()) {
      setError('La fecha de entrega deseada no puede ser anterior a hoy.');
      return;
    }

    const uploading = attachments.some((a) => a.state === 'subiendo');
    if (uploading) { setError('Espera a que terminen de subirse los adjuntos.'); return; }

    setSubmitting(true);
    const sb = window.ihvSupabase;
    const payload = {
      p_request_type: tab,
      p_urgency: urgency,
      p_desired_delivery: desiredDelivery || null,
      p_notes: cleanNotes || null,
      p_supply_items: tab === 'suministros'
        ? items.map((it) => ({
            description: stripCtrl(it.description).trim(),
            quantity: String(Number(it.quantity)),
            unit: stripCtrl(it.unit).trim() || null,
            notes: stripCtrl(it.notes).trim() || null
          }))
        : [],
      p_works_details: tab === 'obras' ? {
        work_type: workType || null,
        location: stripCtrl(location).trim() || null,
        site_address: stripCtrl(siteAddress).trim() || null,
        scope_description: stripCtrl(scope).trim(),
        estimated_area_m2: areaM2 ? String(Number(areaM2)) : '',
        budget_range: stripCtrl(budgetRange).trim() || null
      } : null,
      p_currency: currency,
      p_exchange_rate_to_usd: currency === 'VES' && exchangeRate ? Number(exchangeRate) : null,
      p_project_name: cleanProjectName
    };

    const { data, error: err } = await sb.rpc('submit_quote_request', payload);
    if (err) { setSubmitting(false); setError(translateRpcError(err)); return; }

    const quote = Array.isArray(data) ? data[0] : data;
    const quoteId = quote && quote.id;

    const ready = attachments.filter((a) => a.state === 'subido');
    if (quoteId && ready.length) {
      const rows = ready.map((a) => ({
        quote_id: quoteId,
        storage_path: a.path,
        filename: a.original_filename,
        mime_type: a.mime_type,
        size_bytes: a.size_bytes,
        uploaded_by: session.user.id
      }));
      await sb.from('quote_attachments').insert(rows);
    }

    setSubmitting(false);
    if (quoteId) navigate('/cotizaciones/' + quoteId);
    else navigate('/cotizaciones');
  };

  return (
    <div className="ihv-portal-card">
      <div className="ihv-portal-card__header">
        <h1 className="ihv-portal-card__title">Nueva cotización</h1>
        <a className="ihv-portal-btn ihv-portal-btn--ghost" href="#/cotizaciones">Volver al listado</a>
      </div>
      <div className="ihv-portal-card__body">
        <div className="ihv-portal-tabs" role="tablist">
          <button type="button" role="tab" aria-selected={tab === 'suministros'}
            className={'ihv-portal-tab' + (tab === 'suministros' ? ' ihv-portal-tab--active' : '')}
            onClick={() => setTab('suministros')}>Suministros</button>
          <button type="button" role="tab" aria-selected={tab === 'obras'}
            className={'ihv-portal-tab' + (tab === 'obras' ? ' ihv-portal-tab--active' : '')}
            onClick={() => setTab('obras')}>Obras</button>
        </div>

        <form className="ihv-portal-form" onSubmit={onSubmit} noValidate>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="nc-name">
              {tab === 'obras' ? 'Nombre del proyecto u obra' : 'Nombre del suministro'}
            </label>
            <input id="nc-name" className="ihv-portal-form__input" type="text" maxLength={200}
              value={projectName} onChange={(e) => setProjectName(e.target.value)}
              placeholder={tab === 'obras' ? 'Ej. Remodelación oficinas piso 4' : 'Ej. Suministros oficina central'}
              required />
          </div>

          {tab === 'suministros' ? (
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label">Artículos</label>
              {items.map((it, idx) => (
                <div key={idx} className="ihv-portal-form__row">
                  <input className="ihv-portal-form__input" type="text" placeholder="Descripción"
                    value={it.description} maxLength={500}
                    onChange={(e) => updateItem(idx, 'description', e.target.value)} />
                  <input className="ihv-portal-form__input" type="number" placeholder="Cantidad"
                    value={it.quantity} min="0" step="any"
                    onChange={(e) => updateItem(idx, 'quantity', e.target.value)} />
                  <input className="ihv-portal-form__input" type="text" placeholder="Unidad (opcional)"
                    value={it.unit} onChange={(e) => updateItem(idx, 'unit', e.target.value)} />
                  <button type="button" className="ihv-portal-btn ihv-portal-btn--ghost"
                    onClick={() => removeItem(idx)} disabled={items.length === 1}>Quitar</button>
                </div>
              ))}
              <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" onClick={addItem}
                disabled={items.length >= 100}>Agregar otro artículo</button>
            </div>
          ) : (
            <>
              <div className="ihv-portal-form__field">
                <label className="ihv-portal-form__label" htmlFor="nc-scope">Alcance del proyecto</label>
                <textarea id="nc-scope" className="ihv-portal-form__textarea" rows={6} maxLength={5000}
                  value={scope} onChange={(e) => setScope(e.target.value)} required />
              </div>
              <div className="ihv-portal-form__row">
                <div className="ihv-portal-form__field">
                  <label className="ihv-portal-form__label" htmlFor="nc-wt">Tipo de obra</label>
                  <select id="nc-wt" className="ihv-portal-form__select" value={workType}
                    onChange={(e) => setWorkType(e.target.value)}>
                    <option value="">Seleccionar</option>
                    <option value="civil">Civil</option>
                    <option value="electrica">Eléctrica</option>
                    <option value="sanitaria">Sanitaria</option>
                    <option value="mantenimiento">Mantenimiento</option>
                    <option value="otro">Otro</option>
                  </select>
                </div>
                <div className="ihv-portal-form__field">
                  <label className="ihv-portal-form__label" htmlFor="nc-loc">Localidad</label>
                  <input id="nc-loc" className="ihv-portal-form__input" type="text" value={location}
                    onChange={(e) => setLocation(e.target.value)} />
                </div>
              </div>
              <div className="ihv-portal-form__field">
                <label className="ihv-portal-form__label" htmlFor="nc-addr">Dirección de la obra</label>
                <input id="nc-addr" className="ihv-portal-form__input" type="text" value={siteAddress}
                  onChange={(e) => setSiteAddress(e.target.value)} />
              </div>
              <div className="ihv-portal-form__row">
                <div className="ihv-portal-form__field">
                  <label className="ihv-portal-form__label" htmlFor="nc-area">Área estimada (m²)</label>
                  <input id="nc-area" className="ihv-portal-form__input" type="number" value={areaM2}
                    min="0" step="any" onChange={(e) => setAreaM2(e.target.value)} />
                </div>
                <div className="ihv-portal-form__field">
                  <label className="ihv-portal-form__label" htmlFor="nc-budget">Rango de presupuesto</label>
                  <input id="nc-budget" className="ihv-portal-form__input" type="text" value={budgetRange}
                    onChange={(e) => setBudgetRange(e.target.value)} placeholder="USD" />
                </div>
              </div>
            </>
          )}

          <div className="ihv-portal-form__row">
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="nc-urg">Urgencia</label>
              <select id="nc-urg" className="ihv-portal-form__select" value={urgency}
                onChange={(e) => setUrgency(e.target.value)}>
                <option value="normal">Normal</option>
                <option value="prioritaria">Prioritaria</option>
                <option value="urgente">Urgente</option>
              </select>
            </div>
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="nc-dd">Fecha de entrega deseada</label>
              <input id="nc-dd" className="ihv-portal-form__input" type="date" value={desiredDelivery}
                min={todayLocalISO()} onChange={(e) => setDesiredDelivery(e.target.value)} />
            </div>
          </div>

          <div className="ihv-portal-form__row">
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="nc-cur">Moneda</label>
              <select id="nc-cur" className="ihv-portal-form__select" value={currency}
                onChange={(e) => setCurrency(e.target.value)}>
                <option value="USD">USD</option>
                <option value="VES">VES</option>
              </select>
            </div>
            {currency === 'VES' && (
              <div className="ihv-portal-form__field">
                <label className="ihv-portal-form__label" htmlFor="nc-rate">Tasa BCV (Bs/USD)</label>
                {rateLoading ? (
                  <p className="ihv-portal-form__hint">Cargando tasa del día.</p>
                ) : rateInfo ? (
                  <>
                    <input id="nc-rate" className="ihv-portal-form__input" type="text"
                      value={Number(rateInfo.rate).toLocaleString('es-VE', { minimumFractionDigits: 2, maximumFractionDigits: 4 })}
                      readOnly />
                    <p className="ihv-portal-form__hint">
                      Tasa oficial del {formatDate(rateInfo.date)} ({rateInfo.source}).
                    </p>
                  </>
                ) : (
                  <>
                    <input id="nc-rate" className="ihv-portal-form__input" type="number" value={exchangeRate}
                      min="0" step="any" onChange={(e) => setExchangeRate(e.target.value)} placeholder="Ingresa la tasa manualmente" />
                    <p className="ihv-portal-form__hint">No pudimos cargar la tasa BCV. Ingresa una tasa manual.</p>
                  </>
                )}
              </div>
            )}
          </div>

          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="nc-notes">Notas adicionales</label>
            <textarea id="nc-notes" className="ihv-portal-form__textarea" rows={4} maxLength={2000}
              value={notes} onChange={(e) => setNotes(e.target.value)} />
            <p className="ihv-portal-form__hint">{notes.length}/2000</p>
          </div>

          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="nc-files">Adjuntos (opcional)</label>
            <input id="nc-files" className="ihv-portal-form__input" type="file" multiple onChange={onFiles} />
            {attachments.length > 0 && (
              <ul className="ihv-portal-form__attachments">
                {attachments.map((a) => (
                  <li key={a.key}>
                    <span>{a.original_filename}</span>
                    <span className="ihv-portal-form__hint">{formatBytes(a.size_bytes)}</span>
                    <span className={'ihv-portal-status ihv-portal-status--' + a.state}>{
                      a.state === 'subiendo' ? 'Subiendo' : a.state === 'subido' ? 'Subido' : 'Error'
                    }</span>
                    <button type="button" className="ihv-portal-btn ihv-portal-btn--ghost"
                      onClick={() => removeAttachment(a.key)}>Quitar</button>
                  </li>
                ))}
              </ul>
            )}
          </div>

          <FieldError>{error}</FieldError>
          <div className="ihv-portal-card__footer">
            <button className="ihv-portal-btn ihv-portal-btn--primary" type="submit" disabled={submitting}>
              {submitting ? 'Enviando' : 'Enviar solicitud'}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

function MisCotizacionesView() {
  const [rows, setRows] = useState(null);
  const [error, setError] = useState(null);
  const [busyId, setBusyId] = useState(null);

  const loadRows = useCallback(async () => {
    const { data, error: err } = await window.ihvSupabase
      .from('quote_requests')
      .select('id, reference_code, project_name, request_type, status, created_at, estimated_amount_usd, currency')
      .order('created_at', { ascending: false });
    if (err) { setError('No pudimos cargar tus cotizaciones.'); return; }
    setRows(data || []);
  }, []);

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

  const onDelete = async (e, r) => {
    e.stopPropagation();
    const ok = window.confirm('Vas a eliminar la solicitud ' + r.reference_code + ' de forma definitiva. ¿Deseas continuar?');
    if (!ok) return;
    setError(null);
    setBusyId(r.id);
    const { data, error: err } = await window.ihvSupabase.rpc('delete_my_quote', { p_quote_id: r.id });
    if (err) { setBusyId(null); setError(translateRpcError(err)); return; }
    const res = Array.isArray(data) ? data[0] : data;
    const paths = res && res.storage_paths ? res.storage_paths : [];
    if (paths && paths.length) {
      try { await window.ihvSupabase.storage.from('quote-attachments').remove(paths); } catch (_) {}
    }
    setBusyId(null);
    loadRows();
  };

  const header = (
    <div className="ihv-portal-card__header">
      <h1 className="ihv-portal-card__title">Mis cotizaciones</h1>
      <a className="ihv-portal-btn ihv-portal-btn--primary" href="#/nueva">Nueva cotización</a>
    </div>
  );

  if (rows === null) {
    return (
      <div className="ihv-portal-card">
        {header}
        <div className="ihv-portal-card__body">
          {error ? <FieldError>{error}</FieldError> : <p>Cargando.</p>}
        </div>
      </div>
    );
  }

  if (!rows.length) {
    return (
      <div className="ihv-portal-card">
        {header}
        <div className="ihv-portal-card__body">
          <FieldError>{error}</FieldError>
          <div className="ihv-portal-empty">
            <p>Aún no tienes cotizaciones. Crea la primera desde Nueva cotización.</p>
            <a className="ihv-portal-btn ihv-portal-btn--primary" href="#/nueva">Nueva cotización</a>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="ihv-portal-card">
      {header}
      <div className="ihv-portal-card__body">
        <FieldError>{error}</FieldError>
        <table className="ihv-portal-table">
          <thead>
            <tr className="ihv-portal-table__row">
              <th className="ihv-portal-table__cell">Nombre</th>
              <th className="ihv-portal-table__cell">Tipo</th>
              <th className="ihv-portal-table__cell">Estado</th>
              <th className="ihv-portal-table__cell">Creada</th>
              <th className="ihv-portal-table__cell">Monto estimado</th>
              <th className="ihv-portal-table__cell">Referencia</th>
              <th className="ihv-portal-table__cell">Acciones</th>
            </tr>
          </thead>
          <tbody>
            {rows.map((r) => (
              <tr key={r.id} className="ihv-portal-table__row" onClick={() => navigate('/cotizaciones/' + r.id)}>
                <td className="ihv-portal-table__cell">{r.project_name || '—'}</td>
                <td className="ihv-portal-table__cell">{r.request_type === 'obras' ? 'Obras' : 'Suministros'}</td>
                <td className="ihv-portal-table__cell"><StatusBadge status={r.status} /></td>
                <td className="ihv-portal-table__cell">{formatDate(r.created_at)}</td>
                <td className="ihv-portal-table__cell">
                  {r.estimated_amount_usd != null ? (r.estimated_amount_usd + ' ' + r.currency) : '—'}
                </td>
                <td className="ihv-portal-table__cell" style={{ fontFamily: 'var(--font-mono)' }}>{r.reference_code}</td>
                <td className="ihv-portal-table__cell ihv-portal-table__cell--actions">
                  {(r.status === 'recibida' || r.status === 'en_revision') ? (
                    <button type="button" className="ihv-portal-btn ihv-portal-btn--danger-ghost"
                      disabled={busyId === r.id} onClick={(e) => onDelete(e, r)}>
                      {busyId === r.id ? 'Eliminando' : 'Eliminar'}
                    </button>
                  ) : (
                    <span className="ihv-portal-form__hint">—</span>
                  )}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function DetalleView({ id }) {
  const [quote, setQuote] = useState(null);
  const [items, setItems] = useState([]);
  const [works, setWorks] = useState(null);
  const [history, setHistory] = useState([]);
  const [attachments, setAttachments] = useState([]);
  const [error, setError] = useState(null);
  const [actionError, setActionError] = useState(null);
  const [deleting, setDeleting] = useState(false);

  useEffect(() => {
    let cancelled = false;
    const sb = window.ihvSupabase;
    (async () => {
      const { data: q, error: qErr } = await sb
        .from('quote_requests')
        .select('*')
        .eq('id', id)
        .maybeSingle();
      if (cancelled) return;
      if (qErr || !q) { setError('No pudimos cargar la cotización.'); return; }
      setQuote(q);

      const [it, wd, hi, at] = await Promise.all([
        q.request_type === 'suministros'
          ? sb.from('quote_supply_items').select('*').eq('quote_id', id)
          : Promise.resolve({ data: [] }),
        q.request_type === 'obras'
          ? sb.from('quote_works_details').select('*').eq('quote_id', id).maybeSingle()
          : Promise.resolve({ data: null }),
        sb.from('quote_status_history').select('*').eq('quote_id', id).order('changed_at', { ascending: true }),
        sb.from('quote_attachments').select('*').eq('quote_id', id)
      ]);
      if (cancelled) return;
      setItems(it.data || []);
      setWorks(wd.data || null);
      setHistory(hi.data || []);
      setAttachments(at.data || []);
    })();
    return () => { cancelled = true; };
  }, [id]);

  const onDownload = async (path) => {
    const { data, error: err } = await window.ihvSupabase.storage
      .from('quote-attachments').createSignedUrl(path, 60);
    if (err || !data) return;
    window.open(data.signedUrl, '_blank', 'noopener,noreferrer');
  };

  const onDelete = async () => {
    if (!quote) return;
    const ok = window.confirm('Vas a eliminar la solicitud ' + quote.reference_code + ' de forma definitiva. ¿Deseas continuar?');
    if (!ok) return;
    setActionError(null);
    setDeleting(true);
    const { data, error: err } = await window.ihvSupabase.rpc('delete_my_quote', { p_quote_id: quote.id });
    if (err) { setDeleting(false); setActionError(translateRpcError(err)); return; }
    const res = Array.isArray(data) ? data[0] : data;
    const paths = res && res.storage_paths ? res.storage_paths : [];
    if (paths && paths.length) {
      try { await window.ihvSupabase.storage.from('quote-attachments').remove(paths); } catch (_) {}
    }
    navigate('/cotizaciones');
  };

  if (error) return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><FieldError>{error}</FieldError><a href="#/cotizaciones">Volver</a></div></div>;
  if (!quote) return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>Cargando.</p></div></div>;

  return (
    <div className="ihv-portal-card">
      <h1 className="ihv-portal-card__title">
        <span style={{ fontFamily: 'var(--font-mono)' }}>{quote.reference_code}</span>
      </h1>
      <div className="ihv-portal-card__body">
        <div className="ihv-portal-form__row">
          <div><strong>Estado:</strong> <StatusBadge status={quote.status} /></div>
          <div><strong>Tipo:</strong> {quote.request_type === 'obras' ? 'Obras' : 'Suministros'}</div>
          <div><strong>Creada:</strong> {formatDate(quote.created_at)}</div>
          <div><strong>Moneda:</strong> {quote.currency}</div>
          {quote.estimated_amount_usd != null && (
            <div><strong>Monto:</strong> {quote.estimated_amount_usd} {quote.currency}</div>
          )}
        </div>

        {quote.notes && (
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label">Notas</label>
            <p>{quote.notes}</p>
          </div>
        )}

        {quote.request_type === 'suministros' && (
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label">Artículos</label>
            <table className="ihv-portal-table">
              <thead><tr className="ihv-portal-table__row">
                <th className="ihv-portal-table__cell">Descripción</th>
                <th className="ihv-portal-table__cell">Cantidad</th>
                <th className="ihv-portal-table__cell">Unidad</th>
              </tr></thead>
              <tbody>
                {items.map((it) => (
                  <tr key={it.id} className="ihv-portal-table__row">
                    <td className="ihv-portal-table__cell">{it.description}</td>
                    <td className="ihv-portal-table__cell">{it.quantity}</td>
                    <td className="ihv-portal-table__cell">{it.unit || '—'}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}

        {quote.request_type === 'obras' && works && (
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label">Detalles de la obra</label>
            <p><strong>Alcance:</strong> {works.scope_description}</p>
            {works.work_type && <p><strong>Tipo:</strong> {works.work_type}</p>}
            {works.site_address && <p><strong>Dirección:</strong> {works.site_address}</p>}
            {works.estimated_area_m2 && <p><strong>Área:</strong> {works.estimated_area_m2} m²</p>}
            {works.budget_range && <p><strong>Presupuesto:</strong> {works.budget_range}</p>}
          </div>
        )}

        {attachments.length > 0 && (
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label">Adjuntos</label>
            <ul className="ihv-portal-form__attachments">
              {attachments.map((a) => {
                const scan = a.scan_status || 'pending';
                return (
                  <li key={a.id}>
                    <span>{a.filename}</span>
                    <span className="ihv-portal-form__hint">{formatBytes(a.size_bytes)}</span>
                    {scan === 'clean' && (
                      <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary"
                        onClick={() => onDownload(a.storage_path)}>Descargar</button>
                    )}
                    {scan === 'pending' && <span className="ihv-portal-status ihv-portal-status--pending">En escaneo</span>}
                    {scan === 'infected' && <span className="ihv-portal-status ihv-portal-status--infected">Bloqueado</span>}
                  </li>
                );
              })}
            </ul>
          </div>
        )}

        <div className="ihv-portal-form__field">
          <label className="ihv-portal-form__label">Historial</label>
          <ol className="ihv-portal-timeline">
            {history.map((h) => (
              <li key={h.id} className="ihv-portal-timeline__item">
                <div className="ihv-portal-form__hint">{formatDateTime(h.changed_at)}</div>
                <div>
                  {(h.from_status ? (STATUS_LABELS[h.from_status] || h.from_status) + ' → ' : '')}
                  <strong>{STATUS_LABELS[h.to_status] || h.to_status}</strong>
                </div>
                {h.note && <div className="ihv-portal-form__hint">{h.note}</div>}
              </li>
            ))}
          </ol>
        </div>

        <FieldError>{actionError}</FieldError>
        <div className="ihv-portal-card__footer ihv-portal-card__footer--split">
          <a href="#/cotizaciones" className="ihv-portal-btn ihv-portal-btn--ghost">Volver al listado</a>
          <div className="ihv-portal-card__footer-actions">
            {(() => {
              const waHref = whatsappLink('Hola, consulto por mi cotización ' + quote.reference_code);
              return waHref ? (
                <a className="ihv-portal-btn ihv-portal-btn--whatsapp" href={waHref} target="_blank" rel="noopener">
                  Consultar por WhatsApp
                </a>
              ) : null;
            })()}
            {(quote.status === 'recibida' || quote.status === 'en_revision') && (
              <button type="button" className="ihv-portal-btn ihv-portal-btn--danger-outline"
                disabled={deleting} onClick={onDelete}>
                {deleting ? 'Eliminando' : 'Eliminar solicitud'}
              </button>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

function PerfilView({ session }) {
  const [profile, setProfile] = useState(null);
  const [loaded, setLoaded] = useState(false);

  const [fullName, setFullName] = useState('');
  const [companyName, setCompanyName] = useState('');
  const [rif, setRif] = useState('');
  const [phone, setPhone] = useState('');
  const [waCc, setWaCc] = useState('+58');
  const [waLocal, setWaLocal] = useState('');
  const [saveError, setSaveError] = useState(null);
  const [saveOk, setSaveOk] = useState(false);
  const [saving, setSaving] = useState(false);

  const [pass1, setPass1] = useState('');
  const [pass2, setPass2] = useState('');
  const [passError, setPassError] = useState(null);
  const [passOk, setPassOk] = useState(false);
  const [passBusy, setPassBusy] = useState(false);

  const [quoteCount, setQuoteCount] = useState(null);

  const [confirmText, setConfirmText] = useState('');
  const [delError, setDelError] = useState(null);
  const [delBusy, setDelBusy] = useState(false);

  useEffect(() => {
    let cancelled = false;
    const sb = window.ihvSupabase;
    sb.from('client_profiles')
      .select('full_name, company_name, company_rif, phone, whatsapp, created_at')
      .eq('user_id', session.user.id)
      .maybeSingle()
      .then(({ data }) => {
        if (cancelled) return;
        setProfile(data || {});
        setFullName(data && data.full_name ? data.full_name : '');
        setCompanyName(data && data.company_name ? data.company_name : '');
        setRif(data && data.company_rif ? data.company_rif : '');
        setPhone(data && data.phone ? data.phone : '');
        const wa = splitE164(data && data.whatsapp ? data.whatsapp : '');
        setWaCc(wa.cc);
        setWaLocal(wa.local);
        setLoaded(true);
      });
    sb.from('quote_requests')
      .select('id', { count: 'exact', head: true })
      .then(({ count }) => { if (!cancelled) setQuoteCount(count == null ? 0 : count); });
    return () => { cancelled = true; };
  }, [session.user.id]);

  const meta = (session.user && session.user.app_metadata) || {};
  const providers = Array.isArray(meta.providers) ? meta.providers : (meta.provider ? [meta.provider] : []);
  const hasEmailProvider = providers.indexOf('email') !== -1;

  const onSaveDatos = async (e) => {
    e.preventDefault();
    setSaveError(null);
    setSaveOk(false);
    const name = stripCtrl(fullName).trim();
    const empresa = stripCtrl(companyName).trim();
    const tel = stripCtrl(phone).trim();
    if (name.length < 2) { setSaveError('Ingresa tu nombre completo.'); return; }
    if (empresa.length < 2) { setSaveError('Ingresa el nombre de la empresa.'); return; }
    const normalRif = normalizeRif(rif);
    if (!normalRif) { setSaveError('RIF no válido. Formato: J-12345678-9.'); return; }
    if (tel.length < 7) { setSaveError('Ingresa un teléfono válido.'); return; }
    const wa = composeE164(waCc, waLocal);
    if (wa === false) { setSaveError('Número de WhatsApp no válido. Revisa el código de país y los dígitos.'); return; }

    setSaving(true);
    const payload = {
      user_id: session.user.id,
      email: session.user.email,
      full_name: name,
      company_name: empresa,
      company_rif: normalRif,
      phone: tel,
      whatsapp: wa
    };
    const { error: err } = await window.ihvSupabase
      .from('client_profiles')
      .upsert(payload, { onConflict: 'user_id' });
    setSaving(false);
    if (err) { setSaveError('No pudimos guardar los cambios. Inténtalo de nuevo.'); return; }
    setSaveOk(true);
  };

  const onChangePassword = async (e) => {
    e.preventDefault();
    setPassError(null);
    setPassOk(false);
    if (pass1.length < 8) { setPassError('La contraseña debe tener al menos 8 caracteres.'); return; }
    if (pass1 !== pass2) { setPassError('Las contraseñas no coinciden.'); return; }
    setPassBusy(true);
    const { error: err } = await window.ihvSupabase.auth.updateUser({ password: pass1 });
    setPassBusy(false);
    if (err) { setPassError(translateAuthError(err)); return; }
    setPass1('');
    setPass2('');
    setPassOk(true);
  };

  const onDeleteAccount = async () => {
    if (confirmText !== 'ELIMINAR') return;
    setDelError(null);
    setDelBusy(true);
    try {
      const { error: err } = await window.ihvSupabase.functions.invoke('delete-my-account');
      if (err) {
        setDelBusy(false);
        setDelError('No pudimos eliminar la cuenta. Inténtalo de nuevo en unos minutos.');
        return;
      }
      await window.ihvSupabase.auth.signOut();
      navigate('/login');
    } catch (_) {
      setDelBusy(false);
      setDelError('No pudimos eliminar la cuenta. Inténtalo de nuevo en unos minutos.');
    }
  };

  if (!loaded) {
    return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>Cargando.</p></div></div>;
  }

  return (
    <div className="ihv-portal-profile">
      <div className="ihv-portal-card">
        <h1 className="ihv-portal-card__title">Datos de la cuenta</h1>
        <div className="ihv-portal-card__body">
          <form className="ihv-portal-form" onSubmit={onSaveDatos} noValidate>
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="pf-email">Correo</label>
              <input id="pf-email" className="ihv-portal-form__input" type="email"
                value={session.user.email || ''} readOnly disabled />
              <p className="ihv-portal-form__hint">El correo de acceso no se puede cambiar desde el portal.</p>
            </div>
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="pf-name">Nombre completo</label>
              <input id="pf-name" className="ihv-portal-form__input" type="text" value={fullName}
                onChange={(e) => setFullName(e.target.value)} required />
            </div>
            <div className="ihv-portal-form__field">
              <label className="ihv-portal-form__label" htmlFor="pf-empresa">Empresa</label>
              <input id="pf-empresa" className="ihv-portal-form__input" type="text" value={companyName}
                onChange={(e) => setCompanyName(e.target.value)} required />
            </div>
            <div className="ihv-portal-form__row">
              <div className="ihv-portal-form__field">
                <label className="ihv-portal-form__label" htmlFor="pf-rif">RIF</label>
                <input id="pf-rif" className="ihv-portal-form__input" type="text" value={rif}
                  onChange={(e) => setRif(e.target.value)} placeholder="J-12345678-9" required />
              </div>
              <div className="ihv-portal-form__field">
                <label className="ihv-portal-form__label" htmlFor="pf-tel">Teléfono</label>
                <input id="pf-tel" className="ihv-portal-form__input" type="tel" value={phone}
                  onChange={(e) => setPhone(e.target.value)} required />
              </div>
            </div>
            <WhatsappField idPrefix="pf" cc={waCc} local={waLocal}
              onCcChange={setWaCc} onLocalChange={setWaLocal} optional />
            <FieldError>{saveError}</FieldError>
            {saveOk && <p className="ihv-portal-form__success">Cambios guardados.</p>}
            <div className="ihv-portal-card__footer">
              <button className="ihv-portal-btn ihv-portal-btn--primary" type="submit" disabled={saving}>
                {saving ? 'Guardando' : 'Guardar cambios'}
              </button>
            </div>
          </form>
        </div>
      </div>

      <div className="ihv-portal-card">
        <h2 className="ihv-portal-card__title">Seguridad</h2>
        <div className="ihv-portal-card__body">
          {hasEmailProvider ? (
            <form className="ihv-portal-form" onSubmit={onChangePassword} noValidate>
              <div className="ihv-portal-form__row">
                <div className="ihv-portal-form__field">
                  <label className="ihv-portal-form__label" htmlFor="pf-pass1">Contraseña nueva</label>
                  <input id="pf-pass1" className="ihv-portal-form__input" type="password"
                    autoComplete="new-password" value={pass1} minLength={8}
                    onChange={(e) => setPass1(e.target.value)} required />
                </div>
                <div className="ihv-portal-form__field">
                  <label className="ihv-portal-form__label" htmlFor="pf-pass2">Confirma la contraseña</label>
                  <input id="pf-pass2" className="ihv-portal-form__input" type="password"
                    autoComplete="new-password" value={pass2} minLength={8}
                    onChange={(e) => setPass2(e.target.value)} required />
                </div>
              </div>
              <p className="ihv-portal-form__hint">Mínimo 8 caracteres.</p>
              <FieldError>{passError}</FieldError>
              {passOk && <p className="ihv-portal-form__success">Contraseña actualizada.</p>}
              <div className="ihv-portal-card__footer">
                <button className="ihv-portal-btn ihv-portal-btn--secondary" type="submit" disabled={passBusy}>
                  {passBusy ? 'Actualizando' : 'Actualizar contraseña'}
                </button>
              </div>
            </form>
          ) : (
            <p>El acceso a tu cuenta lo gestiona tu cuenta de Google. No hay contraseña que administrar desde el portal.</p>
          )}
        </div>
      </div>

      <div className="ihv-portal-card">
        <h2 className="ihv-portal-card__title">Actividad</h2>
        <div className="ihv-portal-card__body">
          <dl className="ihv-portal-meta">
            <div className="ihv-portal-meta__item">
              <dt className="ihv-portal-meta__term">Cotizaciones enviadas</dt>
              <dd className="ihv-portal-meta__value">{quoteCount == null ? '—' : quoteCount}</dd>
            </div>
            <div className="ihv-portal-meta__item">
              <dt className="ihv-portal-meta__term">Miembro desde</dt>
              <dd className="ihv-portal-meta__value">{profile && profile.created_at ? formatDate(profile.created_at) : '—'}</dd>
            </div>
            <div className="ihv-portal-meta__item">
              <dt className="ihv-portal-meta__term">Último acceso</dt>
              <dd className="ihv-portal-meta__value">{session.user.last_sign_in_at ? formatDateTime(session.user.last_sign_in_at) : '—'}</dd>
            </div>
          </dl>
        </div>
      </div>

      <div className="ihv-portal-card ihv-portal-danger">
        <h2 className="ihv-portal-card__title ihv-portal-danger__title">Zona de peligro</h2>
        <div className="ihv-portal-card__body">
          <p>
            Al eliminar tu cuenta, tus datos personales se anonimizan de forma permanente y las solicitudes
            históricas dejan de estar asociadas a ti. Esta acción no se puede deshacer.
          </p>
          <div className="ihv-portal-form__field">
            <label className="ihv-portal-form__label" htmlFor="pf-del-confirm">
              Escribe ELIMINAR para confirmar
            </label>
            <input id="pf-del-confirm" className="ihv-portal-form__input" type="text" value={confirmText}
              onChange={(e) => setConfirmText(e.target.value)} autoComplete="off" />
          </div>
          <FieldError>{delError}</FieldError>
          <div className="ihv-portal-card__footer">
            <button type="button" className="ihv-portal-btn ihv-portal-btn--danger"
              disabled={confirmText !== 'ELIMINAR' || delBusy} onClick={onDeleteAccount}>
              {delBusy ? 'Eliminando cuenta' : 'Eliminar mi cuenta'}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

function StaffManagementView({ session, caps }) {
  const supabase = window.__supabaseClient;
  const [staff, setStaff]       = React.useState([]);
  const [roles, setRoles]       = React.useState([]);
  const [allCaps, setAllCaps]   = React.useState([]);
  const [segments, setSegments] = React.useState([]);
  const [loading, setLoading]   = React.useState(true);
  const [creating, setCreating] = React.useState(false);
  const [newEmail, setNewEmail]     = React.useState('');
  const [newName, setNewName]       = React.useState('');
  const [newRole, setNewRole]       = React.useState('junior');
  const [createError, setCreateError] = React.useState('');

  React.useEffect(() => {
    if (!hasCap(caps, 'staff.manage')) return;
    async function load() {
      const [{ data: s }, { data: r }, { data: c }, { data: sg }] = await Promise.all([
        supabase.from('staff_profiles').select('user_id, full_name, email, role, is_active'),
        supabase.from('staff_roles').select('role_key, label').order('rank'),
        supabase.from('capabilities').select('capability_key, label').order('sort'),
        supabase.from('segments').select('segment_key, label').eq('is_active', true).order('sort'),
      ]);
      setStaff(s || []);
      setRoles(r || []);
      setAllCaps(c || []);
      setSegments(sg || []);
      setLoading(false);
    }
    load();
  }, [caps]);

  async function createStaff(e) {
    e.preventDefault();
    setCreateError(''); setCreating(true);
    const tempPin = Math.floor(10000000 + Math.random() * 90000000).toString();
    const { error } = await supabase.rpc('create_staff_member', {
      p_email: newEmail, p_full_name: newName,
      p_role: newRole,  p_pin: tempPin,
    });
    setCreating(false);
    if (error) { setCreateError(error.message); return; }
    setNewEmail(''); setNewName(''); setNewRole('junior');
    const { data: s } = await supabase.from('staff_profiles')
      .select('user_id, full_name, email, role, is_active');
    setStaff(s || []);
  }

  async function toggleCapOverride(userId, capKey, currentCaps, roleCaps) {
    const hasViaRole = !!(roleCaps && roleCaps[capKey]);
    const override = currentCaps[capKey];
    if (override !== undefined) {
      await supabase.from('staff_capability_overrides')
        .delete().eq('user_id', userId).eq('capability_key', capKey);
    } else {
      await supabase.from('staff_capability_overrides')
        .upsert({ user_id: userId, capability_key: capKey, granted: !hasViaRole });
    }
  }

  async function toggleSegment(userId, segKey, currentSegs) {
    const has = currentSegs.indexOf(segKey) !== -1;
    if (has) {
      await supabase.from('segment_staff').delete().eq('user_id', userId).eq('segment_key', segKey);
    } else {
      await supabase.from('segment_staff').upsert({ user_id: userId, segment_key: segKey });
    }
  }

  if (!hasCap(caps, 'staff.manage')) {
    return (
      <div className="ihv-portal-card">
        <div className="ihv-portal-card__body">
          <p>No tienes permiso para gestionar el equipo.</p>
        </div>
      </div>
    );
  }

  if (loading) return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>Cargando...</p></div></div>;

  return (
    <div className="ihv-portal-card">
      <h1 className="ihv-portal-card__title">Gestión de equipo</h1>
      <div className="ihv-portal-card__body">

        <section className="ihv-staff-mgmt__section">
          <h2 className="ihv-staff-mgmt__subtitle">Agregar miembro</h2>
          <form onSubmit={createStaff} className="ihv-portal-form ihv-staff-mgmt__create-form">
            <div className="ihv-portal-form__group">
              <label className="ihv-portal-form__label">Nombre completo</label>
              <input type="text" required className="ihv-portal-form__input"
                value={newName} onChange={e => setNewName(e.target.value)} />
            </div>
            <div className="ihv-portal-form__group">
              <label className="ihv-portal-form__label">Correo</label>
              <input type="email" required className="ihv-portal-form__input"
                value={newEmail} onChange={e => setNewEmail(e.target.value)} />
            </div>
            <div className="ihv-portal-form__group">
              <label className="ihv-portal-form__label">Rol</label>
              <select className="ihv-portal-form__input" value={newRole} onChange={e => setNewRole(e.target.value)}>
                {roles.filter(r => r.role_key !== 'master').map(r =>
                  <option key={r.role_key} value={r.role_key}>{r.label}</option>
                )}
              </select>
            </div>
            {createError && <p className="ihv-portal-form__error">{createError}</p>}
            <button type="submit" disabled={creating} className="ihv-portal-btn ihv-portal-btn--primary">
              {creating ? 'Creando…' : 'Crear miembro'}
            </button>
          </form>
        </section>

        <section className="ihv-staff-mgmt__section">
          <h2 className="ihv-staff-mgmt__subtitle">Equipo actual</h2>
          <StaffMemberList
            staff={staff} roles={roles} allCaps={allCaps} segments={segments}
            supabase={supabase}
            onToggleCap={toggleCapOverride}
            onToggleSeg={toggleSegment}
          />
        </section>
      </div>
    </div>
  );
}

function StaffMemberList({ staff, roles, allCaps, segments, supabase, onToggleCap, onToggleSeg }) {
  const [expanded, setExpanded] = React.useState(null);
  const [memberData, setMemberData] = React.useState({});

  async function expandMember(uid, role) {
    if (expanded === uid) { setExpanded(null); return; }
    setExpanded(uid);
    if (memberData[uid]) return;
    const [{ data: ov }, { data: rc }, { data: ss }] = await Promise.all([
      supabase.from('staff_capability_overrides').select('capability_key, granted').eq('user_id', uid),
      supabase.from('role_capabilities').select('capability_key').eq('role_key', role),
      supabase.from('segment_staff').select('segment_key').eq('user_id', uid),
    ]);
    const roleCapsMap = {};
    for (const r of rc || []) roleCapsMap[r.capability_key] = true;
    const overrideMap = {};
    for (const o of ov || []) overrideMap[o.capability_key] = o.granted;
    const segList = (ss || []).map(s => s.segment_key);
    setMemberData(prev => ({ ...prev, [uid]: { roleCapsMap, overrideMap, segList } }));
  }

  async function handleToggleCap(uid, capKey, role) {
    const d = memberData[uid];
    if (!d) return;
    await onToggleCap(uid, capKey, d.overrideMap, d.roleCapsMap);
    const { data: ov } = await supabase
      .from('staff_capability_overrides').select('capability_key, granted').eq('user_id', uid);
    const overrideMap = {};
    for (const o of ov || []) overrideMap[o.capability_key] = o.granted;
    setMemberData(prev => ({ ...prev, [uid]: { ...prev[uid], overrideMap } }));
  }

  async function handleToggleSeg(uid, segKey) {
    const d = memberData[uid];
    if (!d) return;
    await onToggleSeg(uid, segKey, d.segList);
    const { data: ss } = await supabase
      .from('segment_staff').select('segment_key').eq('user_id', uid);
    setMemberData(prev => ({ ...prev, [uid]: { ...prev[uid], segList: (ss||[]).map(s=>s.segment_key) } }));
  }

  function effectiveCap(d, capKey) {
    if (!d) return false;
    if (d.overrideMap[capKey] !== undefined) return d.overrideMap[capKey];
    return !!d.roleCapsMap[capKey];
  }

  return (
    <div className="ihv-staff-list">
      {staff.map(m => {
        const d = memberData[m.user_id];
        const isExpanded = expanded === m.user_id;
        return (
          <div key={m.user_id} className={`ihv-staff-list__item${isExpanded ? ' ihv-staff-list__item--open' : ''}`}>
            <button className="ihv-staff-list__header" onClick={() => expandMember(m.user_id, m.role)}>
              <span className="ihv-staff-list__name">{m.full_name}</span>
              <span className="ihv-staff-list__email">{m.email}</span>
              <span className="ihv-portal-badge">{m.role}</span>
              <span className={`ihv-portal-badge ${m.is_active ? 'ihv-portal-badge--success' : 'ihv-portal-badge--neutral'}`}>
                {m.is_active ? 'activo' : 'inactivo'}
              </span>
              <span className="ihv-staff-list__chevron">{isExpanded ? '▲' : '▼'}</span>
            </button>

            {isExpanded && d && (
              <div className="ihv-staff-list__detail">
                <div className="ihv-staff-list__section">
                  <p className="ihv-staff-list__section-title">Capacidades</p>
                  <div className="ihv-staff-caps-grid">
                    {allCaps.map(cap => {
                      const active = effectiveCap(d, cap.capability_key);
                      const hasOverride = d.overrideMap[cap.capability_key] !== undefined;
                      return (
                        <label key={cap.capability_key} className={`ihv-staff-caps-grid__item${hasOverride ? ' ihv-staff-caps-grid__item--override' : ''}`}>
                          <input type="checkbox" checked={active}
                            onChange={() => handleToggleCap(m.user_id, cap.capability_key, m.role)} />
                          <span>{cap.label}</span>
                          {hasOverride && <span className="ihv-staff-caps-grid__override-mark">override</span>}
                        </label>
                      );
                    })}
                  </div>
                </div>

                <div className="ihv-staff-list__section">
                  <p className="ihv-staff-list__section-title">Segmentos</p>
                  <div className="ihv-staff-segs-row">
                    {segments.map(seg => (
                      <label key={seg.segment_key} className="ihv-staff-segs-row__item">
                        <input type="checkbox"
                          checked={d.segList.indexOf(seg.segment_key) !== -1}
                          onChange={() => handleToggleSeg(m.user_id, seg.segment_key)} />
                        <span>{seg.label}</span>
                      </label>
                    ))}
                  </div>
                </div>
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

function SegmentsAdminView({ caps }) {
  const supabase = window.__supabaseClient;
  const [segments, setSegments]   = React.useState([]);
  const [tiers, setTiers]         = React.useState([]);
  const [loading, setLoading]     = React.useState(true);
  const [editSeg, setEditSeg]     = React.useState(null);
  const [saving, setSaving]       = React.useState(false);

  React.useEffect(() => {
    if (!hasCap(caps, 'segments.manage')) return;
    async function load() {
      const [{ data: s }, { data: t }] = await Promise.all([
        supabase.from('segments').select('*').order('sort'),
        supabase.from('amount_tiers').select('*').order('sort'),
      ]);
      setSegments(s || []);
      setTiers(t || []);
      setLoading(false);
    }
    load();
  }, [caps]);

  async function saveSeg(e) {
    e.preventDefault();
    setSaving(true);
    const { error } = await supabase.from('segments').upsert(editSeg, { onConflict: 'segment_key' });
    setSaving(false);
    if (error) { alert(error.message); return; }
    const { data: s } = await supabase.from('segments').select('*').order('sort');
    setSegments(s || []);
    setEditSeg(null);
  }

  if (!hasCap(caps, 'segments.manage')) {
    return (
      <div className="ihv-portal-card">
        <div className="ihv-portal-card__body"><p>Sin acceso.</p></div>
      </div>
    );
  }

  if (loading) return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>Cargando...</p></div></div>;

  return (
    <div className="ihv-portal-card">
      <h1 className="ihv-portal-card__title">Segmentos y umbrales</h1>
      <div className="ihv-portal-card__body">

        <section className="ihv-staff-mgmt__section">
          <h2 className="ihv-staff-mgmt__subtitle">Segmentos</h2>
          <table className="ihv-portal-table">
            <thead>
              <tr>
                <th>Clave</th><th>Etiqueta</th><th>Tipo</th>
                <th>Urgencia</th><th>Tier</th><th>Tag manual</th><th>Activo</th><th></th>
              </tr>
            </thead>
            <tbody>
              {segments.map(s => (
                <tr key={s.segment_key}>
                  <td><code>{s.segment_key}</code></td>
                  <td>{s.label}</td>
                  <td>{s.match_request_type || '—'}</td>
                  <td>{s.match_urgency || '—'}</td>
                  <td>{s.match_amount_tier || '—'}</td>
                  <td>{s.match_manual_tag || '—'}</td>
                  <td>{s.is_active ? 'Sí' : 'No'}</td>
                  <td>
                    <button className="ihv-portal-btn ihv-portal-btn--ghost ihv-portal-btn--sm"
                      onClick={() => setEditSeg({...s})}>Editar</button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>

          <button className="ihv-portal-btn ihv-portal-btn--secondary" style={{marginTop:'1rem'}}
            onClick={() => setEditSeg({ segment_key:'', label:'', match_request_type:null,
              match_urgency:null, match_amount_tier:null, match_manual_tag:null, sort:0, is_active:true })}>
            Nuevo segmento
          </button>

          {editSeg && (
            <form onSubmit={saveSeg} className="ihv-portal-form ihv-staff-seg-form">
              <div className="ihv-portal-form__group">
                <label className="ihv-portal-form__label">Clave</label>
                <input className="ihv-portal-form__input" required
                  value={editSeg.segment_key} onChange={e => setEditSeg(p => ({...p, segment_key: e.target.value}))} />
              </div>
              <div className="ihv-portal-form__group">
                <label className="ihv-portal-form__label">Etiqueta</label>
                <input className="ihv-portal-form__input" required
                  value={editSeg.label} onChange={e => setEditSeg(p => ({...p, label: e.target.value}))} />
              </div>
              <div className="ihv-portal-form__group">
                <label className="ihv-portal-form__label">Tipo de solicitud (vacío = cualquiera)</label>
                <input className="ihv-portal-form__input" placeholder="suministros u obras"
                  value={editSeg.match_request_type || ''} onChange={e => setEditSeg(p => ({...p, match_request_type: e.target.value || null}))} />
              </div>
              <div className="ihv-portal-form__group">
                <label className="ihv-portal-form__label">Urgencia (vacío = cualquiera)</label>
                <input className="ihv-portal-form__input" placeholder="normal o urgente"
                  value={editSeg.match_urgency || ''} onChange={e => setEditSeg(p => ({...p, match_urgency: e.target.value || null}))} />
              </div>
              <div className="ihv-portal-form__group">
                <label className="ihv-portal-form__label">Tier de monto (vacío = cualquiera)</label>
                <select className="ihv-portal-form__input"
                  value={editSeg.match_amount_tier || ''} onChange={e => setEditSeg(p => ({...p, match_amount_tier: e.target.value || null}))}>
                  <option value="">Cualquiera</option>
                  {tiers.map(t => <option key={t.tier_key} value={t.tier_key}>{t.label}</option>)}
                </select>
              </div>
              <div className="ihv-portal-form__group">
                <label className="ihv-portal-form__label">Etiqueta manual (vacío = cualquiera)</label>
                <input className="ihv-portal-form__input"
                  value={editSeg.match_manual_tag || ''} onChange={e => setEditSeg(p => ({...p, match_manual_tag: e.target.value || null}))} />
              </div>
              <div className="ihv-portal-form__group">
                <label className="ihv-portal-form__label">
                  <input type="checkbox" checked={editSeg.is_active}
                    onChange={e => setEditSeg(p => ({...p, is_active: e.target.checked}))} />
                  {' '}Activo
                </label>
              </div>
              <div style={{display:'flex', gap:'0.75rem'}}>
                <button type="submit" disabled={saving} className="ihv-portal-btn ihv-portal-btn--primary">
                  {saving ? 'Guardando…' : 'Guardar'}
                </button>
                <button type="button" className="ihv-portal-btn ihv-portal-btn--ghost"
                  onClick={() => setEditSeg(null)}>Cancelar</button>
              </div>
            </form>
          )}
        </section>

        <section className="ihv-staff-mgmt__section">
          <h2 className="ihv-staff-mgmt__subtitle">Umbrales de monto</h2>
          <table className="ihv-portal-table">
            <thead><tr><th>Clave</th><th>Etiqueta</th><th>Mínimo USD</th><th>Máximo USD</th></tr></thead>
            <tbody>
              {tiers.map(t => (
                <tr key={t.tier_key}>
                  <td><code>{t.tier_key}</code></td>
                  <td>{t.label}</td>
                  <td>{t.min_usd != null ? `$${t.min_usd.toLocaleString()}` : '—'}</td>
                  <td>{t.max_usd != null ? `$${t.max_usd.toLocaleString()}` : '—'}</td>
                </tr>
              ))}
            </tbody>
          </table>
          <p className="ihv-portal-form__hint">
            Los umbrales se editan vía migración SQL hasta que la empresa cierre la clasificación.
          </p>
        </section>
      </div>
    </div>
  );
}

function StatBar({ label, value, max, suffix }) {
  const pct = max > 0 ? Math.round((value / max) * 100) : 0;
  return (
    <div className="ihv-an-bar">
      <div className="ihv-an-bar__head">
        <span className="ihv-an-bar__label">{label}</span>
        <span className="ihv-an-bar__val">{value}{suffix || ''}</span>
      </div>
      <div className="ihv-an-bar__track"><div className="ihv-an-bar__fill" style={{ width: pct + '%' }} /></div>
    </div>
  );
}

function AnalyticsView({ caps }) {
  const sb = window.ihvSupabase;
  const [funnel, setFunnel] = useState(null);
  const [resp, setResp] = useState(null);
  const [perf, setPerf] = useState(null);
  const [vol, setVol] = useState(null);
  const [dim, setDim] = useState('request_type');
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!hasCap(caps, 'analytics.view')) return;
    let cancelled = false;
    async function load() {
      const [f, r, p] = await Promise.all([
        sb.rpc('staff_analytics_funnel'),
        sb.rpc('staff_analytics_response'),
        sb.rpc('staff_analytics_performance'),
      ]);
      if (cancelled) return;
      if (f.error || r.error || p.error) { setError('No se pudieron cargar las analíticas.'); return; }
      setFunnel(f.data); setResp(r.data); setPerf(p.data || []);
    }
    load();
    return () => { cancelled = true; };
  }, [caps]);

  useEffect(() => {
    if (!hasCap(caps, 'analytics.view')) return;
    let cancelled = false;
    sb.rpc('staff_analytics_volume', { p_dimension: dim }).then(({ data, error: e }) => {
      if (cancelled) return;
      if (e) { setError('No se pudo cargar el volumen.'); return; }
      setVol(data || []);
    });
    return () => { cancelled = true; };
  }, [caps, dim]);

  if (!hasCap(caps, 'analytics.view')) {
    return (
      <div className="ihv-portal-card"><div className="ihv-portal-card__body">
        <p>No tienes permiso para ver analíticas.</p>
      </div></div>
    );
  }

  const funnelMax = funnel ? Math.max(funnel.total || 1, 1) : 1;
  const volMax = vol && vol.length ? Math.max.apply(null, vol.map((v) => Number(v.count))) : 1;

  return (
    <div className="ihv-portal-card ihv-analytics">
      <div className="ihv-portal-card__header">
        <h1 className="ihv-portal-card__title">Analíticas</h1>
      </div>
      <div className="ihv-portal-card__body">
        {error && <p className="ihv-portal-form__error">{error}</p>}

        <section className="ihv-an-section">
          <h2 className="ihv-an-section__title">Embudo y conversión</h2>
          {funnel ? (
            <div className="ihv-an-grid">
              <StatBar label="Recibida" value={funnel.recibida} max={funnelMax} />
              <StatBar label="En revisión" value={funnel.en_revision} max={funnelMax} />
              <StatBar label="Cotizada" value={funnel.cotizada} max={funnelMax} />
              <StatBar label="Aceptada" value={funnel.aceptada} max={funnelMax} />
              <StatBar label="Rechazada" value={funnel.rechazada} max={funnelMax} />
              <StatBar label="Archivada" value={funnel.archivada} max={funnelMax} />
              <div className="ihv-an-kpi">
                <span className="ihv-an-kpi__num">{funnel.conversion_rate != null ? funnel.conversion_rate + '%' : '—'}</span>
                <span className="ihv-an-kpi__label">Conversión (aceptada / decididas)</span>
              </div>
            </div>
          ) : <p className="ihv-portal-form__hint">Cargando…</p>}
        </section>

        <section className="ihv-an-section">
          <h2 className="ihv-an-section__title">Tiempo de respuesta y SLA</h2>
          {resp ? (
            <div className="ihv-an-kpis">
              <div className="ihv-an-kpi"><span className="ihv-an-kpi__num">{resp.avg_hours_30d != null ? resp.avg_hours_30d + ' h' : '—'}</span><span className="ihv-an-kpi__label">Respuesta media (30 d)</span></div>
              <div className="ihv-an-kpi"><span className="ihv-an-kpi__num">{resp.quoted_30d}</span><span className="ihv-an-kpi__label">Cotizadas (30 d)</span></div>
              <div className="ihv-an-kpi ihv-an-kpi--warn"><span className="ihv-an-kpi__num">{resp.sla_at_risk}</span><span className="ihv-an-kpi__label">En riesgo SLA (&gt;48 h sin cotizar)</span></div>
            </div>
          ) : <p className="ihv-portal-form__hint">Cargando…</p>}
        </section>

        <section className="ihv-an-section">
          <h2 className="ihv-an-section__title">Desempeño por persona</h2>
          {perf ? (perf.length ? (
            <table className="ihv-portal-table">
              <thead><tr><th>Persona</th><th>Asignadas</th><th>Cotizadas</th><th>Ganadas</th><th>Resp. media (h)</th></tr></thead>
              <tbody>
                {perf.map((p) => (
                  <tr key={p.user_id}><td>{p.name}</td><td>{p.total}</td><td>{p.quoted}</td><td>{p.won}</td><td>{p.avg_hours != null ? p.avg_hours : '—'}</td></tr>
                ))}
              </tbody>
            </table>
          ) : <p className="ihv-portal-form__hint">Sin cotizaciones asignadas todavía.</p>) : <p className="ihv-portal-form__hint">Cargando…</p>}
        </section>

        <section className="ihv-an-section">
          <h2 className="ihv-an-section__title">Volumen y monto por dimensión</h2>
          <div className="ihv-an-dim">
            <label className="ihv-portal-form__label" htmlFor="an-dim">Dimensión</label>
            <select id="an-dim" className="ihv-portal-input" value={dim} onChange={(e) => setDim(e.target.value)}>
              <option value="request_type">Tipo</option>
              <option value="urgency">Urgencia</option>
              <option value="amount_tier">Tier de monto</option>
              <option value="manual_tag">Etiqueta manual</option>
            </select>
          </div>
          {vol ? (vol.length ? (
            <div className="ihv-an-grid">
              {vol.map((v) => (
                <StatBar key={v.bucket} label={v.bucket + ' (US$ ' + Number(v.amount_usd).toLocaleString('es-VE') + ')'} value={Number(v.count)} max={volMax} />
              ))}
            </div>
          ) : <p className="ihv-portal-form__hint">Sin datos.</p>) : <p className="ihv-portal-form__hint">Cargando…</p>}
        </section>
      </div>
    </div>
  );
}

function ClientsView({ caps }) {
  const sb = window.ihvSupabase;
  const [rows, setRows] = useState(null);
  const [q, setQ] = useState('');
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!hasCap(caps, 'clients.read')) return;
    let cancelled = false;
    sb.from('client_profiles')
      .select('user_id, full_name, email, company_name, company_rif, phone, whatsapp, created_at')
      .order('created_at', { ascending: false }).limit(200)
      .then(({ data, error: e }) => {
        if (cancelled) return;
        if (e) { setError('No se pudieron cargar los clientes.'); return; }
        setRows(data || []);
      });
    return () => { cancelled = true; };
  }, [caps]);

  if (!hasCap(caps, 'clients.read')) {
    return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>No tienes permiso para ver clientes.</p></div></div>;
  }

  const term = q.trim().toLowerCase();
  const filtered = (rows || []).filter((r) => !term ||
    (r.full_name || '').toLowerCase().includes(term) ||
    (r.company_name || '').toLowerCase().includes(term) ||
    (r.company_rif || '').toLowerCase().includes(term) ||
    (r.email || '').toLowerCase().includes(term));

  return (
    <div className="ihv-portal-card">
      <div className="ihv-portal-card__header"><h1 className="ihv-portal-card__title">Clientes</h1></div>
      <div className="ihv-portal-card__body">
        {error && <p className="ihv-portal-form__error">{error}</p>}
        <input className="ihv-portal-input" placeholder="Buscar por nombre, empresa, RIF o correo"
          value={q} onChange={(e) => setQ(e.target.value)} style={{ marginBottom: 16, maxWidth: 360 }} />
        {rows === null ? <p className="ihv-portal-form__hint">Cargando…</p> : (
          <table className="ihv-portal-table">
            <thead><tr><th>Empresa</th><th>Contacto</th><th>RIF</th><th>Correo</th><th></th></tr></thead>
            <tbody>
              {filtered.map((r) => (
                <tr key={r.user_id}>
                  <td>{r.company_name || '—'}</td>
                  <td>{r.full_name || '—'}</td>
                  <td>{r.company_rif || '—'}</td>
                  <td>{r.email}</td>
                  <td><a className="ihv-portal-btn ihv-portal-btn--secondary" href={'#/staff/clientes/' + r.user_id}>Ver</a></td>
                </tr>
              ))}
              {filtered.length === 0 && <tr><td colSpan="5" className="ihv-portal-form__hint">Sin resultados.</td></tr>}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

function ConfirmAnonymize({ expected, busy, onConfirm, onCancel }) {
  const [val, setVal] = useState('');
  const ok = expected ? val.trim() === expected.trim() : val.trim().toLowerCase() === 'anonimizar';
  return (
    <div>
      <input className="ihv-portal-input" placeholder={expected || 'escribe: anonimizar'} value={val} onChange={(e) => setVal(e.target.value)} style={{ maxWidth: 260, marginBottom: 8 }} />
      <div style={{ display: 'flex', gap: 8 }}>
        <button type="button" className="ihv-portal-btn ihv-portal-btn--danger" disabled={!ok || busy} onClick={onConfirm}>{busy ? 'Anonimizando…' : 'Confirmar'}</button>
        <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" disabled={busy} onClick={onCancel}>Cancelar</button>
      </div>
    </div>
  );
}

function ClientDetailView({ id, caps }) {
  const sb = window.ihvSupabase;
  const [client, setClient] = useState(null);
  const [quotes, setQuotes] = useState(null);
  const [error, setError] = useState(null);
  const [confirming, setConfirming] = useState(false);
  const [busy, setBusy] = useState(false);
  const [done, setDone] = useState(false);

  useEffect(() => {
    if (!hasCap(caps, 'clients.read')) return;
    let cancelled = false;
    async function load() {
      const [c, qs] = await Promise.all([
        sb.from('client_profiles').select('user_id, full_name, email, company_name, company_rif, phone, whatsapp, created_at').eq('user_id', id).single(),
        sb.from('quote_requests').select('id, reference_code, request_type, status, estimated_amount_usd, created_at').eq('client_user_id', id).order('created_at', { ascending: false }),
      ]);
      if (cancelled) return;
      if (c.error) { setError('No se encontró el cliente.'); return; }
      setClient(c.data); setQuotes(qs.data || []);
    }
    load();
    return () => { cancelled = true; };
  }, [id, caps]);

  async function anonymize() {
    setBusy(true); setError(null);
    const { error: e } = await sb.functions.invoke('staff-delete-client', { body: { client_user_id: id } });
    if (e) { setError('No se pudo anonimizar: ' + e.message); setBusy(false); return; }
    setDone(true); setBusy(false);
  }

  if (!hasCap(caps, 'clients.read')) {
    return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>Sin permiso.</p></div></div>;
  }
  if (done) {
    return <div className="ihv-portal-card"><div className="ihv-portal-card__body">
      <p>Cliente anonimizado. Sus cotizaciones se reasignaron a la cuenta del sistema.</p>
      <a className="ihv-portal-btn ihv-portal-btn--secondary" href="#/staff/clientes">Volver a clientes</a>
    </div></div>;
  }

  return (
    <div className="ihv-portal-card">
      <div className="ihv-portal-card__header">
        <h1 className="ihv-portal-card__title">{client ? (client.company_name || client.full_name || 'Cliente') : 'Cliente'}</h1>
        <a className="ihv-portal-btn ihv-portal-btn--secondary" href="#/staff/clientes">Volver</a>
      </div>
      <div className="ihv-portal-card__body">
        {error && <p className="ihv-portal-form__error">{error}</p>}
        {client === null ? <p className="ihv-portal-form__hint">Cargando…</p> : (
          <React.Fragment>
            <dl className="ihv-portal-dl">
              <dt>Contacto</dt><dd>{client.full_name || '—'}</dd>
              <dt>Correo</dt><dd>{client.email}</dd>
              <dt>RIF</dt><dd>{client.company_rif || '—'}</dd>
              <dt>Teléfono</dt><dd>{client.phone || '—'}</dd>
              <dt>WhatsApp</dt><dd>{client.whatsapp || '—'}</dd>
            </dl>

            <h2 className="ihv-an-section__title">Cotizaciones ({quotes ? quotes.length : 0})</h2>
            <table className="ihv-portal-table">
              <thead><tr><th>Referencia</th><th>Tipo</th><th>Estado</th><th>Monto US$</th></tr></thead>
              <tbody>
                {(quotes || []).map((qq) => (
                  <tr key={qq.id}><td><a href={'#/staff/cotizaciones/' + qq.id}>{qq.reference_code}</a></td><td>{qq.request_type}</td><td>{qq.status}</td><td>{qq.estimated_amount_usd != null ? Number(qq.estimated_amount_usd).toLocaleString('es-VE') : '—'}</td></tr>
                ))}
                {quotes && quotes.length === 0 && <tr><td colSpan="4" className="ihv-portal-form__hint">Sin cotizaciones.</td></tr>}
              </tbody>
            </table>

            {hasCap(caps, 'clients.delete') && (
              <div className="ihv-portal-danger">
                <h2 className="ihv-portal-danger__title">Zona peligrosa</h2>
                <p className="ihv-portal-form__hint">Anonimizar elimina los datos personales del cliente y su cuenta de acceso. Las cotizaciones se conservan reasignadas a la cuenta del sistema. Esta acción es irreversible.</p>
                {!confirming ? (
                  <button type="button" className="ihv-portal-btn ihv-portal-btn--danger" onClick={() => setConfirming(true)}>Anonimizar cliente</button>
                ) : (
                  <div style={{ marginTop: 12 }}>
                    <p className="ihv-portal-form__hint">¿Confirmas? Escribe el RIF para continuar.</p>
                    <ConfirmAnonymize expected={client.company_rif || ''} busy={busy} onConfirm={anonymize} onCancel={() => setConfirming(false)} />
                  </div>
                )}
              </div>
            )}
          </React.Fragment>
        )}
      </div>
    </div>
  );
}

function AuditLogView({ caps }) {
  const sb = window.ihvSupabase;
  const [rows, setRows] = useState(null);
  const [action, setAction] = useState('');
  const [page, setPage] = useState(0);
  const [error, setError] = useState(null);
  const PAGE = 50;

  useEffect(() => {
    if (!hasCap(caps, 'audit.view')) return;
    let cancelled = false;
    let req = sb.from('audit_log')
      .select('id, actor_user_id, actor_role, action, target_table, target_id, before, after, created_at')
      .order('created_at', { ascending: false })
      .range(page * PAGE, page * PAGE + PAGE - 1);
    if (action.trim()) req = req.ilike('action', '%' + action.trim() + '%');
    req.then(({ data, error: e }) => {
      if (cancelled) return;
      if (e) { setError('No se pudo cargar la auditoría.'); return; }
      setRows(data || []);
    });
    return () => { cancelled = true; };
  }, [caps, action, page]);

  if (!hasCap(caps, 'audit.view')) {
    return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>No tienes permiso para ver la auditoría.</p></div></div>;
  }

  return (
    <div className="ihv-portal-card">
      <div className="ihv-portal-card__header"><h1 className="ihv-portal-card__title">Auditoría</h1></div>
      <div className="ihv-portal-card__body">
        {error && <p className="ihv-portal-form__error">{error}</p>}
        <input className="ihv-portal-input" placeholder="Filtrar por acción (p. ej. quote.updated)"
          value={action} onChange={(e) => { setPage(0); setAction(e.target.value); }}
          style={{ marginBottom: 16, maxWidth: 360 }} />
        {rows === null ? <p className="ihv-portal-form__hint">Cargando…</p> : (
          <table className="ihv-portal-table ihv-audit-table">
            <thead><tr><th>Fecha</th><th>Acción</th><th>Rol</th><th>Tabla</th><th>Objetivo</th><th>Cambio</th></tr></thead>
            <tbody>
              {rows.map((r) => (
                <tr key={r.id}>
                  <td>{new Date(r.created_at).toLocaleString('es-VE')}</td>
                  <td>{r.action}</td>
                  <td>{r.actor_role || '—'}</td>
                  <td>{r.target_table || '—'}</td>
                  <td className="ihv-audit-id">{r.target_id || '—'}</td>
                  <td><code className="ihv-audit-diff">{r.before || r.after ? JSON.stringify({ before: r.before, after: r.after }) : '—'}</code></td>
                </tr>
              ))}
              {rows.length === 0 && <tr><td colSpan="6" className="ihv-portal-form__hint">Sin registros.</td></tr>}
            </tbody>
          </table>
        )}
        <div className="ihv-audit-pager">
          <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" disabled={page === 0} onClick={() => setPage((p) => Math.max(0, p - 1))}>Anterior</button>
          <span className="ihv-portal-form__hint">Página {page + 1}</span>
          <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" disabled={!rows || rows.length < PAGE} onClick={() => setPage((p) => p + 1)}>Siguiente</button>
        </div>
      </div>
    </div>
  );
}

function SheetView({ caps }) {
  const sb = window.ihvSupabase;
  const [rows, setRows] = useState(null);
  const [error, setError] = useState(null);
  const canEdit = hasCap(caps, 'sheet.edit');

  const load = React.useCallback(async () => {
    const { data, error: e } = await sb.from('quote_requests')
      .select('id, reference_code, request_type, status, urgency, estimated_amount_usd, currency, manual_tag, assigned_to, created_at, valid_until')
      .order('created_at', { ascending: false }).limit(500);
    if (e) { setError('No se pudo cargar la hoja.'); return; }
    setRows(data || []);
  }, []);

  useEffect(() => {
    if (!hasCap(caps, 'sheet.read')) return;
    load();
    const ch = sb.channel('sheet-quotes')
      .on('postgres_changes', { event: '*', schema: 'public', table: 'quote_requests' }, () => load())
      .subscribe();
    return () => { sb.removeChannel(ch); };
  }, [caps, load]);

  if (!hasCap(caps, 'sheet.read')) {
    return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>No tienes permiso para ver la hoja.</p></div></div>;
  }

  const cols = [
    { k: 'reference_code', t: 'Referencia' }, { k: 'request_type', t: 'Tipo' },
    { k: 'status', t: 'Estado' }, { k: 'urgency', t: 'Urgencia' },
    { k: 'estimated_amount_usd', t: 'Monto US$' }, { k: 'currency', t: 'Moneda' },
    { k: 'manual_tag', t: 'Etiqueta' }, { k: 'valid_until', t: 'Vigencia' },
    { k: 'created_at', t: 'Creada' },
  ];

  function toMatrix() {
    const header = cols.map((c) => c.t);
    const body = (rows || []).map((r) => cols.map((c) => {
      const v = r[c.k];
      return v == null ? '' : (c.k === 'created_at' ? new Date(v).toLocaleString('es-VE') : v);
    }));
    return [header, ...body];
  }

  function exportCsv() {
    const m = toMatrix();
    const csv = m.map((row) => row.map((cell) => {
      const s = String(cell).replace(/"/g, '""');
      return /[",\n]/.test(s) ? '"' + s + '"' : s;
    }).join(',')).join('\n');
    const blob = new Blob(['﻿' + csv], { type: 'text/csv;charset=utf-8;' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = 'cotizaciones-' + new Date().toISOString().slice(0, 10) + '.csv';
    a.click(); URL.revokeObjectURL(a.href);
  }

  function exportXlsx() {
    if (!window.XLSX) { setError('La librería de Excel no cargó. Usa CSV.'); return; }
    const ws = window.XLSX.utils.aoa_to_sheet(toMatrix());
    const wb = window.XLSX.utils.book_new();
    window.XLSX.utils.book_append_sheet(wb, ws, 'Cotizaciones');
    window.XLSX.writeFile(wb, 'cotizaciones-' + new Date().toISOString().slice(0, 10) + '.xlsx');
  }

  async function saveCell(rowId, key, value) {
    const allowed = { status: 'p_status', estimated_amount_usd: 'p_amount', manual_tag: 'p_manual_tag' };
    if (!allowed[key]) return;
    const args = { p_quote_id: rowId };
    args[allowed[key]] = key === 'estimated_amount_usd' ? (value === '' ? null : Number(value)) : (value || null);
    const { error: e } = await sb.rpc('staff_update_quote', args);
    if (e) setError(translateRpcError(e));
  }

  return (
    <div className="ihv-portal-card ihv-sheet">
      <div className="ihv-portal-card__header">
        <h1 className="ihv-portal-card__title">Hoja de cotizaciones</h1>
        <div style={{ display: 'flex', gap: 8 }}>
          <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" onClick={exportCsv}>Exportar CSV</button>
          <button type="button" className="ihv-portal-btn ihv-portal-btn--secondary" onClick={exportXlsx}>Exportar XLSX</button>
        </div>
      </div>
      <div className="ihv-portal-card__body">
        {error && <p className="ihv-portal-form__error">{error}</p>}
        {rows === null ? <p className="ihv-portal-form__hint">Cargando…</p> : (
          <div className="ihv-sheet__scroll">
            <table className="ihv-portal-table ihv-sheet__table">
              <thead><tr>{cols.map((c) => <th key={c.k}>{c.t}</th>)}</tr></thead>
              <tbody>
                {rows.map((r) => (
                  <tr key={r.id}>
                    {cols.map((c) => {
                      const editable = canEdit && ['status', 'estimated_amount_usd', 'manual_tag'].includes(c.k);
                      const v = r[c.k];
                      const disp = v == null ? '' : (c.k === 'created_at' ? new Date(v).toLocaleString('es-VE') : String(v));
                      if (!editable) {
                        return <td key={c.k}>{c.k === 'reference_code'
                          ? <a href={'#/staff/cotizaciones/' + r.id}>{disp}</a> : (disp === '' ? '—' : disp)}</td>;
                      }
                      return (
                        <td key={c.k}>
                          <input className="ihv-sheet__cell" defaultValue={disp}
                            onBlur={(e) => { if (e.target.value !== disp) saveCell(r.id, c.k, e.target.value); }} />
                        </td>
                      );
                    })}
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
        {!canEdit && <p className="ihv-portal-form__hint">Modo solo lectura. Edición requiere la capacidad sheet.edit.</p>}
      </div>
    </div>
  );
}

function App() {
  const { session, role, ready, signOut } = useSession();
  const route = useHashRoute();
  const caps = useStaffCapabilities(session);
  const clientHomeRef = useRef(null);

  const resolveClientHome = useCallback(async () => {
    if (clientHomeRef.current) return clientHomeRef.current;
    let dest = '/nueva';
    try {
      const { count, error: err } = await window.ihvSupabase
        .from('quote_requests')
        .select('id', { count: 'exact', head: true });
      if (!err && count != null && count >= 1) dest = '/cotizaciones';
    } catch (_) {}
    clientHomeRef.current = dest;
    return dest;
  }, []);

  useEffect(() => {
    if (!ready) return;

    const isPublic =
      route.name === 'login' ||
      route.name === 'signup' ||
      route.name === 'reset-password' ||
      route.name === 'callback' ||
      route.name === 'verify-email' ||
      route.name === 'staff-login' ||
      route.name === 'staff-2fa';

    if (route.name === 'home') {
      if (!session) { navigate('/login'); return; }
      if (role === 'staff') { navigate('/staff'); return; }
      resolveClientHome().then((dest) => navigate(dest));
      return;
    }

    if (!session && !isPublic) {
      const protectedNames = ['nueva', 'cotizaciones', 'cotizacion-detalle', 'perfil',
        'staff-home', 'staff-equipo', 'staff-segmentos'];
      if (protectedNames.indexOf(route.name) !== -1) {
        try { sessionStorage.setItem('ihv:postLoginRedirect', route.path); } catch (_) {}
      }
      navigate('/login');
      return;
    }

    if (session) {
      if (route.name === 'login' || route.name === 'signup' || route.name === 'callback') {
        let saved = null;
        try {
          saved = sessionStorage.getItem('ihv:postLoginRedirect');
          sessionStorage.removeItem('ihv:postLoginRedirect');
        } catch (_) {}
        if (role === 'staff') { navigate('/staff'); return; }
        if (saved) { navigate(saved); return; }
        resolveClientHome().then((dest) => navigate(dest));
        return;
      }
      const staffRoutes = ['staff-home', 'staff-equipo', 'staff-segmentos', 'staff-quotes', 'staff-quote-detalle',
        'staff-analytics', 'staff-clientes', 'staff-cliente-detalle', 'staff-auditoria', 'staff-hoja'];
      const clientRoutes = ['nueva', 'cotizaciones', 'cotizacion-detalle', 'perfil'];
      if (role === 'staff' && clientRoutes.indexOf(route.name) !== -1) {
        navigate('/staff'); return;
      }
      if (role !== 'staff' && staffRoutes.indexOf(route.name) !== -1) {
        navigate('/nueva'); return;
      }
      if (role !== 'staff' && route.name === 'staff-login') {
        navigate('/login'); return;
      }
    }
  }, [ready, session, role, route.name, route.path, resolveClientHome]);

  const renderView = () => {
    if (!ready) return <div className="ihv-portal-card"><div className="ihv-portal-card__body"><p>Cargando sesión.</p></div></div>;
    switch (route.name) {
      case 'login': return <LoginView />;
      case 'signup': return <SignupView />;
      case 'reset-password': return <ResetPasswordView />;
      case 'callback': return <CallbackView />;
      case 'verify-email': return <VerifyEmailView session={session} role={role} />;
      case 'nueva': return session ? <NuevaCotizacionView session={session} initialTab={route.params.tab} /> : null;
      case 'cotizaciones': return session ? <MisCotizacionesView /> : null;
      case 'cotizacion-detalle': return session ? <DetalleView id={route.params.id} /> : null;
      case 'perfil': return session ? <PerfilView session={session} /> : null;
      case 'staff-home':      return session ? <StaffHomeView session={session} caps={caps} /> : null;
      case 'staff-login':     return <StaffLoginView />;
      case 'staff-2fa':       return <StaffTotpEnrollView />;
      case 'staff-equipo':    return session ? <StaffManagementView session={session} caps={caps} /> : null;
      case 'staff-segmentos': return session ? <SegmentsAdminView caps={caps} /> : null;
      case 'staff-quotes': return session ? <StaffQuotesListView session={session} caps={caps} /> : null;
      case 'staff-quote-detalle': return session ? <StaffQuoteDetailView id={route.params.id} session={session} caps={caps} /> : null;
      case 'staff-analytics':       return session ? <AnalyticsView caps={caps} /> : null;
      case 'staff-clientes':        return session ? <ClientsView caps={caps} /> : null;
      case 'staff-cliente-detalle': return session ? <ClientDetailView id={route.params.id} session={session} caps={caps} /> : null;
      case 'staff-auditoria':       return session ? <AuditLogView caps={caps} /> : null;
      case 'staff-hoja':            return session ? <SheetView session={session} caps={caps} /> : null;
      case 'not-found': return <NotFoundView />;
      default: return null;
    }
  };

  return (
    <div className="ihv-portal-shell">
      <Topbar session={session} role={role} onSignOut={signOut} />
      <main className="ihv-portal-main">{renderView()}</main>
    </div>
  );
}

const rootEl = document.getElementById('root');
if (!rootEl) throw new Error('No se encontró #root en el DOM.');
ReactDOM.createRoot(rootEl).render(<ErrorBoundary><App /></ErrorBoundary>);
