// app.jsx — main App: location modal, top bar, scoring, layout

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

const POPULAR = {
  zh: [
    { name: "东京", lat: 35.6762, lon: 139.6503 },
    { name: "大阪", lat: 34.6937, lon: 135.5023 },
    { name: "京都", lat: 35.0116, lon: 135.7681 },
    { name: "札幌", lat: 43.0618, lon: 141.3545 },
    { name: "福冈", lat: 33.5904, lon: 130.4017 },
    { name: "名古屋", lat: 35.1815, lon: 136.9066 },
  ],
  ja: [
    { name: "東京", lat: 35.6762, lon: 139.6503 },
    { name: "大阪", lat: 34.6937, lon: 135.5023 },
    { name: "京都", lat: 35.0116, lon: 135.7681 },
    { name: "札幌", lat: 43.0618, lon: 141.3545 },
    { name: "福岡", lat: 33.5904, lon: 130.4017 },
    { name: "名古屋", lat: 35.1815, lon: 136.9066 },
  ],
  en: [
    { name: "Tokyo", lat: 35.6762, lon: 139.6503 },
    { name: "Osaka", lat: 34.6937, lon: 135.5023 },
    { name: "Kyoto", lat: 35.0116, lon: 135.7681 },
    { name: "Sapporo", lat: 43.0618, lon: 141.3545 },
    { name: "Fukuoka", lat: 33.5904, lon: 130.4017 },
    { name: "Nagoya", lat: 35.1815, lon: 136.9066 },
  ],
};

// ────────────── Welcome modal
function WelcomeModal({ t, lang, onPick }) {
  const [mode, setMode] = useState("intro"); // intro | locating | search
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);
  const [searching, setSearching] = useState(false);
  const [error, setError] = useState("");

  // Search debounce
  useEffect(() => {
    if (mode !== "search" || !query.trim()) {
      setResults([]);
      return;
    }
    setSearching(true);
    const id = setTimeout(async () => {
      try {
        const r = await W.searchPlaces(query.trim(), lang);
        setResults(r);
      } catch (e) {
        setResults([]);
      } finally {
        setSearching(false);
      }
    }, 280);
    return () => clearTimeout(id);
  }, [query, mode, lang]);

  const useGPS = () => {
    setMode("locating");
    setError("");
    if (!navigator.geolocation) {
      setError(t.location_failed);
      setMode("search");
      return;
    }
    navigator.geolocation.getCurrentPosition(
      async (pos) => {
        const { latitude, longitude } = pos.coords;
        const name = await W.reverseGeocode(latitude, longitude, lang);
        onPick({ lat: latitude, lon: longitude, name });
      },
      (err) => {
        setError(t.location_failed);
        setMode("search");
      },
      { enableHighAccuracy: false, timeout: 10000, maximumAge: 5 * 60 * 1000 }
    );
  };

  const popular = POPULAR[lang] || POPULAR.en;

  return (
    <div className="modal-overlay" role="dialog" aria-modal="true">
      <div className="modal">
        <div>
          <div className="brand-sub">{t.app_sub}</div>
          <h2>{t.welcome}</h2>
          <p>{t.welcome_sub}</p>
        </div>

        {mode === "locating" && (
          <div style={{ display: "flex", alignItems: "center", gap: 10, color: "var(--fg-mute)" }}>
            <div className="spinner" style={{ width: 18, height: 18 }}></div>
            <span>{t.locating}</span>
          </div>
        )}

        {mode !== "locating" && (
          <div className="modal-actions">
            <button className="btn-primary" onClick={useGPS}>
              <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3"/>
              </svg>
              {t.use_gps}
            </button>
            {mode === "intro" && (
              <button className="btn-secondary" onClick={() => setMode("search")}>{t.pick_manually}</button>
            )}
          </div>
        )}

        {mode === "search" && (
          <>
            {error && <p style={{ color: "var(--bad)", fontSize: 12 }}>{error}</p>}
            <div>
              <input
                className="search-input"
                type="text"
                placeholder={t.search_placeholder}
                value={query}
                onChange={(e) => setQuery(e.target.value)}
                autoFocus
              />
              {query.trim().length > 0 && (
                <ul className="search-results">
                  {searching && <li style={{ color: "var(--fg-mute)" }}>{t.loading}</li>}
                  {!searching && results.length === 0 && <li style={{ color: "var(--fg-mute)" }}>{t.no_results}</li>}
                  {!searching && results.map((r, i) => (
                    <li key={i} onClick={() => onPick({ lat: r.latitude, lon: r.longitude, name: r.name + (r.country_code ? ` (${r.country_code})` : "") })}>
                      <span>{r.name}{r.admin1 ? `, ${r.admin1}` : ""}</span>
                      <span className="sub">{r.country}</span>
                    </li>
                  ))}
                </ul>
              )}
            </div>
            <div>
              <div className="brand-sub" style={{ marginBottom: 8 }}>{t.popular}</div>
              <div className="popular">
                {popular.map((p) => (
                  <button key={p.name} onClick={() => onPick(p)}>{p.name}</button>
                ))}
              </div>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// ────────────── Top bar
function TopBar({ t, lang, setLang, location, onChangeLocation, aesthetic, dark, setAesthetic, setDark }) {
  const langs = ["zh", "ja", "en"];
  const aesthetics = [
    { v: "track", label: t.tw_track },
    { v: "touring", label: t.tw_touring },
    { v: "hud", label: t.tw_hud },
  ];
  return (
    <header className="topbar">
      <div className="brand">
        <h1 className="brand-title">{t.app_title}</h1>
        <span className="brand-sub">{t.app_sub}</span>
      </div>
      <div className="topbar-controls">
        {location && (
          <div className="loc-chip">
            <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M12 2a7 7 0 00-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 00-7-7z"/>
              <circle cx="12" cy="9" r="2.5"/>
            </svg>
            <span>{location.name}</span>
            <button onClick={onChangeLocation}>{t.change_location}</button>
          </div>
        )}
        <div className="lang-switch" aria-label="aesthetic">
          {aesthetics.map((a) => (
            <button key={a.v} className={a.v === aesthetic ? "active" : ""} onClick={() => setAesthetic(a.v)}>{a.label}</button>
          ))}
        </div>
        <button
          className="theme-toggle"
          onClick={() => setDark(!dark)}
          aria-label={t.tw_dark}
          title={t.tw_dark}
        >
          {dark ? (
            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
            </svg>
          ) : (
            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M20 14.5A8 8 0 119.5 4a6.5 6.5 0 0010.5 10.5z"/>
            </svg>
          )}
        </button>
        <div className="lang-switch" aria-label="language">
          {langs.map((l) => (
            <button key={l} className={l === lang ? "active" : ""} onClick={() => setLang(l)}>{l.toUpperCase()}</button>
          ))}
        </div>
      </div>
    </header>
  );
}

// ────────────── Main App
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "aesthetic": "track",
  "dark": true,
  "rider": "tourer",
  "rainWeight": 1.8,
  "windWeight": 1.2,
  "tempWeight": 1.2,
  "units": "metric"
}/*EDITMODE-END*/;

function App() {
  const [t9, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [lang, setLangRaw] = useState(() => (window.detectLang ? window.detectLang() : "en"));
  const setLang = (l) => {
    localStorage.setItem("ride_lang", l);
    setLangRaw(l);
  };
  const t = I18N[lang];

  const [location, setLocation] = useState(() => {
    const saved = localStorage.getItem("ride_location");
    if (!saved) return null;
    try { return JSON.parse(saved); } catch { return null; }
  });

  const [weather, setWeather] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [selectedDate, setSelectedDate] = useState(null); // null = today

  // Window width for responsive layout
  const [winW, setWinW] = useState(typeof window !== "undefined" ? window.innerWidth : 1024);
  useEffect(() => {
    const onR = () => setWinW(window.innerWidth);
    window.addEventListener("resize", onR);
    return () => window.removeEventListener("resize", onR);
  }, []);
  const isMobile = winW < 760;

  // Apply theme dataset
  useEffect(() => {
    document.documentElement.dataset.aesthetic = t9.aesthetic;
    document.documentElement.dataset.theme = t9.dark ? "dark" : "light";
  }, [t9.aesthetic, t9.dark]);

  // Persist location
  useEffect(() => {
    if (location) localStorage.setItem("ride_location", JSON.stringify(location));
  }, [location]);

  // Fetch weather whenever location changes
  useEffect(() => {
    if (!location) return;
    let cancelled = false;
    setLoading(true);
    setError("");
    setSelectedDate(null);
    W.fetchWeather(location.lat, location.lon)
      .then((d) => { if (!cancelled) setWeather(d); })
      .catch((e) => { if (!cancelled) setError(t.fetch_error); })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [location]);

  // Build scoring memoized
  const weights = useMemo(() => ({
    rain: t9.rainWeight,
    wind: t9.windWeight,
    temp: t9.tempWeight,
  }), [t9.rainWeight, t9.windWeight, t9.tempWeight]);

  const scoring = useMemo(() => {
    if (!weather) return null;
    try { return W.buildScoring(weather, weights, t9.rider, selectedDate || undefined); } catch (e) { console.error(e); return null; }
  }, [weather, weights, t9.rider, selectedDate]);

  const forecast = useMemo(() => {
    if (!weather) return [];
    try { return W.forecastDays(weather, weights, t9.rider); } catch { return []; }
  }, [weather, weights, t9.rider]);

  // Clothing + tips
  const clothingDay = useMemo(() => {
    if (!scoring) return [];
    const s = scoring.day.slice;
    return REC.clothing(t, {
      feelsAvg: avgArr(s.feels.length ? s.feels : s.temp),
      popMax: maxArr(s.pop),
      precipSum: sumArr(s.precip),
      windAvg: avgArr(s.wind),
      visibility: avgArr(s.visibility),
      uvMax: maxArr(s.uv),
      humidity: avgArr(s.humidity),
      isNight: false,
      riderProfile: t9.rider,
    });
  }, [scoring, t, t9.rider]);

  const tipsList = useMemo(() => {
    if (!scoring) return [];
    const sDay = scoring.day.slice;
    const sNight = scoring.night.slice;
    const moon = W.moonPhase(new Date());
    return REC.tips(t, {
      feelsAvg: avgArr(sDay.feels.length ? sDay.feels : sDay.temp),
      popMax: Math.max(maxArr(sDay.pop), maxArr(sNight.pop)),
      gustMax: Math.max(maxArr(sDay.gust), maxArr(sNight.gust)),
      windAvg: Math.max(avgArr(sDay.wind), avgArr(sNight.wind)),
      visibility: Math.min(avgArr(sDay.visibility) || 99999, avgArr(sNight.visibility) || 99999),
      uvMax: maxArr(sDay.uv),
      roadStatus: scoring.roadStatus,
      moonIllum: moon.illum,
      dayHours: scoring.daylightHours,
    });
  }, [scoring, t]);

  // Render
  return (
    <div className="app">
      {!location && <WelcomeModal t={t} lang={lang} onPick={(p) => setLocation(p)} />}

      <TopBar
        t={t}
        lang={lang}
        setLang={setLang}
        location={location}
        onChangeLocation={() => setLocation(null)}
        aesthetic={t9.aesthetic}
        dark={t9.dark}
        setAesthetic={(v) => setTweak("aesthetic", v)}
        setDark={(v) => setTweak("dark", v)}
      />

      {loading && !weather && (
        <div className="center-msg">
          <div className="spinner"></div>
          <span style={{ color: "var(--fg-mute)", fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.06em" }}>{t.loading}</span>
        </div>
      )}

      {error && !weather && (
        <div className="center-msg">
          <span style={{ color: "var(--bad)" }}>{error}</span>
          <button className="btn-secondary" onClick={() => setLocation({ ...location })}>{t.refresh}</button>
        </div>
      )}

      {scoring && weather && (
        <>
          <div className="score-stack">
            <ScorePanel kind="day" scoreData={scoring.day} sunrise={scoring.sunrise} sunset={scoring.sunset} target={scoring.target} isToday={scoring.isToday} t={t} lang={lang} isMobile={isMobile} />
            <ScorePanel kind="night" scoreData={scoring.night} sunrise={scoring.sunrise} sunset={scoring.sunset} target={scoring.target} isToday={scoring.isToday} t={t} lang={lang} isMobile={isMobile} />
          </div>

          <MetaRow scoring={scoring} t={t} lang={lang} source={weather.__source} />

          <ClothingSection items={clothingDay} t={t} />
          <TipsSection tips={tipsList} t={t} />
          <HourlyChart data={weather} scoring={scoring} t={t} lang={lang} />
          <ForecastSection days={forecast} t={t} lang={lang} selectedDate={scoring.target} onSelect={(d) => { setSelectedDate(d); window.scrollTo({ top: 0, behavior: "smooth" }); }} />

          <footer className="footer">{t.powered_by}</footer>
        </>
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection label={t.tw_aesthetic} />
        <TweakRadio
          label={t.tw_aesthetic}
          value={t9.aesthetic}
          options={[
            { value: "track", label: t.tw_track },
            { value: "touring", label: t.tw_touring },
            { value: "hud", label: t.tw_hud },
          ]}
          onChange={(v) => setTweak("aesthetic", v)}
        />
        <TweakToggle label={t.tw_dark} value={t9.dark} onChange={(v) => setTweak("dark", v)} />

        <TweakSection label={t.tw_rider} />
        <TweakRadio
          label={t.tw_rider}
          value={t9.rider}
          options={[
            { value: "commuter", label: t.tw_commuter },
            { value: "tourer", label: t.tw_tourer },
            { value: "hardcore", label: t.tw_hardcore },
          ]}
          onChange={(v) => setTweak("rider", v)}
        />

        <TweakSection label={t.tw_weights} />
        <TweakSlider label={t.tw_w_rain} value={t9.rainWeight} min={0.5} max={3.5} step={0.1} onChange={(v) => setTweak("rainWeight", v)} />
        <TweakSlider label={t.tw_w_wind} value={t9.windWeight} min={0.5} max={3} step={0.1} onChange={(v) => setTweak("windWeight", v)} />
        <TweakSlider label={t.tw_w_temp} value={t9.tempWeight} min={0.5} max={3} step={0.1} onChange={(v) => setTweak("tempWeight", v)} />
      </TweaksPanel>
    </div>
  );
}

// helpers
function avgArr(a) {
  if (!a || !a.length) return 0;
  return a.reduce((x, y) => x + (y || 0), 0) / a.length;
}
function maxArr(a) {
  if (!a || !a.length) return 0;
  return Math.max(...a.map(x => x || 0));
}
function sumArr(a) {
  if (!a || !a.length) return 0;
  return a.reduce((x, y) => x + (y || 0), 0);
}

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