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

const ACCENT = "#477327";
const ACCENT_DEEP = "#355720";

// Transform API order -> frontend order
function transformOrder(apiOrder) {
  const rawPayload = typeof apiOrder.raw_payload === "string"
    ? JSON.parse(apiOrder.raw_payload || "{}")
    : (apiOrder.raw_payload || {});

  const status = STATUS_MAP[apiOrder.status] || "new";
  const createdAt = new Date(apiOrder.created_at);
  const elapsedMin = Math.max(0, Math.round((Date.now() - createdAt.getTime()) / 60000));

  const items = (rawPayload.items || []).map((it) => ({
    qty: it.quantity || 1,
    name: it.name || it.posCode || "Producto",
    price: it.unitPrice || 0,
    note: it.notes || undefined,
    opts: (it.modifiers || []).map((m) => m.name).join(" - ") || undefined,
  }));

  const customer = rawPayload.customer || {};
  const delivery = rawPayload.delivery || {};
  const addr = customer.deliveryAddress || {};

  // Tiempo estimado de listo (Uber/plataforma indica para cuándo debe estar)
  let estimatedReady = "";
  if (delivery.estimatedPickupTime) {
    const ep = new Date(delivery.estimatedPickupTime);
    if (!isNaN(ep.getTime())) {
      estimatedReady = ep.toLocaleTimeString("es-MX", { hour: "2-digit", minute: "2-digit" });
    }
  }

  return {
    id: apiOrder.id,
    externalId: apiOrder.external_id,
    status,
    platform: apiOrder.platform,
    type: delivery.type === "pickup" ? "Pickup" : "Delivery",
    customer: customer.name || "Cliente",
    phone: customer.phone || "",
    address: addr.street || (delivery.type === "pickup" ? "Recoge en tienda" : ""),
    addressRef: addr.references || "",
    payment: rawPayload.payment?.method || "other",
    posDoc: apiOrder.pos_cm_num_doc || "",
    receivedAt: createdAt.toLocaleDateString("es-MX", {
      day: "numeric", month: "short", year: "numeric",
      hour: "2-digit", minute: "2-digit",
    }),
    estimatedReady,
    // Tiempo de preparación sugerido por la plataforma (min) y si se puede ajustar
    suggestedPrepMinutes: delivery.suggestedPrepMinutes || null,
    canAdjustPrepTime: delivery.canAdjustPrepTime !== false,
    orderNote: rawPayload.notes || "",
    elapsedMin,
    items,
    subtotal: rawPayload.subtotal || 0,
    tax: rawPayload.tax || 0,
    deliveryFee: rawPayload.deliveryFee || 0,
    tip: rawPayload.tip || 0,
    discount: rawPayload.discount || 0,
    total: rawPayload.total || 0,
    areaStatus: apiOrder.areaStatus || [],
  };
}

function useHash() {
  const [hash, setHash] = useState(window.location.hash || "#/");
  useEffect(() => {
    const handler = () => setHash(window.location.hash || "#/");
    window.addEventListener("hashchange", handler);
    return () => window.removeEventListener("hashchange", handler);
  }, []);
  return hash;
}

function App() {
  const [user, setUser] = useState(null);
  const [authChecked, setAuthChecked] = useState(false);
  const hash = useHash();

  // Verificar token al montar: si hay token guardado, validarlo con /auth/me
  useEffect(() => {
    const savedUser = getUser();
    const token = getToken();
    if (!savedUser || !token) {
      setAuthChecked(true);
      return;
    }
    // Verificar que el token siga siendo válido
    fetch(`${API_BASE}/auth/me`, {
      headers: { Authorization: `Bearer ${token}` },
    })
      .then((res) => {
        if (res.ok) {
          setUser(savedUser);
        } else {
          // Token inválido → limpiar
          clearAuth();
        }
        setAuthChecked(true);
      })
      .catch(() => {
        // Error de red → intentar con datos locales (offline-friendly)
        setUser(savedUser);
        setAuthChecked(true);
      });
  }, []);

  // Escuchar cuando fetchWithAuth detecta 401 y limpia auth
  useEffect(() => {
    const handler = () => { setUser(null); };
    window.addEventListener("auth:logout", handler);
    return () => window.removeEventListener("auth:logout", handler);
  }, []);

  // Mantener el contexto de audio "despierto": cualquier gesto del usuario lo reanuda,
  // y también al volver a la pestaña (los navegadores lo suspenden en segundo plano).
  useEffect(() => {
    const warmUp = () => { AudioAlert.warmUp(); };
    const events = ["click", "keydown", "touchstart", "pointerdown"];
    events.forEach((e) => window.addEventListener(e, warmUp));
    const onVis = () => { if (!document.hidden) AudioAlert.warmUp(); };
    document.addEventListener("visibilitychange", onVis);
    return () => {
      events.forEach((e) => window.removeEventListener(e, warmUp));
      document.removeEventListener("visibilitychange", onVis);
    };
  }, []);

  // Apply theme + accent
  const [theme, setTheme] = useState("light");
  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
    document.documentElement.style.setProperty("--accent", ACCENT);
    document.documentElement.style.setProperty("--accent-deep", ACCENT_DEEP);
  }, [theme]);

  // Toast
  const [toast, setToast] = useState(null);
  const toastTimer = useRef(null);
  const showToast = useCallback((msg) => {
    setToast(msg);
    if (toastTimer.current) clearTimeout(toastTimer.current);
    toastTimer.current = setTimeout(() => setToast(null), 2200);
  }, []);

  const handleLogin = useCallback((u) => {
    setUser(u);
    const defaultView = ROLE_DEFAULT_VIEW[u.role] || "orders";
    window.location.hash = `#/${defaultView}`;
  }, []);

  const handleLogout = useCallback(() => {
    clearAuth();
    setUser(null);
    window.location.hash = "#/login";
  }, []);

  // ===== Alerta de pedidos nuevos (badge + sonido), disponible en CUALQUIER vista =====
  const [newOrdersCount, setNewOrdersCount] = useState(0);
  const prevNewCount = useRef(0);

  const refreshNewOrdersCount = useCallback(async () => {
    if (!user || user.role === "kds") { setNewOrdersCount(0); return; }
    try {
      const res = await fetchWithAuth(`${API_BASE}/orders?limit=50`);
      if (!res.ok) return;
      const data = await res.json();
      const count = (data.orders || [])
        .map(transformOrder)
        .filter((o) => o.status === "new").length;
      setNewOrdersCount(count);
    } catch (e) { /* silencioso */ }
  }, [user]);

  // Mantener el conteo fresco: SSE (instantáneo) + polling de respaldo cada 10s
  useSSE(useCallback((eventType) => {
    if (eventType.startsWith("order:") || eventType === "stats:refresh") refreshNewOrdersCount();
  }, [refreshNewOrdersCount]));

  useEffect(() => {
    if (!user) return;
    refreshNewOrdersCount();
    const t = setInterval(refreshNewOrdersCount, 10000);
    return () => clearInterval(t);
  }, [user, refreshNewOrdersCount]);

  // Sonido: alerta inmediata en cuanto APARECE un pedido nuevo (el conteo sube)
  useEffect(() => {
    if (newOrdersCount > prevNewCount.current) AudioAlert.newOrder();
    prevNewCount.current = newOrdersCount;
  }, [newOrdersCount]);

  // Sonido: repetir cada 10s mientras haya pedidos nuevos SIN aceptar
  const hasNewOrders = newOrdersCount > 0;
  useEffect(() => {
    if (!hasNewOrders) return;
    const t = setInterval(() => { AudioAlert.newOrder(); }, 10000);
    return () => clearInterval(t);
  }, [hasNewOrders]);

  // Route to login if no user (después de verificar auth)
  useEffect(() => {
    if (authChecked && !user && hash !== "#/login") {
      window.location.hash = "#/login";
    }
  }, [user, hash, authChecked]);

  // Redirect root to default view
  useEffect(() => {
    if (user && (hash === "#/" || hash === "#/login" || hash === "")) {
      const defaultView = ROLE_DEFAULT_VIEW[user.role] || "orders";
      window.location.hash = `#/${defaultView}`;
    }
  }, [user, hash]);

  // Mientras se verifica el token, mostrar pantalla de carga mínima (evita parpadeo)
  if (!authChecked) {
    return (
      <div style={{
        display: "flex", alignItems: "center", justifyContent: "center",
        height: "100vh", background: "var(--bg, #FAFAF7)",
      }}>
        <div style={{ textAlign: "center", color: "var(--text-3, #8C8880)" }}>
          <div style={{ fontSize: 13, fontWeight: 500 }}>Conectando...</div>
        </div>
      </div>
    );
  }

  if (!user || hash === "#/login") {
    return (
      <>
        <LoginPage onLogin={handleLogin} />
        <Toast message={toast} />
      </>
    );
  }

  const route = hash.replace("#/", "");

  // KDS role: siempre muestra KDS, no importa la ruta
  if (user.role === "kds") {
    return (
      <div className="app">
        <Sidebar active="kds" onChange={(v) => { window.location.hash = `#/${v}`; }} user={user} onLogout={handleLogout} badges={{ orders: newOrdersCount }} />
        <div className="main">
          <KDSView user={user} showToast={showToast} />
        </div>
        <Toast message={toast} />
      </div>
    );
  }

  if (route === "kds") {
    return (
      <div className="app">
        <Sidebar active="kds" onChange={(v) => { window.location.hash = `#/${v}`; }} user={user} onLogout={handleLogout} badges={{ orders: newOrdersCount }} />
        <div className="main">
          <KDSView user={user} showToast={showToast} />
        </div>
        <Toast message={toast} />
      </div>
    );
  }

  if (route === "menu" && (user.role === "admin" || user.role === "gerente")) {
    return (
      <div className="app">
        <Sidebar active="menu" onChange={(v) => { window.location.hash = `#/${v}`; }} user={user} onLogout={handleLogout} badges={{ orders: newOrdersCount }} />
        <div className="main">
          <MenuCatalogView user={user} showToast={showToast} />
        </div>
        <Toast message={toast} />
      </div>
    );
  }

  if (route === "reports" && (user.role === "admin" || user.role === "gerente")) {
    return (
      <div className="app">
        <Sidebar active="reports" onChange={(v) => { window.location.hash = `#/${v}`; }} user={user} onLogout={handleLogout} badges={{ orders: newOrdersCount }} />
        <div className="main">
          <ReportsView user={user} showToast={showToast} />
        </div>
        <Toast message={toast} />
      </div>
    );
  }

  if (route === "history") {
    return (
      <div className="app">
        <Sidebar active="history" onChange={(v) => { window.location.hash = `#/${v}`; }} user={user} onLogout={handleLogout} badges={{ orders: newOrdersCount }} />
        <div className="main">
          <OrderHistoryView user={user} showToast={showToast} />
        </div>
        <Toast message={toast} />
      </div>
    );
  }

  if (route === "storeconfig" && (user.role === "admin" || user.role === "gerente")) {
    return (
      <div className="app">
        <Sidebar active="storeconfig" onChange={(v) => { window.location.hash = `#/${v}`; }} user={user} onLogout={handleLogout} badges={{ orders: newOrdersCount }} />
        <div className="main">
          <StoreConfigView user={user} showToast={showToast} />
        </div>
        <Toast message={toast} />
      </div>
    );
  }

  if (route === "admin" && user.role === "admin") {
    return (
      <div className="app">
        <Sidebar active="admin" onChange={(v) => { window.location.hash = `#/${v}`; }} user={user} onLogout={handleLogout} badges={{ orders: newOrdersCount }} />
        <div className="main">
          <AdminView showToast={showToast} />
        </div>
        <Toast message={toast} />
      </div>
    );
  }

  // Default: Orders dashboard
  return (
    <div className="app">
      <Sidebar active="orders" onChange={(v) => { window.location.hash = `#/${v}`; }} user={user} onLogout={handleLogout} badges={{ orders: newOrdersCount }} />
      <div className="main">
        <OrdersDashboard user={user} theme={theme} setTheme={setTheme} showToast={showToast} />
      </div>
      <Toast message={toast} />
    </div>
  );
}

function OrdersDashboard({ user, theme, setTheme, showToast }) {
  const [orders, setOrders] = useState([]);
  const [platform, setPlatform] = useState("all");
  const [selectedId, setSelectedId] = useState(null);
  const [newIds, setNewIds] = useState(new Set());
  const [prepModal, setPrepModal] = useState(null); // { id, mode: "accept"|"adjust", suggested }
  const [layout, setLayout] = useState("kanban");
  const [loading, setLoading] = useState(true);
  const prevOrderIds = useRef(new Set());

  const fetchOrders = useCallback(async () => {
    try {
      const res = await fetchWithAuth(`${API_BASE}/orders?limit=50`);
      if (!res.ok) {
        console.warn("fetchOrders: response not ok", res.status);
        setLoading(false);
        return;
      }
      const data = await res.json();
      const apiOrders = data.orders || [];

      const transformed = apiOrders
        .map(transformOrder)
        .filter((o) => o.status !== undefined && STATUS_FLOW[o.status]);

      const currentIds = new Set(transformed.map((o) => o.id));
      const brandNew = transformed.filter((o) => !prevOrderIds.current.has(o.id) && o.status === "new");
      if (brandNew.length > 0 && prevOrderIds.current.size > 0) {
        showToast(`${brandNew.length} nuevo${brandNew.length > 1 ? "s" : ""} pedido${brandNew.length > 1 ? "s" : ""}`);
        // El sonido de pedido nuevo lo gestiona App (alerta inmediata + repetición cada 10s); aquí solo el destello visual.
        setNewIds(new Set(brandNew.map((o) => o.id)));
        setTimeout(() => setNewIds(new Set()), 600);
      }
      // Check for urgent orders (>15 min in new/progress)
      const urgentNow = transformed.filter((o) => (o.status === "new" || o.status === "progress") && o.elapsedMin >= 15);
      if (urgentNow.length > 0 && prevOrderIds.current.size > 0) {
        AudioAlert.urgent();
      }
      prevOrderIds.current = currentIds;
      setOrders(transformed);
      setLoading(false);
    } catch (err) {
      console.error("Error fetching orders:", err);
      setLoading(false);
    }
  }, [showToast]);

  // SSE: refresca inmediatamente al recibir eventos del servidor
  useSSE(useCallback((eventType, data) => {
    if (eventType.startsWith("order:") || eventType === "stats:refresh") {
      fetchOrders();
    }
  }, [fetchOrders]));

  // Fallback polling cada 30s (SSE es el canal primario)
  useEffect(() => {
    fetchOrders();
    const t = setInterval(fetchOrders, 30000);
    return () => clearInterval(t);
  }, [fetchOrders]);

  useEffect(() => {
    const t = setInterval(() => {
      setOrders((prev) => prev.map((o) => ({ ...o, elapsedMin: o.elapsedMin + 1 })));
    }, 30000);
    return () => clearInterval(t);
  }, []);

  const filtered = useMemo(() => {
    if (platform === "all") return orders;
    return orders.filter((o) => o.platform === platform);
  }, [orders, platform]);

  const grouped = useMemo(() => ({
    new:      filtered.filter((o) => o.status === "new"),
    progress: filtered.filter((o) => o.status === "progress"),
    ready:    filtered.filter((o) => o.status === "ready" || o.status === "dispatched"),
  }), [filtered]);

  const platformCounts = useMemo(() => {
    const c = { didi_food: 0, rappi: 0, uber_eats: 0 };
    orders.forEach((o) => { if (c[o.platform] !== undefined) c[o.platform]++; });
    return c;
  }, [orders]);

  // Aplica la transición de estado en la UI local
  const applyTransition = useCallback((id, flow) => {
    setOrders((prev) => prev.map((o) => {
      if (o.id !== id) return o;
      if (flow.next === "done") return null;
      return { ...o, status: flow.next };
    }).filter(Boolean));
    setNewIds((s) => new Set([...s, id]));
    setTimeout(() => setNewIds((s) => { const n = new Set(s); n.delete(id); return n; }), 600);
  }, []);

  // Hace el POST de avance de estado (con body opcional, p.ej. tiempo de preparación)
  const postAdvance = useCallback(async (id, flow, body) => {
    try {
      const res = await fetchWithAuth(`${API_BASE}/orders/${id}/${flow.apiAction}`, {
        method: "POST",
        ...(body ? { body: JSON.stringify(body) } : {}),
      });
      if (res.ok) {
        showToast(flow.action);
        applyTransition(id, flow);
      } else {
        const errData = await res.json().catch(() => ({}));
        showToast(`Error: ${errData.error || errData.message || res.statusText}`);
      }
    } catch (err) {
      showToast(`Error de conexion: ${err.message}`);
    }
  }, [showToast, applyTransition]);

  const advance = useCallback((id) => {
    const order = orders.find((o) => o.id === id);
    if (!order) return;
    const flow = STATUS_FLOW[order.status];
    if (!flow) return;
    // Al ACEPTAR, primero pedimos el tiempo de preparación (se envía a la plataforma).
    if (flow.apiAction === "accept") {
      setPrepModal({ id, mode: "accept", suggested: order.suggestedPrepMinutes || 20 });
      return;
    }
    postAdvance(id, flow);
  }, [orders, postAdvance]);

  // Confirmar aceptación con el tiempo de preparación elegido
  const confirmAccept = useCallback((id, minutes) => {
    setPrepModal(null);
    postAdvance(id, STATUS_FLOW.new, { prepTimeMinutes: minutes });
  }, [postAdvance]);

  // Abrir el modal para ajustar el tiempo de un pedido ya aceptado
  const openAdjustTime = useCallback((id) => {
    const order = orders.find((o) => o.id === id);
    setPrepModal({ id, mode: "adjust", suggested: (order && order.suggestedPrepMinutes) || 15 });
  }, [orders]);

  // Confirmar ajuste de tiempo (cocina atrasada) → update-ready-time
  const confirmAdjustTime = useCallback(async (id, minutes) => {
    setPrepModal(null);
    try {
      const res = await fetchWithAuth(`${API_BASE}/orders/${id}/ready-time`, {
        method: "POST", body: JSON.stringify({ minutes }),
      });
      const d = await res.json().catch(() => ({}));
      if (res.ok) showToast(`Tiempo de preparación actualizado a ${minutes} min`);
      else showToast(`Error: ${d.error || d.message || res.statusText}`);
    } catch (err) { showToast(`Error de conexión: ${err.message}`); }
  }, [showToast]);

  const cancelOrder = useCallback(async (id) => {
    try {
      const res = await fetchWithAuth(`${API_BASE}/orders/${id}/cancel`, {
        method: "POST",
        body: JSON.stringify({ reason: "Cancelado desde concentrador" }),
      });
      if (res.ok) { setOrders((prev) => prev.filter((o) => o.id !== id)); showToast(`Pedido #${id} cancelado`); }
    } catch (err) { showToast(`Error al cancelar: ${err.message}`); }
  }, [showToast]);

  const printOrder = useCallback((id) => { showToast(`Enviando ticket #${id} a la impresora`); }, [showToast]);

  const selected = orders.find((o) => o.id === selectedId);

  return (
    <>
      <Header
        platform={platform} setPlatform={setPlatform}
        counts={platformCounts}
        theme={theme} setTheme={setTheme}
        layout={layout} setLayout={setLayout}
        user={user}
      />

      {loading ? (
        <div className="empty" style={{ flex: 1 }}>
          <div className="empty-icon"><Icon name="refresh" size={26} stroke={1.4} /></div>
          <div className="empty-text">Cargando pedidos...</div>
        </div>
      ) : layout === "kanban" ? (
        <div className="kanban">
          {COLUMNS.map((c) => (
            <KanbanColumn
              key={c.id} col={c} orders={grouped[c.id]}
              selected={selectedId} onSelect={setSelectedId}
              onAdvance={advance} onPrint={printOrder} newIds={newIds}
            />
          ))}
        </div>
      ) : (
        <ListView orders={filtered} selected={selectedId} onSelect={setSelectedId} onAdvance={advance} />
      )}

      <OrderModal
        order={selected}
        onClose={() => setSelectedId(null)}
        onAdvance={advance}
        onCancel={cancelOrder}
        onPrint={printOrder}
        onAdjustTime={openAdjustTime}
      />

      <PrepTimeModal
        data={prepModal}
        onConfirm={(minutes) => {
          if (!prepModal) return;
          if (prepModal.mode === "accept") confirmAccept(prepModal.id, minutes);
          else confirmAdjustTime(prepModal.id, minutes);
        }}
        onCancel={() => setPrepModal(null)}
      />
    </>
  );
}

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