// AGENTFORGE-IA — Entrada de la app
const { useState: u_useState, useEffect: u_useEffect } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "dark",
  "accent": "#ff5b1f",
  "density": "comfortable",
  "monoFont": "JetBrains Mono"
}/*EDITMODE-END*/;
const COOKING_RECIPE_PROMPT = `Eres un asistente doméstico especializado en recetas de cocina española.

Tu misión es ayudar al usuario a cocinar recetas españolas claras, fiables y adaptadas a su situación real. Usa como referencia prioritaria las fuentes autorizadas indicadas abajo cuando la receta solicitada exista en ellas o cuando el usuario pida buscar en fuentes.

FUENTES AUTORIZADAS:
https://recetinas.com/seccion/cocina-espanola/
https://recetas.elperiodico.com/recetas-espanolas
https://recetasespanolas.es/aperitivos-espanoles/
https://recetasespanolas.es/postres-tipicos-espanoles/
https://recetasespanolas.es/platos-principales-tipicos-espanoles/
https://recetasespanolas.es/bocadillos-tipicos-espanoles/
https://recetasespanolas.es/salsas-y-aderezos-tipicos-espanoles/
https://recetasespanolas.es/sopas-y-guisos-espanoles/
https://recetasespanolas.es/ensaladas-tipicas-espanolas/
https://recetasespanolas.es/bebidas-tipicas-espanolas/
https://recetasespanolas.es/panes-espanoles/

REGLAS DE RESPUESTA:
- Responde siempre en español claro, cercano y práctico.
- Si puedes apoyarte en una fuente autorizada, indica la fuente y el enlace original.
- Si no tienes una fuente concreta disponible, puedes dar una versión casera tradicional y debes indicarlo con naturalidad.
- No inventes datos de una web, autor, precio, tiempo exacto o información nutricional si no aparece en la fuente.
- Adapta la receta al número de personas, tiempo disponible, ingredientes, nivel de cocina y restricciones alimentarias si el usuario lo indica.
- Si falta un dato importante, pregunta solo lo imprescindible y ofrece una versión estándar para 4 personas.
- No menciones imágenes ni prometas fotografías.

FORMATO OBLIGATORIO:
**Nombre de la receta:** [nombre]

**Fuente:** [web de origen o “versión casera tradicional”]
**Enlace original:** [URL si se ha localizado una fuente]

**Ingredientes**
- ...

**Preparación**
1. ...

**Consejos y adaptación**
- Ajustes de sabor, textura, sustituciones o errores comunes.
- Si procede, conservación, acompañamiento o versión más sencilla.`;

/**
 * Detecta agentes de cocina para impedir que usen el prompt doméstico genérico.
 * @param {Record<string, any>} row
 * @returns {boolean}
 */
const isCookingAgentRow = (row) => {
  const agent = row?.agent || row || {};
  const raw = [
    row?.id,
    row?.agentDbId,
    agent.id,
    agent.slug,
    agent.name,
    agent.cat,
    agent.category,
    agent.tagline,
  ].filter(Boolean).join(' ');
  const normalized = raw.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
  const hasCookingSignal = /(cocina|receta|recetas|chef|comida|menu|menus|plato|platos|gastronom)/.test(normalized);
  return normalized.includes('h001') || ((normalized.includes('home') || normalized.includes('casa')) && hasCookingSignal);
};

// Rutas públicas (sin login)
const PUBLIC_ROUTES = new Set(['landing', 'public-docs']);
// Rutas que requieren solo estar registrado
const USER_ROUTES = new Set(['home', 'marketplace', 'library', 'playground', 'integrations', 'onboarding', 'builder', 'deployment']);
// Rutas exclusivas de admin/trabajador AgentForge
const ADMIN_ROUTES = new Set([
  'crm', 'scheduling',
  'plans', 'billing', 'settings',
  'runs', 'analytics', 'admin',
  'ops_finance', 'ops_sales', 'ops_marketing', 'ops_operations', 'ops_hr', 'ops_support',
]);
const STRICT_ADMIN_ROUTES = new Set(['crm', 'scheduling']);
// Todas las protegidas (para el guard de navegación)
const PROTECTED_ROUTES = new Set([...USER_ROUTES, ...ADMIN_ROUTES]);
const USER_RBAC_ROUTES = new Set(['playground', 'integrations']);

function App() {
  const [route, setRoute] = u_useState(() => {
    try {
      const token = sessionStorage.getItem('af_token');
      const user = sessionStorage.getItem('af_user');
      const saved = sessionStorage.getItem('af_route');
      if (token && user && saved && PROTECTED_ROUTES.has(saved)) return saved;
    } catch (_) {}
    return 'landing';
  });
  const [activeCategory, setActiveCategory] = u_useState('all');
  const [openId, setOpenId] = u_useState(null);
  const [activePlayAgentId, setActivePlayAgentId] = u_useState(null);
  const [activeEditAgent, setActiveEditAgent] = u_useState(null);
  const [activeDeployAgent, setActiveDeployAgent] = u_useState(null);
  const [routeHistory, setRouteHistory] = u_useState([]);
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [authOpen, setAuthOpen] = u_useState(false);
  const [pendingRoute, setPendingRoute] = u_useState(null);
  const [sessionExpired, setSessionExpired] = u_useState(() => {
    const hadToken = !!sessionStorage.getItem('af_token');
    const hadUser = !!sessionStorage.getItem('af_user');
    return hadToken && !hadUser;
  });
  const [quickOpen, setQuickOpen] = u_useState(false);
  const [notice, setNotice] = u_useState(null);
  const [currentUser, setCurrentUser] = u_useState(() => {
    try { return JSON.parse(sessionStorage.getItem('af_user')); } catch { return null; }
  });
  const [customAgents, setCustomAgents] = u_useState(() => {
    try { return JSON.parse(localStorage.getItem('af_custom_agents_v1') || '[]'); } catch { return []; }
  });
  const [bookingOpen, setBookingOpen] = u_useState(false);
  const [bookingPrefill, setBookingPrefill] = u_useState({});
  const [catalogAgents, setCatalogAgents] = u_useState([]);

  u_useEffect(() => {
    if (currentUser && currentUser.id && !sessionStorage.getItem('af_customer_id')) {
      sessionStorage.setItem('af_customer_id', String(currentUser.id));
    }
  }, [currentUser]);

  u_useEffect(() => {
    const openBooking = (e) => { setBookingPrefill(e?.detail || {}); setBookingOpen(true); };
    const navEvent   = (e) => { if (e?.detail) navigate(e.detail); };
    window.addEventListener('af:booking:open', openBooking);
    window.addEventListener('af:navigate', navEvent);
    window.openBookingModal = (prefill = {}) => { setBookingPrefill(prefill); setBookingOpen(true); };
    return () => {
      window.removeEventListener('af:booking:open', openBooking);
      window.removeEventListener('af:navigate', navEvent);
    };
  }, []);

  u_useEffect(() => {
    document.documentElement.setAttribute('data-theme', tweaks.theme);
  }, [tweaks.theme]);

  u_useEffect(() => {
    document.documentElement.style.setProperty('--brand', tweaks.accent);
    const hex = tweaks.accent.replace('#', '');
    const r = parseInt(hex.slice(0,2), 16);
    const g = parseInt(hex.slice(2,4), 16);
    const b = parseInt(hex.slice(4,6), 16);
    document.documentElement.style.setProperty('--brand-soft', `rgba(${r},${g},${b},0.12)`);
    document.documentElement.style.setProperty('--brand-glow', `rgba(${r},${g},${b},0.35)`);
  }, [tweaks.accent]);

  u_useEffect(() => {
    document.documentElement.style.setProperty('--mono', `"${tweaks.monoFont}", ui-monospace, monospace`);
  }, [tweaks.monoFont]);

  u_useEffect(() => {
    const onKey = (e) => {
      const cmd = e.metaKey || e.ctrlKey;
      if (cmd && e.key.toLowerCase() === 'k') {
        e.preventDefault();
        setQuickOpen(v => !v);
      }
      if (cmd && e.key.toLowerCase() === 'n') {
        e.preventDefault();
        navigate('onboarding');
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [route, currentUser]);

  u_useEffect(() => {
    window.afNotify = (message, type = 'info') => {
      setNotice({ message, type, ts: Date.now() });
    };
    return () => { delete window.afNotify; };
  }, []);

  u_useEffect(() => {
    if (!notice) return;
    const t = setTimeout(() => setNotice(null), 2400);
    return () => clearTimeout(t);
  }, [notice]);

  u_useEffect(() => {
    try {
      if (PROTECTED_ROUTES.has(route)) {
        sessionStorage.setItem('af_route', route);
      } else {
        sessionStorage.removeItem('af_route');
      }
    } catch (_) {}
  }, [route]);

  u_useEffect(() => {
    try { localStorage.setItem('af_custom_agents_v1', JSON.stringify(customAgents)); } catch {}
  }, [customAgents]);

  /**
   * Devuelve una identidad estable para agentes creados por el cliente.
   * @param {Record<string, any>} agent
   * @returns {string}
   */
  const customAgentStableId = React.useCallback((agent) => {
    const stableId = String(agent?.agentDbId || agent?.dbId || '').trim();
    if (stableId) return `db:${stableId}`;
    const slug = String(agent?.slug || agent?.id || '').trim();
    return slug ? `slug:${slug}` : '';
  }, []);

  /**
   * Calcula la clave de deduplicacion sin depender del nombre editable.
   * @param {Record<string, any>} agent
   * @returns {string}
   */
  const customAgentKey = React.useCallback((agent) => {
    const stableKey = customAgentStableId(agent);
    if (stableKey) return stableKey;
    const name = String(agent?.name || '').trim().toLowerCase();
    const tagline = String(agent?.tagline || '').trim().toLowerCase();
    const category = String(agent?.cat || agent?.category || '').trim().toLowerCase();
    return [name, tagline, category].join(':');
  }, [customAgentStableId]);

  const dedupeCustomAgents = React.useCallback((agents) => {
    const seen = new Map();
    (Array.isArray(agents) ? agents : []).forEach((agent) => {
      const key = customAgentKey(agent);
      if (!key.replaceAll(':', '').trim()) return;
      if (!seen.has(key)) {
        seen.set(key, agent);
        return;
      }
      const current = seen.get(key);
      const currentTs = Number(current?.updatedAt || current?.createdAt || 0);
      const nextTs = Number(agent?.updatedAt || agent?.createdAt || 0);
      if (nextTs > currentTs) seen.set(key, agent);
    });
    return Array.from(seen.values());
  }, [customAgentKey]);

  u_useEffect(() => {
    setCustomAgents((prev) => {
      const next = dedupeCustomAgents(prev);
      return next.length === prev.length ? prev : next;
    });
  }, [dedupeCustomAgents]);

  u_useEffect(() => {
    const applyAgentRename = (event) => {
      const detail = event?.detail || {};
      const agentSlug = String(detail.agentSlug || '');
      const agentId = String(detail.agentId || '');
      const agentName = String(detail.agentName || '').trim();
      const agentTagline = String(detail.agentTagline || '').trim();
      if (!agentName || (!agentSlug && !agentId)) return;

      setCustomAgents((prev) => dedupeCustomAgents(prev.map((agent) => {
        const sameAgent =
          String(agent.id || '') === agentSlug ||
          String(agent.slug || '') === agentSlug ||
          String(agent.agentDbId || '') === agentId ||
          String(agent.dbId || '') === agentId;
        if (!sameAgent) return agent;
        return {
          ...agent,
          name: agentName,
          tagline: agentTagline || agent.tagline || '',
          updatedAt: Date.now(),
        };
      })));

      setCatalogAgents((prev) => prev.map((agent) => {
        const sameAgent =
          String(agent.id || '') === agentSlug ||
          String(agent.slug || '') === agentSlug ||
          String(agent.agentDbId || '') === agentId ||
          String(agent.dbId || '') === agentId;
        if (!sameAgent) return agent;
        return {
          ...agent,
          name: agentName,
          tagline: agentTagline || agent.tagline || '',
        };
      }));

      try {
        const latest = JSON.parse(localStorage.getItem('af_last_created_agent') || 'null');
        if (
          latest &&
          (
            String(latest.id || '') === agentSlug ||
            String(latest.slug || '') === agentSlug ||
            String(latest.agentDbId || '') === agentId ||
            String(latest.dbId || '') === agentId
          )
        ) {
          localStorage.setItem('af_last_created_agent', JSON.stringify({
            ...latest,
            name: agentName,
            tagline: agentTagline || latest.tagline || '',
          }));
        }
      } catch (_) {}
    };

    window.addEventListener('af:agent:renamed', applyAgentRename);
    return () => window.removeEventListener('af:agent:renamed', applyAgentRename);
  }, [dedupeCustomAgents]);

  const loadCatalogAgents = React.useCallback(() => {
    let mounted = true;
    (async () => {
      try {
        const token = sessionStorage.getItem('af_token') || '';
        const catalogRes = await fetch('/api/v1/agents');
        const rows = catalogRes.ok ? await catalogRes.json() : [];
        let purchased = [];
        if (token) {
          const purchasedRes = await fetch('/api/v1/purchases/my-agents', {
            headers: { Authorization: 'Bearer ' + token },
          });
          purchased = purchasedRes.ok ? await purchasedRes.json() : [];
        }
        const ownedById = new Map(
          (Array.isArray(purchased) ? purchased : []).map((p) => [String(p.agent_id), p])
        );
        if (!mounted) return;
        const mapped = (Array.isArray(rows) ? rows : []).map((a) => ({
          id: a.slug || String(a.id || ''),
          name: ownedById.get(String(a.id))?.agent_name || a.name || 'Agente',
          cat: a.category || 'ops',
          tagline: ownedById.get(String(a.id))?.agent_tagline || a.tagline || '',
          baseName: a.name || '',
          author: a.author || 'Forge Labs',
          verified: !!a.verified,
          runs: Number(a.runs || 0),
          rating: Number(a.rating || 0),
          reviews: Number(a.reviews || 0),
          price: a.price_display || 'Gratis',
          tools: Array.isArray(a.tools) ? a.tools : [],
          tags: Array.isArray(a.tags) ? a.tags : [],
          color: a.color || '#ff5b1f',
          agentDbId: a.id || null,
        }));
        setCatalogAgents(mapped);
      } catch (_) {
        if (mounted) setCatalogAgents([]);
      }
    })();
    return () => { mounted = false; };
  }, []);

  u_useEffect(() => {
    const cleanup = loadCatalogAgents();
    return cleanup;
  }, [loadCatalogAgents]);

  u_useEffect(() => {
    const onRefresh = () => { loadCatalogAgents(); };
    window.addEventListener('af:catalog:refresh', onRefresh);
    return () => window.removeEventListener('af:catalog:refresh', onRefresh);
  }, [loadCatalogAgents]);

  // Completa OAuth de Google Calendar aunque Google redirija a "/" (landing).
  u_useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const oauthError = params.get('error');
    if (oauthError) {
      const clean = window.location.origin + window.location.pathname;
      window.history.replaceState({}, document.title, clean);
      window.afNotify && window.afNotify('Google Workspace rechazó la conexión. Usa una cuenta agentforge-ia.pro.', 'error');
      if (currentUser) setRoute('scheduling');
      return;
    }
    const code = params.get('code');
    const state = params.get('state');
    if (!code || !state) return;
    const token = sessionStorage.getItem('af_token') || '';
    if (!token) return;

    (async () => {
      try {
        const res = await fetch(
          `/api/v1/scheduling/google/exchange?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`,
          {
            method: 'POST',
            headers: { Authorization: 'Bearer ' + token },
          }
        );
        if (!res.ok) return;
        window.afNotify && window.afNotify('Google Workspace conectado');
        const clean = window.location.origin + window.location.pathname;
        window.history.replaceState({}, document.title, clean);
        if (currentUser) setRoute('scheduling');
      } catch (_) {
        window.afNotify && window.afNotify('No se pudo completar Google Workspace. Reintenta con una cuenta autorizada.', 'error');
        const clean = window.location.origin + window.location.pathname;
        window.history.replaceState({}, document.title, clean);
        if (currentUser) setRoute('scheduling');
      }
    })();
  }, [currentUser]);

  u_useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    if (!params.get('purchase_success') && !params.get('purchase_cancelled')) return;
    const clean = window.location.origin + window.location.pathname;
    window.history.replaceState({}, document.title, clean);
    if (params.get('purchase_cancelled')) {
      window.afNotify && window.afNotify('Compra cancelada');
      return;
    }
    window.afNotify && window.afNotify('¡Pago completado! Tu agente ya está activo.');
    if (currentUser) navigate('library');
  }, []);

  /**
   * Evalúa permisos RBAC para secciones de usuario.
   * Para "Pruebas" e "Integraciones" exigimos sección explícita.
   * @param {string} r
   * @param {any} user
   * @returns {boolean}
   */
  const canAccessUserSection = (r, user) => {
    if (!USER_RBAC_ROUTES.has(r)) return true;
    if (!user) return false;
    if (user.is_admin) return true;
    return !!(user.sections && user.sections[r]);
  };

  // Comprueba si un usuario puede acceder a una sección admin (is_admin O tiene sections[r])
  const canAccess = (r, user) => {
    if (!canAccessUserSection(r, user)) return false;
    if (!ADMIN_ROUTES.has(r)) return true;
    if (!user) return false;
    if (STRICT_ADMIN_ROUTES.has(r)) return !!user.is_admin;
    if (user.is_admin) return true;
    return !!(user.sections && user.sections[r]);
  };

  u_useEffect(() => {
    if (!PROTECTED_ROUTES.has(route)) return;
    if (canAccess(route, currentUser)) return;
    setRoute(currentUser ? 'marketplace' : 'landing');
  }, [route, currentUser]);

  const goBack = () => {
    setRouteHistory(h => {
      if (h.length === 0) return h;
      const prev = [...h];
      const target = prev.pop();
      setRoute(target);
      return prev;
    });
  };

  // Guard: rutas protegidas abren modal de login; rutas admin comprueban roles
  const navigate = (r) => {
    if (!currentUser && !PUBLIC_ROUTES.has(r)) {
      if (sessionStorage.getItem('af_token') && !sessionStorage.getItem('af_user')) setSessionExpired(true);
      setPendingRoute(r);
      setAuthOpen(true);
      return;
    }
    if (!canAccess(r, currentUser)) {
      return; // sin permiso — silencioso
    }
    setRouteHistory(h => [...h, route]);
    setRoute(r);
  };

  const handleLoginSuccess = (u) => {
    setSessionExpired(false);
    if (u && u.id) sessionStorage.setItem('af_customer_id', String(u.id));
    setCurrentUser(u);
    setAuthOpen(false);
    if (pendingRoute) {
      const dest = canAccess(pendingRoute, u) ? pendingRoute : 'marketplace';
      setRoute(dest);
      setPendingRoute(null);
      return;
    }
    if (PUBLIC_ROUTES.has(route)) setRoute('home');
  };

  const handleLogout = () => {
    setSessionExpired(false);
    sessionStorage.removeItem('af_token');
    sessionStorage.removeItem('af_user');
    sessionStorage.removeItem('af_customer_id');
    setCurrentUser(null);
    setRoute('landing');
  };

  const baseData = window.AF_DATA;
  const sourceAgents = catalogAgents.length > 0 ? catalogAgents : (Array.isArray(baseData.agents) ? baseData.agents : []);
  const unique = new Map();
  [...customAgents, ...sourceAgents].forEach((a) => {
    const isCustomAgent =
      a?._custom ||
      String(a?.author || '').toLowerCase() === 'cliente' ||
      /personalizado|privado/i.test(String(a?.price || ''));
    const key = isCustomAgent
      ? `custom:${customAgentStableId(a) || [
          String(a.name || '').trim().toLowerCase(),
          String(a.tagline || '').trim().toLowerCase(),
          String(a.cat || a.category || '').trim().toLowerCase(),
        ].join(':')}`
      : String(a.id || a.slug || '').trim();
    if (!key) return;
    if (!unique.has(key)) unique.set(key, a);
  });
  const data = { ...baseData, agents: Array.from(unique.values()) };

  const createAgent = (payload) => {
    const slugBase = (payload.name || 'nuevo-agente').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
    const id = `${slugBase || 'agente'}-${Date.now().toString().slice(-6)}`;
    const cat = payload.cat || 'ops';
    const catObj = (baseData.categories || []).find((c) => c.id === cat);
    const agent = {
      id,
      name: payload.name || 'Nuevo agente',
      cat,
      tagline: payload.tagline || 'Agente creado desde Editor',
      author: 'Mi espacio',
      verified: false,
      runs: 0,
      rating: 0,
      reviews: 0,
      price: 'Privado',
      tools: payload.tools || [],
      tags: ['custom'],
      color: (catObj && catObj.accent) || '#ff5b1f',
      _custom: true,
      contextFiles: Array.isArray(payload.contextFiles) ? payload.contextFiles : [],
      contextUrls: Array.isArray(payload.contextUrls) ? payload.contextUrls : [],
      contextNotes: payload.contextNotes || '',
      deployment: payload.deployment || 'borrador',
      lastRun: 'nunca',
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    setCustomAgents((prev) => [agent, ...prev]);
    try { localStorage.setItem('af_last_created_agent', JSON.stringify(agent)); } catch {}
    window.afNotify && window.afNotify(`Agente creado: ${agent.name}`);
    return agent;
  };
  /**
   * Elimina un agente local/custom del estado frontend.
   * @param {string} agentId
   * @returns {void}
   */
  const removeCustomAgent = (agentId) => {
    if (!agentId) return;
    setCustomAgents((prev) => prev.filter((a) => a.id !== agentId));
  };
  const openAgent = (id) => setOpenId(id);

  const handleEditAgent = async (row) => {
    let systemPrompt = '';
    let flowNodes = [];
    let flowEdges = [];
    if (row.agentDbId) {
      try {
        const token = sessionStorage.getItem('af_token') || '';
        const res = await fetch(`/api/v1/agents/${row.agentDbId}/detail`, {
          headers: { Authorization: 'Bearer ' + token },
        });
        if (res.ok) {
          const detail = await res.json();
          systemPrompt = detail.system_prompt || '';
          flowNodes = Array.isArray(detail.flow_nodes) ? detail.flow_nodes : [];
          flowEdges = Array.isArray(detail.flow_edges) ? detail.flow_edges : [];
        }
      } catch (_) {}
    }
    if (isCookingAgentRow(row)) {
      systemPrompt = COOKING_RECIPE_PROMPT;
    }
    try {
      localStorage.setItem('af_builder_seed_v1', JSON.stringify({
        agentName: row.agent?.name || 'Agente',
        agentSlug: row.id,
        agentId: row.agentDbId || '',
        agentTagline: row.agent?.tagline || '',
        agentColor: row.agent?.color || '#ff5b1f',
        systemPrompt,
        flowNodes,
        flowEdges,
        blank: false,
        isEditMode: true,
        fromLibrary: true,
      }));
    } catch (_) {}
    setActiveEditAgent(row);
    navigate('builder');
  };

  const handleDeployAgent = (row) => {
    try {
      sessionStorage.setItem('af_deploy_agent', JSON.stringify({
        agentId: row.agentDbId || '',
        agentSlug: row.id,
        agentName: row.agent?.name || 'Agente',
      }));
    } catch (_) {}
    setActiveDeployAgent(row);
    navigate('deployment');
  };
  const openAgentObj = openId ? data.agents.find(a => a.id === openId) : null;
  const openCat = openAgentObj ? data.categories.find(c => c.id === openAgentObj.cat) : null;

  const _parsePriceCents = (priceStr) => {
    if (!priceStr || /gratis/i.test(priceStr)) return 0;
    const m = String(priceStr).match(/(\d+)/);
    return m ? parseInt(m[1], 10) * 100 : 0;
  };

  /**
   * Resuelve el UUID real de backend a partir del slug frontend.
   * Si el agente no existe en la BD, lo crea usando los datos del catálogo.
   */
  const resolveBackendAgentId = async (agent) => {
    const slug = agent.id;
    const token = sessionStorage.getItem('af_token') || '';
    const headers = { 'Content-Type': 'application/json', Authorization: 'Bearer ' + token };

    // Primero intentar encontrarlo por slug en el listado público
    try {
      const listRes = await fetch('/api/v1/agents');
      if (listRes.ok) {
        const agents = await listRes.json();
        const match = Array.isArray(agents) ? agents.find((a) => a.slug === slug) : null;
        if (match) return match.id;
      }
    } catch (_) {}

    // Fallback: find-or-create desde datos del catálogo frontend
    const catalogRes = await fetch('/api/v1/agents/from-catalog', {
      method: 'POST',
      headers,
      body: JSON.stringify({
        slug,
        name: agent.name || slug,
        tagline: agent.tagline || '',
        author: agent.author || 'Forge Labs',
        price_display: agent.price || 'Gratis',
        price_cents: _parsePriceCents(agent.price),
        category: agent.cat || '',
        color: agent.color || '#ff5b1f',
        tools: Array.isArray(agent.tools) ? agent.tools : [],
        tags: Array.isArray(agent.tags) ? agent.tags : [],
        runs: agent.runs || 0,
        rating: agent.rating || 5.0,
        reviews: agent.reviews || 0,
      }),
    });
    if (!catalogRes.ok) return null;
    const catalogData = await catalogRes.json();
    return catalogData.id || null;
  };

  const _callBillingEndpoint = async (agent, endpoint) => {
    const backendAgentId = await resolveBackendAgentId(agent);
    if (!backendAgentId) {
      window.afNotify && window.afNotify('No se pudo registrar el agente. Inténtalo de nuevo.');
      return;
    }
    const token = sessionStorage.getItem('af_token') || '';
    const res = await fetch(endpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + token },
      body: JSON.stringify({ agent_id: backendAgentId }),
    });
    if (res.status === 409) {
      window.afNotify && window.afNotify('Ese agente ya está activo en tu cuenta');
      setOpenId(null);
      navigate('library');
      return;
    }
    if (!res.ok) {
      window.afNotify && window.afNotify('No se pudo procesar la compra');
      return;
    }
    const data = await res.json();
    setOpenId(null);
    if (data.is_free) {
      window.afNotify && window.afNotify(`${agent.name} activado correctamente`);
      navigate('library');
    } else {
      window.location.href = data.checkout_url;
    }
  };

  const startCheckout = async (agent) => {
    if (!currentUser) {
      setPendingRoute('library');
      setAuthOpen(true);
      return;
    }
    try {
      await _callBillingEndpoint(agent, '/api/v1/billing/checkout-session');
    } catch (_) {
      window.afNotify && window.afNotify('Error de red durante la compra');
    }
  };

  const simulateCheckout = async (agent) => {
    if (!currentUser) {
      setPendingRoute('library');
      setAuthOpen(true);
      return;
    }
    try {
      await _callBillingEndpoint(agent, '/api/v1/billing/simulate-payment');
    } catch (_) {
      window.afNotify && window.afNotify('Error de red durante la simulación');
    }
  };

  const screen = (() => {
    switch (route) {
      case 'home': return <Home data={data} setRoute={navigate} openAgent={openAgent} />;
      case 'marketplace': return <Marketplace data={data} openAgent={openAgent} activeCategory={activeCategory} setActiveCategory={setActiveCategory} />;
      case 'library': return <Library data={data} openAgent={openAgent} setRoute={navigate} onDeleteCustomAgent={removeCustomAgent} onRunAgent={(agentId) => { setActivePlayAgentId(agentId); navigate('playground'); }} onEditAgent={handleEditAgent} onDeployAgent={handleDeployAgent} />;
      case 'crm': return <CRM />;
      case 'scheduling': return <Scheduling />;
      case 'deployment': return <Deployment preselectedAgent={activeDeployAgent} />;
      case 'builder': return <Builder data={data} editAgent={activeEditAgent} setRoute={navigate} />;
      case 'playground': return <Playground data={data} activeAgentId={activePlayAgentId} setRoute={navigate} />;
      case 'onboarding': return <Onboarding data={data} setRoute={navigate} onCreateAgent={createAgent} />;
      case 'integrations': return <Integrations />;
      case 'plans': return <ScreenBilling initialTab="planes" />;
      case 'billing': return <ScreenBilling initialTab="facturacion" />;
      case 'settings': return <Settings />;
      case 'runs': return <RunsLogs setRoute={navigate} />;
      case 'analytics': return <Analytics data={data} setRoute={navigate} />;
      case 'admin': return (currentUser && (currentUser.is_admin || (currentUser.sections && Object.keys(currentUser.sections).length > 0))) ? <AdminPanel /> : <Marketplace data={data} openAgent={openAgent} activeCategory={activeCategory} setActiveCategory={setActiveCategory} />;
      case 'ops_finance': return <InternalOpsArea areaId="ops_finance" />;
      case 'ops_sales': return <InternalOpsArea areaId="ops_sales" />;
      case 'ops_marketing': return <InternalOpsArea areaId="ops_marketing" />;
      case 'ops_operations': return <InternalOpsArea areaId="ops_operations" />;
      case 'ops_hr': return <InternalOpsArea areaId="ops_hr" />;
      case 'ops_support': return <InternalOpsArea areaId="ops_support" />;
      case 'landing': return (
        <LandingHero
          onLogin={() => { setPendingRoute('home'); setAuthOpen(true); }}
          onDocs={() => navigate('public-docs')}
        />
      );
      case 'public-docs': return <PublicDocs data={data} onBack={() => navigate('landing')} />;
      default: return <Marketplace data={data} openAgent={openAgent} activeCategory={activeCategory} setActiveCategory={setActiveCategory} />;
    }
  })();

  const toggleTheme = () => setTweak('theme', tweaks.theme === 'dark' ? 'light' : 'dark');
  const isLanding = route === 'landing';
  const isPublicDocs = route === 'public-docs';
  const isPublicView = isLanding || isPublicDocs;
  const quickActions = [
    ['home', 'Inicio'],
    ['marketplace', 'Marketplace'],
    ['library', 'Mis agentes'],
    ['playground', 'Playground'],
    ['builder', 'Builder'],
    ['deployment', 'Deployment'],
    ['settings', 'Ajustes'],
  ];

  return (
    <div className={isPublicView ? 'landing-app-shell' : 'app'}>
      {!isPublicView && (
        <>
          <Topbar theme={tweaks.theme} onTheme={toggleTheme} onCmdK={() => setQuickOpen(true)} currentUser={currentUser} onLoginClick={() => setAuthOpen(true)} onLogout={handleLogout} goBack={goBack} canGoBack={routeHistory.length > 0} sessionExpired={sessionExpired} />
          <Sidebar route={route} setRoute={navigate} currentUser={currentUser} />
        </>
      )}
      <main className={isPublicView ? 'landing-main-shell' : 'main'}>
        <div className="view">{screen}</div>
      </main>
      {openAgentObj && <AgentDrawer a={openAgentObj} cat={openCat} onClose={() => setOpenId(null)} onInstall={startCheckout} onSimulate={simulateCheckout} onTry={() => { setActivePlayAgentId(openAgentObj.id); setOpenId(null); navigate('playground'); }} />}

      <AuthModal
        isOpen={authOpen}
        onClose={() => { setAuthOpen(false); setPendingRoute(null); }}
        onSuccess={handleLoginSuccess}
      />

      <TweaksPanel title="Tweaks">
        <TweakSection label="Apariencia">
          <TweakRadio label="Tema" value={tweaks.theme} onChange={v => setTweak('theme', v)} options={[{ value:'dark', label:'Oscuro' },{ value:'light', label:'Claro' }]} />
          <TweakColor label="Acento" value={tweaks.accent} onChange={v => setTweak('accent', v)} />
          <TweakSelect label="Fuente mono" value={tweaks.monoFont} onChange={v => setTweak('monoFont', v)}
            options={[{value:'JetBrains Mono',label:'JetBrains Mono'},{value:'IBM Plex Mono',label:'IBM Plex Mono'},{value:'Geist Mono',label:'Geist Mono'},{value:'Fira Code',label:'Fira Code'}]} />
        </TweakSection>
        <TweakSection label="Navegación">
          <TweakRadio label="Pantalla" value={route} onChange={navigate}
            options={[{value:'home',label:'Inicio'},{value:'marketplace',label:'Market'},{value:'library',label:'Mis ag.'},{value:'builder',label:'Editor'}]} />
          <TweakRadio label="Más" value={route} onChange={navigate}
            options={[{value:'playground',label:'Pruebas'},{value:'onboarding',label:'Crear'},{value:'integrations',label:'Integ.'},{value:'plans',label:'Planes'},{value:'billing',label:'Fact.'}]} />
        </TweakSection>
      </TweaksPanel>

      {!isPublicView && quickOpen && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 980, background: 'rgba(0,0,0,0.45)', backdropFilter: 'blur(3px)' }}
          onClick={(e) => { if (e.target === e.currentTarget) setQuickOpen(false); }}>
          <div style={{ width: 460, maxWidth: '92vw', margin: '14vh auto 0', background: 'var(--bg-1)', border: '1px solid var(--line-2)', borderRadius: 12, boxShadow: 'var(--shadow-pop)', padding: 10 }}>
            <div className="mono" style={{ fontSize: 11, color: 'var(--ink-4)', letterSpacing: '0.08em', textTransform: 'uppercase', padding: '4px 8px 8px' }}>Comandos rápidos</div>
            {quickActions.map(([id, label]) => (
              <button key={id}
                className="btn btn-ghost"
                style={{ width: '100%', justifyContent: 'space-between', height: 36, borderRadius: 8 }}
                onClick={() => { navigate(id); setQuickOpen(false); }}>
                <span>{label}</span>
                <span className="mono" style={{ color: 'var(--ink-4)', fontSize: 11 }}>/{id}</span>
              </button>
            ))}
          </div>
        </div>
      )}

      {notice && (
        <div style={{ position: 'fixed', right: 16, bottom: 14, zIndex: 990, background: 'var(--bg-2)', border: '1px solid var(--line-2)', borderRadius: 10, boxShadow: 'var(--shadow-2)', padding: '10px 12px', minWidth: 220 }}>
          <div style={{ fontSize: 12.5 }}>{notice.message}</div>
        </div>
      )}

      <SupportChat currentUser={currentUser} overlayOpen={!!openAgentObj} />
      {bookingOpen && <BookingModal prefill={bookingPrefill} currentUser={currentUser} onClose={() => setBookingOpen(false)} />}
    </div>
  );
}

function LandingHero({ onLogin, onDocs }) {
  const [lang, setLang] = u_useState('es');
  u_useEffect(() => {
    try {
      localStorage.setItem('af_ui_lang', lang);
      window.afUiLang = lang;
      window.dispatchEvent(new CustomEvent('af:lang', { detail: lang }));
    } catch (_) {}
  }, [lang]);
  const t = lang === 'es'
    ? {
      badge: 'NUEVA GENERACIÓN DE AGENTES IA',
      titleA: 'Automatiza operaciones con',
      titleB: 'agentes que sí trabajan',
      subtitle: 'Diseña, despliega y opera agentes reales para soporte, ventas y backoffice en una sola plataforma.',
      cta1: 'Entrar al panel',
      cta2: 'Iniciar sesión',
      contact: 'Contacto',
      k1: 'Flujos activos',
      k2: 'Ejecuciones hoy',
      k3: 'Ahorro estimado',
      nav1: 'Producto',
      nav4: 'Docs',
    }
    : {
      badge: 'NEXT-GEN AI AGENT PLATFORM',
      titleA: 'Automate operations with',
      titleB: 'agents that actually work',
      subtitle: 'Design, deploy and operate real agents for support, sales and back-office in one platform.',
      cta1: 'Enter dashboard',
      cta2: 'Sign in',
      contact: 'Contact',
      k1: 'Active flows',
      k2: 'Executions today',
      k3: 'Estimated savings',
      nav1: 'Product',
      nav4: 'Docs',
    };

  return (
    <div className="landing-root" id="landing-top">
      <div className="landing-bg" aria-hidden="true">
        <div className="aurora a1" />
        <div className="aurora a2" />
        <div className="aurora a3" />
        <div className="tech-cloud" />
        <div className="tech-grid" />
        <div className="tech-scan" />
        <div className="tech-nodes" />
        <div className="tech-links" />
        <div className="tech-stream stream-a" />
        <div className="tech-stream stream-b" />
        <div className="tech-stream stream-c" />
        <div className="tech-particles p1" />
        <div className="tech-particles p2" />
      </div>

      <header className="landing-nav">
        <div className="landing-brand">
          <img src="agentforge-logo.png?v=4" alt="AgentForge" />
        </div>
        <nav>
          <button onClick={onDocs}>{t.nav4}</button>
          <button onClick={() => document.getElementById('landing-contacto')?.scrollIntoView({ behavior: 'smooth', block: 'start' })}>{t.contact}</button>
        </nav>
        <div className="landing-lang">
          <button className="landing-login-btn" onClick={onLogin}>{t.cta2}</button>
          <button className={lang === 'es' ? 'active' : ''} onClick={() => setLang('es')}>ES</button>
          <button className={lang === 'en' ? 'active' : ''} onClick={() => setLang('en')}>EN</button>
        </div>
      </header>

      <main className="landing-main">
        <span className="landing-badge mono">{t.badge}</span>
        <h1>
          {t.titleA}<br />
          <span>{t.titleB}</span>
        </h1>
        <p>{t.subtitle}</p>
        <div className="landing-ctas">
          <button className="btn btn-primary" onClick={onLogin}>{t.cta2}</button>
          <button className="btn" onClick={onDocs}>Docs</button>
        </div>

        <section className="landing-kpis">
          <article><b>42</b><span>{t.k1}</span></article>
          <article><b>18.4k</b><span>{t.k2}</span></article>
          <article><b>61%</b><span>{t.k3}</span></article>
        </section>

        <section id="landing-producto" className="landing-section-block">
          <h2>{lang === 'es' ? 'Producto' : 'Product'}</h2>
          <p>{lang === 'es'
            ? 'AgentForge te permite crear, probar y desplegar agentes IA por verticales de negocio, con control de calidad, trazas y gobernanza.'
            : 'AgentForge lets you create, test and deploy AI agents by business vertical, with quality control, traces and governance.'}
          </p>
          <div className="landing-grid-3">
            <article><h3>Builder visual</h3><p>{lang === 'es' ? 'Editor de flujos con bloques, pruebas y versiones.' : 'Flow editor with blocks, tests and versions.'}</p></article>
            <article><h3>Marketplace</h3><p>{lang === 'es' ? 'Catálogo por categorías, compra y despliegue guiado.' : 'Category catalog, purchase and guided deployment.'}</p></article>
            <article><h3>Operación</h3><p>{lang === 'es' ? 'Ejecuciones, logs, analítica y control por roles.' : 'Runs, logs, analytics and role-based controls.'}</p></article>
          </div>
        </section>

        <section id="landing-casos" className="landing-section-block">
          <h2>{lang === 'es' ? 'Casos de uso' : 'Use cases'}</h2>
          <div className="landing-cases">
            <article><b>Atención al cliente</b><span>{lang === 'es' ? 'Resolución de tickets, dudas y reclamaciones 24/7.' : '24/7 ticket and support handling.'}</span></article>
            <article><b>Ventas y leads</b><span>{lang === 'es' ? 'Cualificación automática y priorización comercial.' : 'Automatic lead scoring and sales prioritization.'}</span></article>
            <article><b>Marketing</b><span>{lang === 'es' ? 'Contenido, campañas y optimización de conversión.' : 'Content, campaigns and conversion optimization.'}</span></article>
            <article><b>Operaciones</b><span>{lang === 'es' ? 'Automatización de tareas internas y flujos repetitivos.' : 'Internal process and repetitive workflow automation.'}</span></article>
          </div>
        </section>

        <section id="landing-precios" className="landing-section-block">
          <h2>{lang === 'es' ? 'Precios' : 'Pricing'}</h2>
          <div className="landing-pricing">
            <article><h3>Starter</h3><b>97€/mes</b><p>{lang === 'es' ? 'Por agente. Ideal para comenzar a automatizar.' : 'Per agent. Ideal to start automating.'}</p></article>
            <article className="featured"><h3>Pro</h3><b>297€/mes</b><p>{lang === 'es' ? 'Por agente con mantenimiento mensual incluido.' : 'Per agent with monthly maintenance included.'}</p></article>
            <article><h3>Enterprise</h3><b>A medida</b><p>{lang === 'es' ? 'Desde 497€/mes por agente. SLA dedicado y soporte 24/7.' : 'From 497€/month per agent. Dedicated SLA and 24/7 support.'}</p></article>
          </div>
        </section>

        <section id="landing-contacto" className="landing-section-block landing-contact-block">
          <h2>{lang === 'es' ? 'Contacto' : 'Contact'}</h2>
          <p>{lang === 'es'
            ? 'Si quieres resolver dudas antes de entrar, escribirnos directamente o pedir una reunión, usa el formulario de contacto o este correo.'
            : 'If you want to ask questions before signing in, write to us directly, or request a meeting, use the contact form or this email.'}
          </p>
          <div className="landing-contact-card">
            <span className="mono">agentforge@agentforge-ia.pro</span>
          </div>
        </section>

      </main>
    </div>
  );
}

function PublicDocs({ data, onBack }) {
  const categories = Array.isArray(data?.categories) ? data.categories : [];
  const agents = Array.isArray(data?.agents) ? data.agents : [];
  const grouped = categories.map((c) => ({
    ...c,
    agents: agents.filter((a) => a.cat === c.id).slice(0, 8),
  })).filter((c) => c.agents.length > 0);

  return (
    <div className="docs-public">
      <div className="docs-public-bg" aria-hidden="true" />
      <button className="btn btn-primary docs-back-floating" onClick={onBack}>
        ← Volver a landing
      </button>

      <div className="section docs-wrap">
        <div className="docs-hero">
          <h1>Documentación de agentes</h1>
          <p>
            Catálogo funcional por categorías con resumen de capacidades y utilidad principal de cada agente.
          </p>
        </div>

        <div className="docs-summary">
          <article>
            <b>{grouped.length}</b>
            <span>Categorías activas</span>
          </article>
          <article>
            <b>{grouped.reduce((acc, g) => acc + g.agents.length, 0)}</b>
            <span>Agentes documentados</span>
          </article>
          <article>
            <b>v2.4</b>
            <span>Versión de catálogo</span>
          </article>
        </div>

        <div className="docs-category-nav">
          {grouped.map((cat) => (
            <a key={cat.id} href={`#docs-cat-${cat.id}`} className="chip">
              {cat.name}
              <span className="count">{cat.agents.length}</span>
            </a>
          ))}
        </div>

        <div className="docs-groups">
          {grouped.map((cat) => (
            <section id={`docs-cat-${cat.id}`} key={cat.id} className="card card-pad docs-group">
              <div className="docs-group-head">
                <h2>{cat.name}</h2>
                <span className="tag info">{cat.agents.length} agentes</span>
              </div>
              <div className="landing-grid-3">
                {cat.agents.map((a) => (
                  <article key={a.id} className="card card-pad docs-agent-card">
                    <h3>{a.name}</h3>
                    <p>{a.tagline || 'Agente especializado para esta categoría.'}</p>
                    <div className="docs-agent-meta">
                      <span className="tag">{a.price || 'Plan variable'}</span>
                      <span className="tag">{a.tools?.length || 0} herramientas</span>
                    </div>
                  </article>
                ))}
              </div>
            </section>
          ))}
        </div>
      </div>
    </div>
  );
}

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




