import { useCallback, useEffect, useMemo, useState } from "react";
import {
  cancelDownload, deleteDownload, downloadVideo, onRefreshProgress,
  openExternal, refreshFeeds,
} from "./api";
import Player from "./components/Player";
import Settings from "./components/Settings";
import Sidebar from "./components/Sidebar";
import TopBar from "./components/TopBar";
import VideoRow from "./components/VideoRow";
import { useConnectivity } from "./hooks/useConnectivity";
import { useDownloads } from "./hooks/useDownloads";
import { useFeed } from "./hooks/useFeed";
import type { FeedFilter, FeedItem, RefreshProgress } from "./types";

export default function App() {
  const [channelId, setChannelId] = useState<string | null>(null);
  const [search, setSearch] = useState("");
  const [downloadedOnly, setDownloadedOnly] = useState(false);
  const [hideShorts, setHideShorts] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [playing, setPlaying] = useState<{ item: FeedItem; path: string } | null>(null);
  const [refreshing, setRefreshing] = useState(false);
  const [refreshProgress, setRefreshProgress] = useState<RefreshProgress | null>(null);
  const [notice, setNotice] = useState<string | null>(null);

  const { online, reachable, forcedOffline, setForcedOffline, probe } = useConnectivity();

  // Offline, the only videos that can be played are the ones already on disk,
  // so the feed collapses to those regardless of the toggle.
  const effectiveDownloadedOnly = downloadedOnly || !online;

  const filter: FeedFilter = useMemo(
    () => ({
      channel_id: channelId,
      search: search.trim() || null,
      downloaded_only: effectiveDownloadedOnly,
      hide_shorts: hideShorts,
      limit: 500,
    }),
    [channelId, search, effectiveDownloadedOnly, hideShorts],
  );

  const { items, channels, loading, error, reload } = useFeed(filter);
  const live = useDownloads(reload);

  useEffect(() => {
    let un: (() => void) | undefined;
    onRefreshProgress(setRefreshProgress).then((u) => (un = u));
    return () => un?.();
  }, []);

  const totalVideos = useMemo(
    () => channels.reduce((n, c) => n + c.video_count, 0),
    [channels],
  );

  const doRefresh = useCallback(async () => {
    setRefreshing(true);
    setNotice(null);
    try {
      const s = await refreshFeeds();
      const failed = s.failures.length;
      setNotice(
        `Checked ${s.channels} channel${s.channels === 1 ? "" : "s"}` +
          (failed ? ` · ${failed} failed` : ""),
      );
      await reload();
    } catch (e) {
      setNotice(String(e));
    } finally {
      setRefreshing(false);
      setRefreshProgress(null);
    }
  }, [reload]);

  const openItem = useCallback(
    async (item: FeedItem) => {
      const path = live[item.id]?.path ?? item.path;
      const done = (live[item.id]?.state ?? item.state) === "done";
      if (done && path) {
        setPlaying({ item, path });
      } else if (online) {
        await openExternal(`https://www.youtube.com/watch?v=${item.id}`);
      } else {
        setNotice("That video isn't downloaded, and you're offline.");
      }
    },
    [live, online],
  );

  const emptyMessage = () => {
    if (loading) return "Loading…";
    if (channels.length === 0)
      return "No subscriptions yet. Open Settings and import your Takeout subscriptions.csv.";
    if (totalVideos === 0) return "Subscriptions imported. Hit Refresh to pull in their latest videos.";
    if (!online) return "You're offline, and nothing has been downloaded yet.";
    if (effectiveDownloadedOnly) return "No downloaded videos match this filter.";
    return "Nothing matches this filter.";
  };

  return (
    <div className="h-screen flex bg-ink text-white overflow-hidden">
      <Sidebar channels={channels} activeChannel={channelId} onSelect={setChannelId}
        onOpenSettings={() => setShowSettings(true)} totalVideos={totalVideos} />

      <div className="flex-1 min-w-0 flex flex-col">
        <TopBar
          search={search} onSearch={setSearch}
          downloadedOnly={effectiveDownloadedOnly} onDownloadedOnly={setDownloadedOnly}
          hideShorts={hideShorts} onHideShorts={setHideShorts}
          online={online} reachable={reachable} forcedOffline={forcedOffline}
          onToggleForcedOffline={() => { setForcedOffline(!forcedOffline); probe(); }}
          onRefresh={doRefresh} refreshing={refreshing} refreshProgress={refreshProgress}
        />

        {!online && (
          <div className="px-5 py-2 bg-amber-500/15 text-amber-200 text-xs border-b border-amber-500/25">
            Offline — showing only videos you've downloaded.
            {forcedOffline && " (Offline mode is forced on.)"}
          </div>
        )}

        {(notice || error) && (
          <div className="px-5 py-2 bg-surface text-xs text-muted border-b border-edge flex items-center justify-between gap-3">
            <span className="truncate">{error ?? notice}</span>
            <button onClick={() => setNotice(null)}
              className="shrink-0 hover:text-white cursor-pointer">
              Dismiss
            </button>
          </div>
        )}

        <main className="flex-1 overflow-y-auto px-3 py-3">
          {items.length === 0 ? (
            <div className="h-full grid place-items-center">
              <p className="text-muted text-sm max-w-md text-center leading-relaxed">
                {emptyMessage()}
              </p>
            </div>
          ) : (
            <div className="max-w-5xl mx-auto">
              {items.map((item) => (
                <VideoRow key={item.id} item={item} live={live[item.id]} online={online}
                  onOpen={() => openItem(item)}
                  onDownload={() => downloadVideo(item.id).catch((e) => setNotice(String(e)))}
                  onCancel={() => cancelDownload(item.id).catch((e) => setNotice(String(e)))}
                  onDelete={() =>
                    deleteDownload(item.id).then(reload).catch((e) => setNotice(String(e)))
                  } />
              ))}
            </div>
          )}
        </main>
      </div>

      {playing && (
        <Player item={playing.item} path={playing.path}
          onClose={() => setPlaying(null)}
          onDelete={async () => {
            await deleteDownload(playing.item.id);
            setPlaying(null);
            reload();
          }} />
      )}

      {showSettings && (
        <Settings onClose={() => setShowSettings(false)} onImported={() => reload()} />
      )}
    </div>
  );
}
