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

/* ── Layout primitives ── */

function Screen({ className, children }) {
  const cls = className ? `screen ${className}` : "screen";
  return <div className={cls}>{children}</div>;
}

/* ── Helpers ── */

function formatPrice(priceCents) {
  return "$" + (priceCents / 100).toFixed(priceCents % 100 === 0 ? 0 : 2);
}

function tokenPriceInUsd(prices, symbol) {
  const entry = prices?.result?.prices?.[symbol]?.USD;
  if (!entry) return null;
  return Number(entry.amount) / Math.pow(10, entry.decimals);
}

function convertCentsToTokens(prices, priceCents) {
  const priceUsd = priceCents / 100;
  const tokens = [];

  const wldPrice = tokenPriceInUsd(prices, "WLD");
  if (wldPrice) {
    const wldCount = priceUsd / wldPrice;
    const wldRaw = BigInt(Math.round(wldCount * 1e18));
    tokens.push({ symbol: "WLD", token_amount: wldRaw.toString() });
  }

  const usdcPrice = tokenPriceInUsd(prices, "USDC");
  if (usdcPrice) {
    const usdcCount = priceUsd / usdcPrice;
    const usdcRaw = BigInt(Math.round(usdcCount * 1e6));
    tokens.push({ symbol: "USDC", token_amount: usdcRaw.toString() });
  }

  console.log("[*] convertCentsToTokens:", { priceCents, priceUsd, tokens });
  return tokens;
}

/* ── Comics Carousel ── */

function ComicsCarousel({ artifactId, userId }) {
  const [page, setPage] = useState(0);
  const [scale, setScale] = useState(1);
  const [pan, setPan] = useState({ x: 0, y: 0 });

  const totalPages = 3;
  const containerRef = useRef(null);

  /* touch state tracking */
  const touchState = useRef({
    startTouches: null,
    startPan: { x: 0, y: 0 },
    startScale: 1,
    startDist: null,
    swiping: false,
    swipeStartX: 0,
  });

  function dist(t1, t2) {
    const dx = t1.clientX - t2.clientX;
    const dy = t1.clientY - t2.clientY;
    return Math.sqrt(dx * dx + dy * dy);
  }

  function clampPan(px, py, s) {
    const el = containerRef.current;
    if (!el) return { x: px, y: py };
    const w = el.clientWidth;
    const h = el.clientHeight;
    const maxX = Math.max(0, (w * s - w) / 2);
    const maxY = Math.max(0, (h * s - h) / 2);
    return {
      x: Math.min(maxX, Math.max(-maxX, px)),
      y: Math.min(maxY, Math.max(-maxY, py)),
    };
  }

  function goTo(newPage) {
    setPage(newPage);
    setScale(1);
    setPan({ x: 0, y: 0 });
  }

  const onTouchStart = useCallback((e) => {
    const touches = Array.from(e.touches);
    const ts = touchState.current;
    ts.startTouches = touches;
    ts.startPan = pan;
    ts.startScale = scale;

    if (touches.length === 2) {
      ts.startDist = dist(touches[0], touches[1]);
      ts.swiping = false;
    } else if (touches.length === 1) {
      ts.startDist = null;
      ts.swiping = scale <= 1;
      ts.swipeStartX = touches[0].clientX;
    }
  }, [pan, scale]);

  const onTouchMove = useCallback((e) => {
    e.preventDefault();
    const touches = Array.from(e.touches);
    const ts = touchState.current;

    if (touches.length === 2 && ts.startDist !== null) {
      const newDist = dist(touches[0], touches[1]);
      const newScale = Math.min(5, Math.max(1, ts.startScale * (newDist / ts.startDist)));
      const clamped = clampPan(ts.startPan.x, ts.startPan.y, newScale);
      setScale(newScale);
      setPan(clamped);
    } else if (touches.length === 1 && ts.startTouches?.length === 1) {
      const dx = touches[0].clientX - ts.startTouches[0].clientX;
      const dy = touches[0].clientY - ts.startTouches[0].clientY;

      if (ts.swiping) {
        /* handled on touchend */
      } else {
        const newPan = clampPan(ts.startPan.x + dx, ts.startPan.y + dy, scale);
        setPan(newPan);
      }
    }
  }, [scale]);

  const onTouchEnd = useCallback((e) => {
    const ts = touchState.current;
    if (ts.swiping && e.changedTouches.length === 1) {
      const dx = e.changedTouches[0].clientX - ts.swipeStartX;
      if (Math.abs(dx) > 50) {
        if (dx < 0 && page < totalPages - 1) goTo(page + 1);
        else if (dx > 0 && page > 0) goTo(page - 1);
      }
    }
    ts.startTouches = null;
    ts.startDist = null;
  }, [page, scale]);

  const onWheel = useCallback((e) => {
    e.preventDefault();
    const delta = e.deltaY > 0 ? 0.9 : 1.1;
    const newScale = Math.min(5, Math.max(1, scale * delta));
    const clamped = clampPan(pan.x, pan.y, newScale);
    setScale(newScale);
    setPan(clamped);
  }, [scale, pan]);

  /* mouse drag for pan */
  const mouseState = useRef({ dragging: false, startX: 0, startY: 0, startPan: { x: 0, y: 0 } });

  const onMouseDown = useCallback((e) => {
    if (scale <= 1) return;
    mouseState.current = { dragging: true, startX: e.clientX, startY: e.clientY, startPan: pan };
  }, [scale, pan]);

  const onMouseMove = useCallback((e) => {
    if (!mouseState.current.dragging) return;
    const dx = e.clientX - mouseState.current.startX;
    const dy = e.clientY - mouseState.current.startY;
    const clamped = clampPan(mouseState.current.startPan.x + dx, mouseState.current.startPan.y + dy, scale);
    setPan(clamped);
  }, [scale]);

  const onMouseUp = useCallback(() => {
    mouseState.current.dragging = false;
  }, []);

  const pages = [1, 2, 3].map((n) => `/api/comic/${artifactId}/${n}?userId=${userId}`);

  return (
    <div className="carousel-wrap">
      <div
        ref={containerRef}
        className="carousel-viewport"
        onTouchStart={onTouchStart}
        onTouchMove={onTouchMove}
        onTouchEnd={onTouchEnd}
        onWheel={onWheel}
        onMouseDown={onMouseDown}
        onMouseMove={onMouseMove}
        onMouseUp={onMouseUp}
        onMouseLeave={onMouseUp}
        style={{ cursor: scale > 1 ? "grab" : "default" }}
      >
        {pages.map((src, i) => (
          <img
            key={src}
            className="carousel-image"
            src={src}
            alt={`Page ${i + 1}`}
            draggable={false}
            style={{
              transform: i === page ? `translate(${pan.x}px, ${pan.y}px) scale(${scale})` : "none",
              transformOrigin: "center center",
              userSelect: "none",
              position: "absolute",
              opacity: i === page ? 1 : 0,
              pointerEvents: i === page ? "auto" : "none",
            }}
          />
        ))}
      </div>

      <div className="carousel-controls">
        <button
          className="carousel-btn"
          onClick={() => goTo(page - 1)}
          disabled={page === 0}
        >&#9664;</button>
        <span className="carousel-indicator">{page + 1} / {totalPages}</span>
        <button
          className="carousel-btn"
          onClick={() => goTo(page + 1)}
          disabled={page === totalPages - 1}
        >&#9654;</button>
      </div>
    </div>
  );
}

/* ── App ── */

function App() {
  const [screen, setScreen] = useState("loading");
  const [testing, setTesting] = useState(false);
  const [user, setUser] = useState(null);
  const [artifacts, setArtifacts] = useState([]);
  const [currentArtifact, setCurrentArtifact] = useState(null);
  const [prices, setPrices] = useState(null);
  const [overlay, setOverlay] = useState(null);
  const [toast, setToast] = useState(null);

  const minikit = useRef(null);
  const toastTimer = useRef(null);
  const userRef = useRef(null);
  const pricesRef = useRef(null);

  userRef.current = user;
  pricesRef.current = prices;

  function showOverlay(title, message) {
    setOverlay({ title, message });
  }

  function hideOverlay() {
    setOverlay(null);
  }

  function showToast(message, type = "", duration = 3000) {
    if (toastTimer.current) clearTimeout(toastTimer.current);
    setToast({ message, type });
    toastTimer.current = setTimeout(() => setToast(null), duration);
  }

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

  async function init() {
    console.log("[*] Initializing...");
    try {
      const configRes = await fetch("/api/config");
      const config = await configRes.json();
      console.log("[*] Config:", config);

      let MK = null;
      if (config.testing) {
        setTesting(true);
        const mod = await import("/lib/testing/minikit.js");
        MK = mod.MiniKit;
      } else {
        const mod = await import("https://cdn.jsdelivr.net/npm/@worldcoin/minikit-js@latest/+esm");
        MK = mod.MiniKit;
      }

      minikit.current = MK;
      MK.install();
      console.log("[*] MiniKit installed, isInstalled:", MK.isInstalled());

      if (MK.isInstalled()) {
        await authenticate(MK);
        await loadPrices();
        await loadArtifacts(userRef.current);
        setScreen("archive");
      } else {
        setScreen("outside");
      }
    } catch (error) {
      console.error("[!] Init error:", error);
      showToast("Failed to initialize: " + error.message, "error", 5000);
    }
  }

  async function loadPrices() {
    console.log("[*] Loading prices...");
    try {
      const res = await fetch("/api/prices");
      const data = await res.json();
      setPrices(data);
      pricesRef.current = data;
      console.log("[*] Prices loaded:", data);
    } catch (error) {
      console.error("[!] Error loading prices:", error);
    }
  }

  async function authenticate(MK) {
    console.log("[*] Authenticating...");
    try {
      const savedUser = localStorage.getItem("sf_user");
      if (savedUser) {
        const parsed = JSON.parse(savedUser);
        setUser(parsed);
        userRef.current = parsed;
        console.log("[*] Restored user from localStorage:", parsed);
        return;
      }

      const nonce = crypto.randomUUID().replace(/-/g, "");
      const request = {
        nonce,
        expirationTime: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
      };

      console.log("[*] walletAuth request:", request);
      const result = await MK.walletAuth(request);
      console.log("[*] walletAuth response:", result);

      if (result.executedWith === "minikit" || result.executedWith === "wagmi") {
        const userInfo = await MK.getUserByAddress(result.data.address);
        console.log("[*] getUserByAddress:", userInfo);

        const res = await fetch("/api/users", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            walletAddress: userInfo.walletAddress,
            username: userInfo.username,
            profilePictureUrl: userInfo.profilePictureUrl,
          }),
        });
        const newUser = await res.json();
        setUser(newUser);
        userRef.current = newUser;
        localStorage.setItem("sf_user", JSON.stringify(newUser));
        console.log("[*] User saved:", newUser);
      } else {
        console.warn("[!] Authentication failed:", result);
        showToast("Authentication failed", "error");
      }
    } catch (error) {
      console.error("[!] Auth error:", error);
      showToast("Authentication error: " + error.message, "error");
    }
  }

  async function loadArtifacts(currentUser) {
    console.log("[*] Loading artifacts...");
    try {
      const url = currentUser
        ? `/api/users/${currentUser.id}/artifacts`
        : "/api/artifacts";
      const res = await fetch(url);
      const data = await res.json();
      setArtifacts(data);
      console.log("[*] Artifacts loaded:", data.length);
    } catch (error) {
      console.error("[!] Error loading artifacts:", error);
      showToast("Failed to load artifacts", "error");
    }
  }

  function openDetail(artifact) {
    setCurrentArtifact(artifact);
    setScreen("detail");
  }

  async function startPayment(artifact) {
    const currentUser = userRef.current;
    if (!currentUser) {
      showToast("Please authenticate first", "error");
      return;
    }

    let currentPrices = pricesRef.current;
    if (!currentPrices) {
      showToast("Prices not loaded yet, please wait", "error");
      await loadPrices();
      currentPrices = pricesRef.current;
      if (!currentPrices) return;
    }

    console.log("[*] Starting payment for artifact:", artifact.id);
    showOverlay("Pay", "Initiating payment...");

    try {
      const initRes = await fetch("/api/initiate-payment", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ artifactId: artifact.id, userId: currentUser.id }),
      });
      const paymentInit = await initRes.json();
      console.log("[*] Payment initiated:", paymentInit);

      if (!paymentInit.id) {
        hideOverlay();
        showToast("Failed to initiate payment: " + (paymentInit.error || "Unknown error"), "error");
        return;
      }

      const tokens = convertCentsToTokens(currentPrices, paymentInit.price_cents);
      if (tokens.length === 0) {
        hideOverlay();
        showToast("Could not calculate token amounts", "error");
        return;
      }

      showOverlay("Pay", "Waiting for payment confirmation...");

      const payRequest = {
        reference: paymentInit.id,
        to: paymentInit.to,
        tokens,
        description: paymentInit.description,
      };
      console.log("[*] MiniKit.pay request:", payRequest);

      const payResult = await minikit.current.pay(payRequest);
      console.log("[*] MiniKit.pay response:", payResult);

      if (payResult.executedWith === "minikit") {
        showOverlay("Confirming", "Verifying your payment...");

        const confirmRes = await fetch(`/api/confirm-payment/${paymentInit.id}`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payResult.data),
        });
        const confirmation = await confirmRes.json();
        console.log("[*] Payment confirmation:", confirmation);

        if (confirmation.success) {
          hideOverlay();
          showToast("Comic unlocked!", "success");
          await loadArtifacts(currentUser);
          setCurrentArtifact((prev) => {
            const updated = artifacts.find((a) => a.id === artifact.id);
            return updated ? { ...updated, unlocked: true } : { ...artifact, unlocked: true };
          });
        } else {
          hideOverlay();
          showToast("Payment verification failed", "error");
        }
      } else {
        hideOverlay();
        console.warn("[!] Payment failed or cancelled:", payResult);
        showToast("Payment was cancelled or failed", "error");
      }
    } catch (error) {
      hideOverlay();
      console.error("[!] Payment error:", error);
      showToast("Payment error: " + error.message, "error", 5000);
    }
  }

  /* ── Render ── */

  const unlocked =
    currentArtifact &&
    (currentArtifact.unlocked === true || currentArtifact.unlocked === "true");


  return (
    <div className="app">

      {/* Loading */}
      {screen === "loading" && (
        <Screen className="screen-loading">
          Loading, please wait...
        </Screen>
      )}

      {/* Outside World app */}
      {screen === "outside" && (
        <Screen className="screen-outside">
          <h1>Symbolic Frontiers</h1>
          <img src="logo.png" alt="Symbolic Frontiers" width="200" height="200" />
          <p>
            Only available through the{" "}
            <a href="https://world.org/" target="_blank" rel="noopener noreferrer" style={{ color: "#38d9a9" }}>
              world.org
            </a>{" "}
            platform.
          </p>
          <a className="btn-outside" href="https://world.org/ecosystem" rel="noopener noreferrer">
            Get the World app
          </a>
        </Screen>
      )}

      {/* Archive */}
      <Screen className="index-screen">
        {testing && <div className="testing-banner">TESTING MODE</div>}
        <div className="archive-header">Comics</div>
        <div className="archive-list">
          {artifacts.map((artifact) => {
            const coverImg = artifact.cover_image_url || artifact.preview_picture_url || "";
            return (
              <div className="artifact-card" key={artifact.id}>
                <img className="artifact-cover" src={coverImg} alt={artifact.title} />
                <button className="btn-view" onClick={() => openDetail(artifact)}>
                  View
                </button>
              </div>
            );
          })}
        </div>
        <div className="archive-footer">© Symbolic Frontiers</div>
      </Screen>

      {/* Detail (overlay) */}
      <Screen className={`overlay-screen ${screen === "detail" ? "active" : ""}`}>
        <div className="detail-toolbar">
          <button className="btn-back" onClick={() => { setScreen("archive"); setCurrentArtifact(null); }}>
            &#9664; Back
          </button>
        </div>

        {currentArtifact && (
          <>
            <div className="detail-info">
              <h2>{currentArtifact.title}</h2>
              {currentArtifact.subtitle && (
                <p className="detail-subtitle">{currentArtifact.subtitle}</p>
              )}
              {currentArtifact.description && (
                <p className="detail-description">{currentArtifact.description}</p>
              )}
            </div>

            {unlocked ? (
              <ComicsCarousel key={currentArtifact.id} artifactId={currentArtifact.id} userId={user?.id} />
            ) : (
              <div className="detail-preview-wrap">
                <div
                  className="detail-preview"
                  style={{ backgroundImage: `url('${currentArtifact.preview_picture_url || ""}')` }}
                />
                <div className="detail-preview-banner">
                  Unlock to read the full comic.
                </div>
              </div>
            )}

            {!unlocked && (
              <div className="detail-bottom">
                <button
                  className="btn-unlock"
                  onClick={() => startPayment(currentArtifact)}
                >
                  &#128274;&#65039; Unlock &middot; {formatPrice(currentArtifact.price_cents || 0)}
                </button>
              </div>
            )}
          </>
        )}
      </Screen>

      {/* Processing overlay */}
      <div className={`processing-overlay ${overlay ? "active" : ""}`}>
        <div className="processing-overlay-content">
          <div className="spinner" />
          <h3>{overlay?.title}</h3>
          <p>{overlay?.message}</p>
        </div>
      </div>

      {/* Toast */}
      <div className={`toast ${toast ? "visible " + toast.type : ""}`}>
        {toast?.message}
      </div>
    </div>
  );
}

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