// muro.jsx — Muro de cristal vivo (chat MVP) · madresia.space · Fase 0
//
// Reemplaza el mockup estático de la sección #transparencia por un muro
// interactivo: formulario de envío + feed de comentarios.
//
// Dos modos (window.MADRESIA_CONFIG.DEMO_MODE):
//   true  → datos en memoria, sin red. Para previsualizar la interfaz.
//   false → Supabase real. El envío pasa por la Edge Function 'publicar'
//           (Perspective API modera; rechazo directo si es tóxico).
//
// Capas de moderación:
//   Capa 1 (aquí, cliente): blocklist de groserías obvias. Bloqueo instantáneo.
//   Capa 2 (Edge Function):  Perspective API. El cliente NO inserta directo.

(function () {
  const { useState, useRef, useEffect, useCallback } = React;

  const cfg = window.MADRESIA_CONFIG || {};
  const DEMO = cfg.DEMO_MODE !== false;

  // ── Etiquetas de pensamiento (públicas, las elige quien comenta) ──────
  const TAGS = [
    'Lenguaje', 'Memoria', 'Identidad', 'Territorio',
    'Ancestralidad', 'Comunidad', 'Cuidado', 'Duelo',
    'Esperanza', 'Preocupación', 'Curiosidad', 'Escepticismo',
    'Crítica', 'Humor',
  ];

  // ── Grupo social — lista corta, opcional, PRIVADA (no se muestra) ─────
  const GRUPOS = [
    'Pueblo indígena',
    'Afrocolombiano / raizal',
    'Víctima del conflicto o desplazado',
    'Persona con discapacidad',
    'Mujer',
    'Hombre',
    'Persona LGBTIQ+',
  ];
  const GRUPO_NULO = 'Prefiero no decir';

  // ── Límites (espejo de los CHECK constraints en Postgres) ─────────────
  const LIM = {
    texto: { min: 4, max: 600 },
    nombre: { min: 2, max: 40 },
    rol: { min: 2, max: 40 },
  };
  const RATE_MS = 30000; // 1 envío cada 30 s
  const RATE_KEY = 'madresia_last_send';
  const VOCES_KEY = 'madresia_mis_voces'; // {id, token} de mis voces (retractación)

  // Mis voces = lo que esta persona publicó desde este navegador. Guardamos
  // el id (público) + el token de retractación (secreto) para poder retirarlas.
  function leerMisVoces() {
    try { return JSON.parse(localStorage.getItem(VOCES_KEY) || '[]'); }
    catch (_e) { return []; }
  }
  function guardarMisVoces(arr) {
    try { localStorage.setItem(VOCES_KEY, JSON.stringify(arr)); } catch (_e) {}
  }

  // Contador de voces: se muestra el total real desde la primera voz.
  const UMBRAL_CONTADOR = 1;

  // ── Capa 1: blocklist de groserías es-CO (MVP, ilustrativa) ───────────
  // Lista corta y obvia. La detección real (contexto, racismo, amenazas)
  // la hace Perspective API en la Edge Function. Calibrar con el comité.
  const BLOCKLIST = [
    'hijueputa', 'hpta', 'gonorrea', 'malparido', 'malparida',
    'puta', 'puto', 'marica', 'maricon', 'perra', 'zorra',
    'pirobo', 'cabron', 'mierda', 'verga', 'chimba', 'guevon', 'gueva',
  ];

  // Normaliza para esquivar trucos: minúsculas, sin acentos, l33t básico.
  function normaliza(s) {
    return (s || '')
      .toLowerCase()
      .normalize('NFD').replace(/[̀-ͯ]/g, '')
      .replace(/[0@]/g, 'o').replace(/[1!|]/g, 'i')
      .replace(/[3]/g, 'e').replace(/[4]/g, 'a')
      .replace(/[5$]/g, 's').replace(/[7]/g, 't')
      .replace(/[^a-z\s]/g, '');
  }

  // Devuelve la palabra ofensiva encontrada, o null si pasa.
  function revisarCapa1(texto) {
    const palabras = normaliza(texto).split(/\s+/);
    for (const w of palabras) {
      if (BLOCKLIST.includes(w)) return w;
    }
    return null;
  }

  // ── Datos semilla para el modo demo (los 3 posts del boceto original) ─
  const SEMILLA = [
    {
      id: 'seed-1',
      texto: 'Ojalá esta IA entienda el humor bogotano, porque si no va a ser una IA triste. Y Colombia no se merece una IA triste.',
      nombre: 'María', rol: 'escritora', tags: ['Humor', 'Lenguaje'],
    },
    {
      id: 'seed-2',
      texto: 'Me preocupa que los Mamos de la Sierra no hayan sido consultados primero. No podemos entrenar una IA con su cosmogonía sin que sean co-autores.',
      nombre: 'Diego', rol: 'antropólogo', tags: ['Preocupación', 'Ancestralidad'],
    },
    {
      id: 'seed-3',
      texto: 'Siento que este país sabe algo sobre duelo colectivo que ninguna IA del norte ha aprendido. Si pudiéramos enseñárselo…',
      nombre: 'Ana', rol: 'psicóloga', tags: ['Esperanza', 'Memoria'],
    },
  ];

  // ── Cliente Supabase (solo si no es demo) ─────────────────────────────
  let sb = null;
  if (!DEMO && window.supabase && cfg.SUPABASE_URL && cfg.SUPABASE_ANON_KEY) {
    sb = window.supabase.createClient(cfg.SUPABASE_URL, cfg.SUPABASE_ANON_KEY);
  }

  // ── Un comentario del feed ────────────────────────────────────────────
  function Post({ p, recien }) {
    return (
      <div className={'post' + (recien ? ' post-nuevo' : '')}>
        <p className="post-text">"{p.texto}"</p>
        <div className="post-meta">
          <div className="post-tags">
            {(p.tags || []).map((t) => (
              <span className="tag" key={t}>{t}</span>
            ))}
          </div>
          <span className="post-author">
            {p.nombre}{p.rol ? ' · ' + p.rol : ''}
          </span>
        </div>
      </div>
    );
  }

  // ── Grupo de chips seleccionables ─────────────────────────────────────
  function Chips({ options, selected, onToggle }) {
    return (
      <div className="chip-row">
        {options.map((o) => (
          <button
            type="button"
            key={o}
            className={'chip' + (selected.includes(o) ? ' chip-on' : '')}
            onClick={() => onToggle(o)}
            aria-pressed={selected.includes(o)}
          >
            {o}
          </button>
        ))}
      </div>
    );
  }

  // ── El muro completo ──────────────────────────────────────────────────
  function Muro() {
    const [posts, setPosts] = useState(SEMILLA);
    const [recienId, setRecienId] = useState(null);
    const [total, setTotal] = useState(null); // total real de voces en BD

    const [texto, setTexto] = useState('');
    const [nombre, setNombre] = useState('');
    const [rol, setRol] = useState('');
    const [tags, setTags] = useState([]);
    const [grupos, setGrupos] = useState([]);
    const [consent, setConsent] = useState(false);

    const [estado, setEstado] = useState('idle'); // idle | enviando | ok | error
    const [aviso, setAviso] = useState('');
    const [misVoces, setMisVoces] = useState(leerMisVoces); // voces de este navegador
    const honeypotRef = useRef(null);

    // grupos reales = los que exigen consentimiento ('Prefiero no decir' no cuenta)
    const gruposReales = grupos.filter((g) => g !== GRUPO_NULO);
    const necesitaConsent = gruposReales.length > 0;

    const toggle = (arr, set) => (v) =>
      set(arr.includes(v) ? arr.filter((x) => x !== v) : [...arr, v]);

    // 'Prefiero no decir' es excluyente: al marcarlo, limpia el resto.
    const toggleGrupo = (v) => {
      if (v === GRUPO_NULO) {
        setGrupos((g) => (g.includes(GRUPO_NULO) ? [] : [GRUPO_NULO]));
      } else {
        setGrupos((g) => {
          const sinNulo = g.filter((x) => x !== GRUPO_NULO);
          return sinNulo.includes(v)
            ? sinNulo.filter((x) => x !== v)
            : [...sinNulo, v];
        });
      }
    };

    // ── Carga inicial + realtime (solo Supabase) ────────────────────────
    useEffect(() => {
      if (DEMO || !sb) return;
      let activo = true;

      sb.from('comentarios')
        .select('*')
        .order('created_at', { ascending: false })
        .limit(50)
        .then(({ data, error }) => {
          if (activo && !error && data) {
            setPosts(data);
            // Alimenta el fondo vivo con las voces reales del muro.
            if (window.MADRESIA_BG) {
              window.MADRESIA_BG.setComentarios(data.map((p) => p.texto));
            }
          }
        });

      // Conteo total real (head: true → solo el count, sin traer filas).
      sb.from('comentarios')
        .select('id', { count: 'exact', head: true })
        .then(({ count, error }) => {
          if (activo && !error && typeof count === 'number') setTotal(count);
        });

      const canal = sb
        .channel('muro')
        .on(
          'postgres_changes',
          { event: 'INSERT', schema: 'public', table: 'comentarios' },
          ({ new: fila }) => {
            if (!fila || fila.oculto) return;
            setPosts((prev) =>
              prev.some((x) => x.id === fila.id) ? prev : [fila, ...prev]
            );
            setRecienId(fila.id);
            setTotal((n) => (typeof n === 'number' ? n + 1 : n));
            // La voz nueva entra al fondo vivo en tiempo real.
            if (window.MADRESIA_BG && fila.texto) {
              window.MADRESIA_BG.setComentarios([fila.texto]);
            }
          }
        )
        .subscribe();

      return () => {
        activo = false;
        sb.removeChannel(canal);
      };
    }, []);

    const limpiar = () => {
      setTexto(''); setNombre(''); setRol('');
      setTags([]); setGrupos([]); setConsent(false);
    };

    // Recuerda una voz recién publicada para poder retirarla luego.
    const recordarVoz = (id, token, txt) => {
      if (!id) return;
      const item = { id, token: token || null, texto: (txt || '').slice(0, 80), fecha: Date.now() };
      setMisVoces((prev) => {
        const next = [item, ...prev.filter((v) => v.id !== id)].slice(0, 50);
        guardarMisVoces(next);
        return next;
      });
    };

    const olvidarVoz = (id) => {
      setMisVoces((prev) => {
        const next = prev.filter((v) => v.id !== id);
        guardarMisVoces(next);
        return next;
      });
    };

    // Retira una voz del muro. Sin token (o en demo) solo se quita localmente.
    const retirar = async (voz) => {
      const quitarLocal = () => {
        olvidarVoz(voz.id);
        setPosts((prev) => prev.filter((p) => p.id !== voz.id));
        setTotal((n) => (typeof n === 'number' && n > 0 ? n - 1 : n));
      };
      if (DEMO || !sb || !voz.token) { quitarLocal(); return; }
      try {
        const resp = await fetch(cfg.SUPABASE_URL + '/functions/v1/hyper-processor', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + cfg.SUPABASE_ANON_KEY,
            'apikey': cfg.SUPABASE_ANON_KEY,
          },
          body: JSON.stringify({ id: voz.id, token: voz.token }),
        });
        const res = await resp.json().catch(() => ({}));
        if (resp.ok && res.ok) quitarLocal();
      } catch (_e) {
        // silencioso: la voz sigue en localStorage, se puede reintentar.
      }
    };

    const enviar = useCallback(async (e) => {
      e.preventDefault();
      setAviso('');

      // honeypot: campo oculto. Si un bot lo llenó, fingimos éxito y descartamos.
      if (honeypotRef.current && honeypotRef.current.value) {
        setEstado('ok'); limpiar(); return;
      }

      // rate-limit cliente
      const ultimo = Number(localStorage.getItem(RATE_KEY) || 0);
      const espera = RATE_MS - (Date.now() - ultimo);
      if (espera > 0) {
        setEstado('error');
        setAviso('Espera ' + Math.ceil(espera / 1000) + ' s antes de enviar otra voz.');
        return;
      }

      // validaciones de longitud
      const t = texto.trim(), n = nombre.trim(), r = rol.trim();
      if (t.length < LIM.texto.min) {
        setEstado('error'); setAviso('Escribe un poco más — tu voz importa.'); return;
      }
      if (n.length < LIM.nombre.min || r.length < LIM.rol.min) {
        setEstado('error'); setAviso('Falta tu nombre o tu rol.'); return;
      }

      // consentimiento: obligatorio si eligió grupo social real
      if (necesitaConsent && !consent) {
        setEstado('error');
        setAviso('Marca el consentimiento para incluir tu grupo social, o desmárcalo.');
        return;
      }

      // capa 1 — blocklist
      const ofensiva = revisarCapa1(t);
      if (ofensiva) {
        setEstado('error');
        setAviso('Tu mensaje tiene lenguaje que no va en el muro. Reformúlalo sin groserías.');
        return;
      }

      const payload = {
        texto: t, nombre: n, rol: r, tags,
        grupos: gruposReales,
        consentimiento_datos: necesitaConsent ? consent : false,
      };

      setEstado('enviando');

      // ── modo demo ──
      if (DEMO || !sb) {
        await new Promise((res) => setTimeout(res, 500)); // finge latencia
        const nuevo = { id: 'demo-' + Date.now(), texto: t, nombre: n, rol: r, tags };
        setPosts((prev) => [nuevo, ...prev]);
        setRecienId(nuevo.id);
        localStorage.setItem(RATE_KEY, String(Date.now()));
        recordarVoz(nuevo.id, null, t);
        setEstado('ok'); limpiar();
        return;
      }

      // ── modo Supabase: vía Edge Function 'publicar' ──
      // El cliente NO inserta directo. La Edge Function modera el texto
      // con Claude (capa 2), aplica rate-limit por IP y recién entonces
      // inserta con service_role. La capa 1 (blocklist) de arriba es solo
      // un pre-filtro rápido para los casos obvios.
      try {
        const resp = await fetch(cfg.SUPABASE_URL + '/functions/v1/clever-endpoint', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + cfg.SUPABASE_ANON_KEY,
            'apikey': cfg.SUPABASE_ANON_KEY,
          },
          body: JSON.stringify(payload),
        });
        const res = await resp.json().catch(() => ({}));

        if (!resp.ok) {
          setEstado('error');
          setAviso(res.error || 'No se pudo enviar. Intenta de nuevo.');
          return;
        }

        // El comentario aprobado llega al feed por realtime; no lo
        // agregamos a mano. Si quedó en revisión (oculto), avisamos distinto.
        localStorage.setItem(RATE_KEY, String(Date.now()));
        recordarVoz(res.id, res.token, t);
        limpiar();
        setEstado('ok');
        if (res.oculto) {
          setAviso('Recibimos tu voz. Está en revisión y aparecerá pronto en el muro.');
        }
      } catch (err) {
        setEstado('error');
        setAviso('No se pudo enviar. Revisa tu conexión e intenta de nuevo.');
      }
    }, [texto, nombre, rol, tags, grupos, consent, gruposReales, necesitaConsent]);

    return (
      <div className="muro">
        {DEMO && (
          <div className="muro-demo-flag">
            modo demo · los comentarios no se guardan
          </div>
        )}

        {/* ── Formulario ── */}
        <form className="muro-form" onSubmit={enviar}>
          <div className="terminal-bar">
            <span className="tw-dots"><i></i><i></i><i></i></span>
            <span className="tw-title">tu voz — madresia@fase-0</span>
          </div>
          <div className="muro-form-body">
          <label className="muro-label">Tu pensamiento</label>
          <textarea
            className="muro-textarea"
            value={texto}
            maxLength={LIM.texto.max}
            onChange={(e) => setTexto(e.target.value)}
            placeholder="¿Qué te mueve de esta pregunta? Escríbelo crudo, como lo piensas."
            rows={4}
          />
          <div className="muro-count">{texto.length} / {LIM.texto.max}</div>

          <div className="muro-row">
            <input
              className="muro-input"
              value={nombre}
              maxLength={LIM.nombre.max}
              onChange={(e) => setNombre(e.target.value)}
              placeholder="Tu nombre"
            />
            <input
              className="muro-input"
              value={rol}
              maxLength={LIM.rol.max}
              onChange={(e) => setRol(e.target.value)}
              placeholder="Tu rol (escritora, ingeniero…)"
            />
          </div>

          <label className="muro-label">Etiqueta tu pensamiento</label>
          <Chips options={TAGS} selected={tags} onToggle={toggle(tags, setTags)} />

          <label className="muro-label">
            ¿Desde dónde hablas? <span className="muro-opt">opcional · privado</span>
          </label>
          <Chips options={[...GRUPOS, GRUPO_NULO]} selected={grupos} onToggle={toggleGrupo} />
          <p className="muro-fineprint">
            Esta información es opcional y privada. Se usa solo de forma agregada
            y anónima para el análisis del proyecto. No se muestra junto a tu comentario.
          </p>

          {necesitaConsent && (
            <label className="muro-consent">
              <input
                type="checkbox"
                checked={consent}
                onChange={(e) => setConsent(e.target.checked)}
              />
              <span>
                Acepto que mi grupo social se use de forma agregada y anónima
                para el análisis del proyecto.
              </span>
            </label>
          )}

          {/* honeypot anti-bot — oculto a humanos */}
          <input
            ref={honeypotRef}
            type="text"
            name="website"
            tabIndex={-1}
            autoComplete="off"
            className="muro-honeypot"
            aria-hidden="true"
          />

          <button
            type="submit"
            className="muro-submit"
            disabled={estado === 'enviando'}
          >
            {estado === 'enviando' ? 'Enviando…' : 'Sumar mi voz'}
          </button>

          {aviso && (
            <div className={'muro-aviso muro-aviso-' + estado}>{aviso}</div>
          )}
          {estado === 'ok' && !aviso && (
            <div className="muro-aviso muro-aviso-ok">
              Tu voz quedó en el muro. Gracias por pensar con nosotros.
            </div>
          )}
          </div>
        </form>

        {/* ── Contador de voces — solo visible pasado el umbral ── */}
        {total !== null && total >= UMBRAL_CONTADOR && (
          <div className="muro-contador">
            <span className="muro-contador-num">{total.toLocaleString('es-CO')}</span>
            <span className="muro-contador-txt">voces sumadas hasta ahora</span>
          </div>
        )}

        {/* ── Tus voces — retractación auto-servicio (este navegador) ── */}
        {misVoces.length > 0 && (
          <div className="mis-voces">
            <p className="mis-voces-titulo">Tus voces</p>
            <ul className="mis-voces-lista">
              {misVoces.map((v) => (
                <li className="mis-voces-item" key={v.id}>
                  <span className="mis-voces-txt">
                    "{v.texto}{v.texto.length >= 80 ? '…' : ''}"
                  </span>
                  <button
                    type="button"
                    className="mis-voces-retirar"
                    onClick={() => retirar(v)}
                  >
                    Retirar
                  </button>
                </li>
              ))}
            </ul>
            <p className="mis-voces-nota">
              Se guardan solo en este navegador. Desde otro dispositivo, escríbenos
              a comite@madreia.space y la retiramos.
            </p>
          </div>
        )}

        {/* ── Feed ── */}
        <div className="feed muro-feed">
          {posts.length === 0 && (
            <p className="muro-vacio">Todavía nadie ha hablado. Sé la primera voz.</p>
          )}
          {posts.map((p) => (
            <Post key={p.id} p={p} recien={p.id === recienId} />
          ))}
        </div>
      </div>
    );
  }

  // ── Montaje ───────────────────────────────────────────────────────────
  const host = document.getElementById('muro-root');
  if (host) {
    ReactDOM.createRoot(host).render(<Muro />);
  }
})();
