import React, { useCallback, useEffect, useMemo, useState } from "react";
import {
  ActivityIndicator,
  FlatList,
  Image,
  RefreshControl,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { router } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
import { authFetch } from "../lib/apiFetch";
import { API_BASE_URL } from "../config";
import { useMiniPlayerInset } from "../hooks/useMiniPlayerInset";
import { formatBytes } from "../utils/format";
import { useMedia } from "../context/MediaContext";

interface HistoryItem {
  id: number;
  videoId: string | null;
  title: string | null;
  channel: string | null;
  thumbnail: string | null;
  webpageUrl: string | null;
  formatId: string;
  type: "video" | "audio";
  durationSeconds: number | null;
  fileSize: number | null;
  createdAt: string;
}

const PAGE_SIZE = 20;

/** "audio-320" -> "MP3 320kbps", "video-1080p" -> "1080p", "video-best" -> "Best quality" — mirrors server's format-id shape (VIDEO_FORMAT_RE/AUDIO_FORMAT_RE in api.js). Falls back to the raw id for anything unrecognized. */
function describeFormatId(formatId: string): string {
  const audioMatch = formatId.match(/^audio-(\d+)$/);
  if (audioMatch) return `MP3 ${audioMatch[1]}kbps`;
  const videoMatch = formatId.match(/^video-(best|\d+p)$/);
  if (videoMatch) return videoMatch[1] === "best" ? "Best quality" : videoMatch[1];
  return formatId;
}

function formatDuration(seconds?: number | null) {
  if (!seconds && seconds !== 0) return null;
  const m = Math.floor(seconds / 60);
  const s = Math.floor(seconds % 60);
  return `${m}:${s.toString().padStart(2, "0")}`;
}

function formatDate(iso: string) {
  return new Date(iso).toLocaleString(undefined, {
    month: "short",
    day: "numeric",
    year: "numeric",
    hour: "numeric",
    minute: "2-digit",
  });
}

/** Correlates a history row to an on-device MediaItem/PendingDownload — both carry webpageUrl + type, but neither carries the server's videoId, so this is the only stable key available client-side. */
function deviceKey(webpageUrl: string | null | undefined, type: string) {
  return webpageUrl ? `${webpageUrl}|${type}` : null;
}

export default function HistoryScreen() {
  const [items, setItems] = useState<HistoryItem[]>([]);
  const [nextCursor, setNextCursor] = useState<number | null>(null);
  const [initialLoading, setInitialLoading] = useState(true);
  const [loadingMore, setLoadingMore] = useState(false);
  const [refreshing, setRefreshing] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const miniPlayerInset = useMiniPlayerInset();
  const { downloads, pendingDownloads, searchAndDownload } = useMedia();

  const onDeviceKeys = useMemo(
    () => new Set(downloads.map((d) => deviceKey(d.webpageUrl, d.type)).filter(Boolean)),
    [downloads],
  );
  const pendingKeys = useMemo(
    () => new Set(pendingDownloads.map((p) => deviceKey(p.webpageUrl, p.type)).filter(Boolean)),
    [pendingDownloads],
  );

  const fetchPage = useCallback(async (cursor?: number) => {
    const url =
      cursor != null
        ? `${API_BASE_URL}/history?limit=${PAGE_SIZE}&cursor=${cursor}`
        : `${API_BASE_URL}/history?limit=${PAGE_SIZE}`;
    const res = await authFetch(url);
    if (!res.ok) throw new Error("Failed to load history.");
    return (await res.json()) as { items: HistoryItem[]; nextCursor: number | null };
  }, []);

  const loadFirstPage = useCallback(async () => {
    setError(null);
    try {
      const data = await fetchPage();
      setItems(data.items);
      setNextCursor(data.nextCursor);
    } catch {
      setError("Couldn't load history. Pull down to try again.");
    }
  }, [fetchPage]);

  useEffect(() => {
    setInitialLoading(true);
    loadFirstPage().finally(() => setInitialLoading(false));
  }, [loadFirstPage]);

  const handleRefresh = async () => {
    setRefreshing(true);
    await loadFirstPage();
    setRefreshing(false);
  };

  const handleLoadMore = async () => {
    if (loadingMore || initialLoading || nextCursor == null) return;
    setLoadingMore(true);
    try {
      const data = await fetchPage(nextCursor);
      setItems((prev) => [...prev, ...data.items]);
      setNextCursor(data.nextCursor);
    } catch {
      // Leave nextCursor as-is — scrolling back to the bottom retries.
    } finally {
      setLoadingMore(false);
    }
  };

  const handleRedownload = (item: HistoryItem) => {
    if (!item.webpageUrl) return;
    searchAndDownload(item.webpageUrl, item.formatId, {
      title: item.title ?? undefined,
      thumbnail: item.thumbnail,
      channel: item.channel,
      webpageUrl: item.webpageUrl,
      durationSeconds: item.durationSeconds,
    });
  };

  const renderItem = ({ item }: { item: HistoryItem }) => {
    const key = deviceKey(item.webpageUrl, item.type);
    const isOnDevice = key ? onDeviceKeys.has(key) : false;
    const isPending = key ? pendingKeys.has(key) : false;

    return (
      <View style={styles.row}>
        {item.thumbnail ? (
          <Image source={{ uri: item.thumbnail }} style={styles.thumb} />
        ) : (
          <View style={[styles.thumb, styles.thumbFallback]}>
            <Ionicons
              name={item.type === "video" ? "film-outline" : "musical-notes-outline"}
              size={20}
              color="#1DB954"
            />
          </View>
        )}
        <View style={{ marginLeft: 10, flex: 1 }}>
          <Text style={styles.title} numberOfLines={1}>
            {item.title || "Untitled"}
          </Text>
          <Text style={styles.subtitle} numberOfLines={1}>
            {[item.channel, formatDuration(item.durationSeconds), describeFormatId(item.formatId)]
              .filter(Boolean)
              .join(" • ")}
          </Text>
          <Text style={styles.meta} numberOfLines={1}>
            {formatDate(item.createdAt)}
            {item.fileSize ? ` • ${formatBytes(item.fileSize)}` : ""}
          </Text>
        </View>

        {isPending ? (
          <ActivityIndicator size="small" color="#1DB954" style={{ marginLeft: 8 }} />
        ) : isOnDevice ? (
          <Ionicons
            name="checkmark-circle"
            size={22}
            color="#1DB954"
            style={{ marginLeft: 8 }}
          />
        ) : (
          item.webpageUrl && (
            <TouchableOpacity
              style={styles.redownloadBtn}
              onPress={() => handleRedownload(item)}
              hitSlop={8}
            >
              <Ionicons name="cloud-download-outline" size={22} color="#1DB954" />
            </TouchableOpacity>
          )
        )}
      </View>
    );
  };

  return (
    <SafeAreaView style={styles.container} edges={["top", "left", "right"]}>
      <View style={styles.header}>
        <TouchableOpacity onPress={() => router.back()}>
          <Ionicons name="chevron-back" size={26} color="#FFF" />
        </TouchableOpacity>
        <Text style={styles.headerTitle}>History</Text>
        <View style={{ width: 26 }} />
      </View>

      {initialLoading ? (
        <View style={styles.centerFill}>
          <ActivityIndicator color="#1DB954" size="large" />
        </View>
      ) : error && items.length === 0 ? (
        <View style={styles.centerFill}>
          <Ionicons name="alert-circle-outline" size={32} color="#666" />
          <Text style={styles.emptyText}>{error}</Text>
        </View>
      ) : items.length === 0 ? (
        <View style={styles.centerFill}>
          <Ionicons name="time-outline" size={32} color="#666" />
          <Text style={styles.emptyText}>No downloads yet.</Text>
        </View>
      ) : (
        <FlatList
          data={items}
          keyExtractor={(item) => String(item.id)}
          renderItem={renderItem}
          contentContainerStyle={{ padding: 16, paddingBottom: 16 + miniPlayerInset }}
          refreshControl={
            <RefreshControl refreshing={refreshing} onRefresh={handleRefresh} tintColor="#1DB954" />
          }
          onEndReachedThreshold={0.4}
          onEndReached={handleLoadMore}
          ListFooterComponent={
            loadingMore ? (
              <ActivityIndicator color="#1DB954" style={{ marginVertical: 16 }} />
            ) : null
          }
        />
      )}
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: "#121212" },
  header: {
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    paddingHorizontal: 16,
    paddingVertical: 12,
  },
  headerTitle: { color: "#FFF", fontSize: 17, fontWeight: "bold" },
  centerFill: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
  emptyText: { color: "#888", fontSize: 14, marginTop: 10, textAlign: "center" },
  row: {
    flexDirection: "row",
    alignItems: "center",
    backgroundColor: "#1E1E1E",
    padding: 10,
    borderRadius: 8,
    marginBottom: 10,
  },
  redownloadBtn: { marginLeft: 8, padding: 4 },
  thumb: { width: 64, height: 40, borderRadius: 4 },
  thumbFallback: { backgroundColor: "#2A2A2A", alignItems: "center", justifyContent: "center" },
  title: { color: "#FFF", fontSize: 14, fontWeight: "600" },
  subtitle: { color: "#AAA", fontSize: 12, marginTop: 2 },
  meta: { color: "#666", fontSize: 11, marginTop: 2 },
});
