// Root component for the ezscreenshots ASO tool (production).
const { useState: useS, useEffect: useE, useMemo: useM, useCallback: useCB, useRef: useR } = React;

const THEMES = {
  terracotta: { name: "Terracotta", accent: "#BE644A", deep: "#a5553c", soft: "#f1d9d0", paper: "#F4EFE5", paperDeep: "#EEE7D8", rule: "#d8d3c8" },
};

const LS_SAVED = "ezaso.saved.v1";
const LS_PINNED = "ezaso.pinned.v1";
const LS_LAST = "ezaso.lastTerm.v1";

function loadJSON(key, fallback) {
  try {
    const v = localStorage.getItem(key);
    return v ? JSON.parse(v) : fallback;
  } catch (e) { return fallback; }
}
function saveJSON(key, v) {
  try { localStorage.setItem(key, JSON.stringify(v)); } catch (e) {}
}

function App() {
  const [query, setQuery] = useS(() => loadJSON(LS_LAST, ""));
  const [keyword, setKeyword] = useS(() => loadJSON(LS_LAST, ""));
  const [suggestions, setSuggestions] = useS([]);
  const [apps, setApps] = useS([]);
  const [loading, setLoading] = useS(false);
  const [selectedAppId, setSelectedAppId] = useS(null);
  const [selectedApp, setSelectedApp] = useS(null);
  const [drawerOpen, setDrawerOpen] = useS(false);
  const [pinned, setPinned] = useS(() => loadJSON(LS_PINNED, []));
  const [pinnedApps, setPinnedApps] = useS([]);
  const [saved, setSaved] = useS(() => loadJSON(LS_SAVED, []));
  const [compareOpen, setCompareOpen] = useS(false);
  const [compareShots, setCompareShots] = useS({});
  const [keywordsOpen, setKeywordsOpen] = useS(false);
  const [apiModalOpen, setApiModalOpen] = useS(false);
  const [sponsorModalOpen, setSponsorModalOpen] = useS(false);
  const [myAppId, setMyAppId] = useS(() => loadJSON("ezaso.myApp.v1", null));
  const searchConsumeRef = useR(false);
  const compareConsumeRef = useR(false);
  const [isProUser, setIsProUser] = useS(() => window.asoPro?.isPro() || false);
  const [researchRemaining, setResearchRemaining] = useS(() => window.asoPro?.getResearchRemaining() ?? 2);

  useE(() => {
    if (!window.asoPro) return;
    window.asoPro.validateOnLoad().then(() => {
      setIsProUser(window.asoPro.isPro());
      setResearchRemaining(window.asoPro.getResearchRemaining());
      window.asoPro.updateGauge();
    });
  }, []);

  const syncResearch = useCB(() => {
    if (!window.asoPro) return;
    setIsProUser(window.asoPro.isPro());
    setResearchRemaining(window.asoPro.getResearchRemaining());
    window.asoPro.updateGauge();
  }, []);

  const gateResearch = useCB((action) => {
    if (!window.asoPro) { action(); return true; }
    if (window.asoPro.isPro()) { action(); return true; }
    if (!window.asoPro.canUseResearch()) {
      window.asoPro.showPaywall();
      syncResearch();
      return false;
    }
    action();
    return true;
  }, [syncResearch]);

  const billResearch = useCB(() => {
    if (!window.asoPro || window.asoPro.isPro()) return;
    window.asoPro.consumeResearch();
    syncResearch();
  }, [syncResearch]);

  const tweaks = { density: "comfy", showDifficulty: true, showCrossPromo: true, theme: "terracotta", isPro: isProUser };

  // Persist
  useE(() => saveJSON(LS_SAVED, saved), [saved]);
  useE(() => saveJSON(LS_PINNED, pinned), [pinned]);
  useE(() => saveJSON(LS_LAST, keyword), [keyword]);
  useE(() => saveJSON("ezaso.myApp.v1", myAppId), [myAppId]);

  // Esc closes drawer/modal
  useE(() => {
    const onKey = (e) => {
      if (e.key === "Escape") {
        if (apiModalOpen) setApiModalOpen(false);
        else if (sponsorModalOpen) setSponsorModalOpen(false);
        else if (keywordsOpen) setKeywordsOpen(false);
        else if (compareOpen) setCompareOpen(false);
        else if (drawerOpen) setDrawerOpen(false);
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [drawerOpen, compareOpen, keywordsOpen, apiModalOpen, sponsorModalOpen]);

  // Apply theme
  useE(() => {
    const t = THEMES.terracotta;
    const root = document.body.style;
    root.setProperty("--accent", t.accent);
    root.setProperty("--accent-deep", t.deep);
    root.setProperty("--accent-soft", t.soft);
    root.setProperty("--paper", t.paper);
    root.setProperty("--paper-deep", t.paperDeep);
    root.setProperty("--rule-deep", t.rule);
  }, []);

  // Fetch search results when keyword changes
  useE(() => {
    if (!keyword) { setApps([]); setSuggestions([]); return; }
    setLoading(true);
    const consume = searchConsumeRef.current;
    searchConsumeRef.current = false;
    Promise.all([
      window.asoAPI.search(keyword, { num: 15, consume }),
      window.asoAPI.suggest(keyword),
    ]).then(([searchResults, suggestResults]) => {
      setApps(searchResults);
      setSuggestions(suggestResults);
      setLoading(false);
      if (consume) billResearch();
      if (window.plausible) plausible('ASO Search', { props: { term: keyword } });
    }).catch((err) => {
      setLoading(false);
      if (err && err.message === 'Research limit reached' && window.asoPro) {
        window.asoPro.showPaywall();
        syncResearch();
      }
    });
  }, [keyword]);

  // Fetch full app detail + screenshots when drawer opens
  useE(() => {
    if (!selectedAppId || !drawerOpen) return;
    const appFromList = apps.find(a => a.appId === selectedAppId || a.id === selectedAppId);
    if (appFromList) setSelectedApp(appFromList);
    const numId = appFromList ? appFromList.id : selectedAppId;
    if (numId) {
      window.asoAPI.app(numId).then(fullApp => {
        setSelectedApp(prev => {
          if (!prev) return fullApp;
          const merged = { ...prev, ...fullApp };
          if (!fullApp.subtitle && prev.subtitle) merged.subtitle = prev.subtitle;
          return merged;
        });
      }).catch(() => {});
      window.asoAPI.screenshots(numId, { consume: false }).then(data => {
        setSelectedApp(prev => prev ? { ...prev, screenshotImages: data.screenshots || [] } : prev);
      }).catch(() => {});
    }
  }, [selectedAppId, drawerOpen]);

  // Sync pinned apps data
  useE(() => {
    if (pinned.length === 0) { setPinnedApps([]); return; }
    const found = pinned.map(id => apps.find(a => a.appId === id)).filter(Boolean);
    setPinnedApps(found);
  }, [pinned, apps]);

  // Fetch screenshots for compare modal
  useE(() => {
    if (!compareOpen || pinnedApps.length === 0) return;
    const consumeCompare = compareConsumeRef.current;
    compareConsumeRef.current = false;
    if (consumeCompare && pinnedApps.length > 0 && pinnedApps.every(app => compareShots[app.id])) {
      billResearch();
      return;
    }
    let billed = !consumeCompare;
    pinnedApps.forEach(app => {
      if (compareShots[app.id]) return;
      const shouldConsume = consumeCompare && !billed;
      window.asoAPI.screenshots(app.id, { consume: shouldConsume }).then(data => {
        setCompareShots(prev => ({ ...prev, [app.id]: data.screenshots || [] }));
        if (shouldConsume) {
          billed = true;
          billResearch();
        }
      }).catch(() => {});
    });
  }, [compareOpen, pinnedApps]);

  // Fetch full details (description, languages) for pinned apps when either modal opens
  useE(() => {
    if ((!keywordsOpen && !compareOpen) || pinnedApps.length === 0) return;
    pinnedApps.forEach(app => {
      if (app._enriched) return;
      window.asoAPI.app(app.id).then(fullApp => {
        setPinnedApps(prev => prev.map(a =>
          a.id === app.id
            ? { ...a, _enriched: true, description: fullApp.description || a.description || '', languages: fullApp.languages || a.languages || [], updated: fullApp.updated || a.updated || '' }
            : a
        ));
      }).catch(() => {});
    });
  }, [keywordsOpen, compareOpen, pinnedApps.length]);

  const savedSet = useM(() => new Set(saved.map(s => s.term)), [saved]);

  const onSubmitSearch = useCB((term) => {
    const t = (term || "").trim();
    if (!t) return;
    if (t === keyword && apps.length > 0) return;
    gateResearch(() => {
      searchConsumeRef.current = true;
      setKeyword(t);
      setQuery(t);
      setDrawerOpen(false);
      setSelectedAppId(null);
      setSelectedApp(null);
    });
  }, [keyword, apps.length, gateResearch]);

  const onPickKeyword = useCB((term) => {
    if (term === keyword && apps.length > 0) return;
    gateResearch(() => {
      searchConsumeRef.current = true;
      setKeyword(term);
      setQuery(term);
      setDrawerOpen(false);
      setSelectedAppId(null);
      setSelectedApp(null);
    });
  }, [keyword, apps.length, gateResearch]);

  const onSelectApp = useCB((appId) => {
    if (drawerOpen && selectedAppId === appId) return;
    setSelectedAppId(appId);
    setDrawerOpen(true);
    if (window.plausible) plausible('ASO App Click', { props: { appId } });
  }, [drawerOpen, selectedAppId]);

  const onTogglePin = useCB((appId) => {
    setPinned(prev => {
      if (prev.includes(appId)) return prev.filter(id => id !== appId);
      const next = [...prev, appId];
      return next.length > 4 ? next.slice(next.length - 4) : next;
    });
  }, []);

  const onToggleSaveKeyword = useCB((term) => {
    setSaved(prev => {
      const exists = prev.find(s => s.term === term);
      if (exists) return prev.filter(s => s.term !== term);
      const sugg = suggestions.find(s => s.term === term);
      const priority = sugg ? sugg.priority : 0;
      if (window.plausible) plausible('ASO Keyword Saved', { props: { term } });
      return [...prev, { term, priority }];
    });
  }, [suggestions]);

  const hasResults = !!keyword;

  return (
    <div>
      <Topbar
        isPro={isProUser}
        researchRemaining={researchRemaining}
        onHome={() => {
          setKeyword("");
          setQuery("");
          setDrawerOpen(false);
          setSelectedAppId(null);
          setSelectedApp(null);
          setCompareOpen(false);
        }}
      />

      <div className="shell">
        {hasResults ? (
          <ResultsView
            keyword={keyword}
            query={query}
            setQuery={setQuery}
            onSubmit={onSubmitSearch}
            suggestions={suggestions}
            apps={apps}
            loading={loading}
            savedSet={savedSet}
            saved={saved}
            pinned={pinned}
            selectedAppId={selectedAppId}
            onPickKeyword={onPickKeyword}
            onToggleSaveKeyword={onToggleSaveKeyword}
            onSelectApp={onSelectApp}
            onTogglePin={onTogglePin}
            tweaks={tweaks}
            onOpenApi={() => setApiModalOpen(true)}
          />
        ) : (
          <EmptyHero
            query={query}
            setQuery={setQuery}
            onSubmit={onSubmitSearch}
            saved={saved}
            tweaks={tweaks}
            onOpenSponsor={() => setSponsorModalOpen(true)}
          />
        )}
      </div>

      <AuditDrawer
        app={selectedApp}
        open={drawerOpen && !!selectedApp}
        pinned={selectedApp ? pinned.includes(selectedApp.appId) : false}
        showCrossPromo={tweaks.showCrossPromo}
        onClose={() => setDrawerOpen(false)}
        onTogglePin={() => selectedApp && onTogglePin(selectedApp.appId)}
        myAppId={myAppId}
        onSetMyApp={setMyAppId}
      />

      <CompareTray
        pinnedApps={pinnedApps}
        onOpen={() => {
          if (compareOpen) return;
          gateResearch(() => {
            compareConsumeRef.current = true;
            setCompareOpen(true);
            if (window.plausible) plausible('ASO Compare Open');
          });
        }}
        onOpenKeywords={() => { setKeywordsOpen(true); if (window.plausible) plausible('ASO Keyword Extractor Open'); }}
        onClose={() => setPinned([])}
      />

      {compareOpen && (
        <CompareModal
          pinnedApps={pinnedApps}
          compareShots={compareShots}
          onClose={() => setCompareOpen(false)}
          onUnpin={(id) => {
            setPinned(p => p.filter(x => x !== id));
          }}
          myAppId={myAppId}
          onSetMyApp={setMyAppId}
        />
      )}

      <CompetitorKeywordsModal
        open={keywordsOpen && pinnedApps.length >= 1}
        onClose={() => setKeywordsOpen(false)}
        pinnedApps={pinnedApps}
        suggestions={suggestions}
        savedKeywords={saved}
        onSaveKeyword={(term, priority) => {
          setSaved(prev => {
            if (prev.find(s => s.term === term)) return prev.filter(s => s.term !== term);
            return [...prev, { term, priority: priority || 0 }];
          });
        }}
        isPro={tweaks.isPro}
      />

      {apiModalOpen && <ApiAccessModal onClose={() => setApiModalOpen(false)} />}
      {sponsorModalOpen && <SponsorModal onClose={() => setSponsorModalOpen(false)} />}
    </div>
  );
}

/* ── Topbar ─────────────────────────────────────────────────── */
function Topbar({ onHome, isPro, researchRemaining }) {
  const limit = window.asoPro?.RESEARCH_LIMIT || 2;
  const gaugeText = isPro
    ? 'Pro · Unlimited'
    : researchRemaining === 0
      ? 'No research left'
      : `${researchRemaining}/${limit} research`;
  return (
    <header className="topbar">
      <a href="https://ezscreenshots.com" className="brand">ezscreenshots<span className="dot">.</span></a>
      <button className="crumb crumb-btn" onClick={onHome} title="Back to start">/ ASO Research</button>
      <span className="spacer" />
      <button
        type="button"
        className={`research-gauge${isPro ? ' pro' : ''}${!isPro && researchRemaining === 0 ? ' warning' : ''}`}
        id="asoResearchGauge"
        title={isPro ? 'Pro status' : 'Research limit'}
        onClick={() => window.asoPro?.onGaugeClick()}
      >
        <span id="asoResearchGaugeText">{gaugeText}</span>
      </button>
      <a className="top-link" href="https://ezscreenshots.com/tools">Tools</a>
      <a className="top-link" href="https://x.com/aryanbhasin_" target="_blank" rel="noopener">@aryanbhasin_</a>
      <a className="top-cta" href="https://ezscreenshots.com/app">Open editor →</a>
    </header>
  );
}

/* ── Empty hero ─────────────────────────────────────────────── */
function EmptyHero({ query, setQuery, onSubmit, saved, tweaks, onOpenSponsor }) {
  return (
    <div className="hero">
      <div className="eyebrow">
        {tweaks.isPro
          ? 'by ezscreenshots'
          : 'by ezscreenshots · 2 free researches per device'}
      </div>
      <h1>a <em>fast ASO Research tool</em> for App Store</h1>
      <p className="lede">
        Research keywords and audit your competitors.
      </p>
      <div className="hero-search">
        <SearchBar
          value={query}
          onChange={setQuery}
          onSubmit={onSubmit}
          autoFocus
          placeholder="Try 'habit tracker', 'meditation', 'todo list'…"
        />
        <div className="examples">
          <span className="ex-label">Try</span>
          {window.EXAMPLES.map(ex => (
            <button key={ex} onClick={() => onSubmit(ex)}>{ex}</button>
          ))}
        </div>
        <button className="sponsor-link" onClick={onOpenSponsor}>
          Sponsor this tool
        </button>
      </div>
    </div>
  );
}

/* ── Results view ───────────────────────────────────────────── */
function ResultsView({
  keyword, query, setQuery, onSubmit,
  suggestions, apps, loading, savedSet, saved, pinned, selectedAppId,
  onPickKeyword, onToggleSaveKeyword, onSelectApp, onTogglePin, tweaks,
  onOpenApi
}) {
  return (
    <div className="results">
      <div style={{ maxWidth: 640, margin: "0 auto 28px" }}>
        <SearchBar
          value={query}
          onChange={setQuery}
          onSubmit={onSubmit}
          placeholder="Search a new keyword…"
        />
      </div>

      <ResultsHeader
        keyword={keyword}
        apps={apps}
        suggestions={suggestions}
        showDifficulty={tweaks.showDifficulty}
      />

      <div className="results-grid">
        <div>
          <KeywordDiscovery
            keyword={keyword}
            suggestions={suggestions}
            savedSet={savedSet}
            onPick={onPickKeyword}
            onToggleSave={onToggleSaveKeyword}
          />
          <SavedKeywords
            saved={saved}
            onPick={onPickKeyword}
            onUnsave={onToggleSaveKeyword}
          />
          <button className="api-access-link" onClick={onOpenApi}>
            <Icon name="external" size={12} />
            Want API access?
          </button>
        </div>
        <div>
          <AppsList
            keyword={keyword}
            apps={apps}
            selectedAppId={selectedAppId}
            pinned={pinned}
            onSelect={onSelectApp}
            onTogglePin={onTogglePin}
          />
        </div>
      </div>
    </div>
  );
}

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