// components/BigVideoCard.tsx
import React, {
  useEffect,
  useRef,
  useState,
  useCallback,
  useMemo,
} from "react";
import {
  View,
  Text,
  TouchableOpacity,
  StyleSheet,
  useWindowDimensions,
  Animated,
  PanResponder,
} from "react-native";
import { Image } from "expo-image";
import Video, { VideoRef } from "react-native-video";
import { Ionicons } from "@expo/vector-icons";
import { buildDashMpdUri } from "../utils/dash";
import { getPlayUrl, getVideoDetail } from "../services/bilibili";
import { coverImageUrl } from "../utils/imageUrl";
import { useSettingsStore } from "../store/settingsStore";
import { useTheme } from "../utils/theme";
import { useLiveStore } from "../store/liveStore";
import { formatCount, formatDuration } from "../utils/format";
import type { VideoItem } from "../services/types";

const HEADERS = {
  Referer: "https://www.bilibili.com",
  "User-Agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
};

const BAR_H = 3;
// Minimum horizontal distance (px) before treating the gesture as a seek
const SWIPE_THRESHOLD = 8;
// Full swipe across the screen = seek this many seconds
const SWIPE_SECONDS = 90;

function clamp(v: number, lo: number, hi: number) {
  return Math.max(lo, Math.min(hi, v));
}

interface Props {
  item: VideoItem;
  isVisible: boolean;
  isScrolling?: boolean;
  onPress: () => void;
}

export const BigVideoCard = React.memo(function BigVideoCard({
  item,
  isVisible,
  isScrolling,
  onPress,
}: Props) {
  const { width: SCREEN_W } = useWindowDimensions();
  const trafficSaving = useSettingsStore(s => s.trafficSaving);
  const liveActive = useLiveStore(s => s.isActive);
  const theme = useTheme();
  const THUMB_H = SCREEN_W * 0.5625;
  const mediaDimensions = { width: SCREEN_W - 8, height: THUMB_H };

  const [videoUrl, setVideoUrl] = useState<string | undefined>();
  const [isDash, setIsDash] = useState(false);
  const [paused, setPaused] = useState(true);
  const [muted, setMuted] = useState(true);
  const thumbOpacity = useRef(new Animated.Value(1)).current;

  const videoRef = useRef<VideoRef>(null);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [buffered, setBuffered] = useState(0);

  // Refs for PanResponder (avoid stale closures)
  const currentTimeRef = useRef(0);
  const durationRef = useRef(0);
  const seekingRef = useRef(false);
  const [seekLabel, setSeekLabel] = useState<string | null>(null);

  // Reset video state when the item changes
  useEffect(() => {
    setVideoUrl(undefined);
    setIsDash(false);
    setPaused(true);
    setMuted(true);
    setCurrentTime(0);
    setDuration(0);
    setBuffered(0);
    thumbOpacity.setValue(1);
  }, [item.bvid]);

  // Preload: fetch play URL on mount (before card is visible)
  useEffect(() => {
    if (videoUrl || trafficSaving || liveActive) return;
    let cancelled = false;
    (async () => {
      try {
        let cid = item.cid;
        if (!cid) {
          const detail = await getVideoDetail(item.bvid);
          cid = detail.cid ?? detail.pages?.[0]?.cid;
        }
        if (!cid) {
          console.warn('BigVideoCard: no cid available for', item.bvid);
          return;
        }
        if (cancelled) return;
        const playData = await getPlayUrl(item.bvid, cid, 16);
        if (cancelled) return;
        if (playData.dash) {
          if (!cancelled) setIsDash(true);
          try {
            const mpdUri = await buildDashMpdUri(playData, 16);
            if (!cancelled) setVideoUrl(mpdUri);
          } catch {
            if (!cancelled) setVideoUrl(playData.dash.video[0]?.baseUrl);
          }
        } else {
          if (!cancelled) setVideoUrl(playData.durl?.[0]?.url);
        }
      } catch (e) {
        console.warn("BigVideoCard: failed to load play URL", e);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [item.bvid]);

  // Pause/resume based on visibility and scroll state
  useEffect(() => {
    if (!videoUrl) return;
    if (!isVisible || trafficSaving || liveActive) {
      setPaused(true);
      setMuted(true);
      Animated.timing(thumbOpacity, {
        toValue: 1,
        duration: 150,
        useNativeDriver: true,
      }).start();
    } else if (isScrolling) {
      setPaused(true);
    } else {
      setPaused(false);
      Animated.timing(thumbOpacity, {
        toValue: 0,
        duration: 300,
        useNativeDriver: true,
      }).start();
    }
  }, [isVisible, isScrolling, videoUrl, trafficSaving, liveActive]);

  const handleVideoReady = () => {
    if (!isVisible || isScrolling || trafficSaving || liveActive) return;
    setPaused(false);
    Animated.timing(thumbOpacity, {
      toValue: 0,
      duration: 300,
      useNativeDriver: true,
    }).start();
  };

  // Keep refs in sync
  useEffect(() => {
    currentTimeRef.current = currentTime;
  }, [currentTime]);
  useEffect(() => {
    durationRef.current = duration;
  }, [duration]);

  // Horizontal swipe to seek
  const swipeStartTime = useRef(0);
  const screenWRef = useRef(SCREEN_W);
  useEffect(() => {
    screenWRef.current = SCREEN_W;
  }, [SCREEN_W]);

  const panResponder = useMemo(
    () =>
      PanResponder.create({
        onStartShouldSetPanResponderCapture: () => true,
        onMoveShouldSetPanResponder: (_, gs) =>
          Math.abs(gs.dx) > SWIPE_THRESHOLD &&
          Math.abs(gs.dx) > Math.abs(gs.dy) * 1.5,
        onPanResponderGrant: () => {
          seekingRef.current = true;
          swipeStartTime.current = currentTimeRef.current;
        },
        onMoveShouldSetPanResponderCapture: (_, gs) =>
          Math.abs(gs.dx) > SWIPE_THRESHOLD &&
          Math.abs(gs.dx) > Math.abs(gs.dy) * 1.5,
        onPanResponderMove: (_, gs) => {
          if (durationRef.current <= 0) return;
          const delta = (gs.dx / screenWRef.current) * SWIPE_SECONDS;
          const target = clamp(
            swipeStartTime.current + delta,
            0,
            durationRef.current,
          );
          setSeekLabel(formatDuration(Math.floor(target)));
        },
        onPanResponderRelease: (_, gs) => {
          if (durationRef.current > 0) {
            const delta = (gs.dx / screenWRef.current) * SWIPE_SECONDS;
            const target = clamp(
              swipeStartTime.current + delta,
              0,
              durationRef.current,
            );
            videoRef.current?.seek(target);
            setCurrentTime(target);
          }
          seekingRef.current = false;
          setSeekLabel(null);
        },
        onPanResponderTerminate: () => {
          seekingRef.current = false;
          setSeekLabel(null);
        },
      }),
    [],
  );

  const progressRatio = duration > 0 ? clamp(currentTime / duration, 0, 1) : 0;
  const bufferedRatio = duration > 0 ? clamp(buffered / duration, 0, 1) : 0;

  return (
    <View style={[styles.card, { backgroundColor: theme.card }]}>
      {/* Media area */}
      <View style={[mediaDimensions, { position: "relative" }]}>
        {/* Video player — rendered first so it sits behind the thumbnail */}
        {videoUrl && !liveActive && (
          <Video
            ref={videoRef}
            source={
              isDash
                ? { uri: videoUrl, type: "mpd", headers: HEADERS }
                : { uri: videoUrl, headers: HEADERS }
            }
            style={StyleSheet.absoluteFill}
            resizeMode="cover"
            muted={muted}
            paused={paused || seekingRef.current}
            repeat
            controls={false}
            onReadyForDisplay={handleVideoReady}
            onProgress={({
              currentTime: ct,
              seekableDuration: dur,
              playableDuration: buf,
            }) => {
              if (!seekingRef.current) setCurrentTime(ct);
              if (dur > 0) setDuration(dur);
              setBuffered(buf);
            }}
          />
        )}

        {/* Thumbnail — on top of Video, fades out once video is ready */}
        <Animated.View
          style={[StyleSheet.absoluteFill, { opacity: thumbOpacity }]}
          pointerEvents="none"
        >
          <Image
            source={{ uri: coverImageUrl(item.pic, trafficSaving ? 'normal' : 'hd') }}
            style={mediaDimensions}
            contentFit="cover"
            recyclingKey={item.bvid}
          />
        </Animated.View>

        {/* Swipe gesture layer */}
        <View
          style={[StyleSheet.absoluteFill, { zIndex: 5 }]}
          {...panResponder.panHandlers}
        />

        {/* Seek time label */}
        {seekLabel && (
          <View style={styles.seekBadge}>
            <Text style={styles.seekText}>
              {seekLabel} / {formatDuration(durationRef.current)}
            </Text>
          </View>
        )}

        <View style={styles.meta}>
          <Ionicons name="play" size={11} color="#fff" />
          <Text style={styles.metaText}>
            {formatCount(item.stat?.view ?? 0)}
          </Text>
        </View>

        <View style={styles.durationBadge}>
          <Text style={styles.durationText}>
            {formatDuration(item.duration)}
          </Text>
        </View>

        {/* Mute toggle — visible only when video is playing */}
        {videoUrl && !paused && (
          <TouchableOpacity
            style={styles.muteBtn}
            onPress={() => setMuted((m) => !m)}
            activeOpacity={0.7}
          >
            <Ionicons
              name={muted ? "volume-mute" : "volume-high"}
              size={16}
              color="#fff"
            />
          </TouchableOpacity>
        )}
      </View>

      {/* Progress bar between video and info — always rendered to avoid height jump */}
      <View style={styles.progressTrack}>
        {videoUrl && duration > 0 && (
          <>
            <View
              style={[
                styles.progressLayer,
                {
                  width: `${bufferedRatio * 100}%` as any,
                  backgroundColor: "rgba(0,174,236,0.25)",
                },
              ]}
            />
            <View
              style={[
                styles.progressLayer,
                {
                  width: `${progressRatio * 100}%` as any,
                  backgroundColor: "#00AEEC",
                },
              ]}
            />
          </>
        )}
      </View>
      {/* Info */}
      <TouchableOpacity onPress={onPress} activeOpacity={0.85}>
        <View style={styles.info}>
          <Text style={[styles.title, { color: theme.text }]} numberOfLines={2}>
            {item.title}
          </Text>

          <Text style={[styles.owner, { color: theme.textSub }]} numberOfLines={1}>
            {item.owner?.name ?? ""}
          </Text>
        </View>
      </TouchableOpacity>
    </View>
  );
});

const styles = StyleSheet.create({
  card: {
    marginHorizontal: 4,
    marginBottom: 6,
    backgroundColor: "#fff",
    borderRadius: 6,
    overflow: "hidden",
  },
  durationBadge: {
    position: "absolute",
    bottom: 4,
    right: 4,
    backgroundColor: "rgba(0,0,0,0.6)",
    borderRadius: 3,
    paddingHorizontal: 4,
    paddingVertical: 1,
    zIndex: 2,
  },
  durationText: { color: "#fff", fontSize: 10 },
  muteBtn: {
    position: "absolute",
    top: 8,
    right: 8,
    backgroundColor: "rgba(0,0,0,0.5)",
    borderRadius: 14,
    width: 28,
    height: 28,
    alignItems: "center",
    justifyContent: "center",
    zIndex: 6,
  },
  seekBadge: {
    position: "absolute",
    top: "40%",
    alignSelf: "center",
    backgroundColor: "rgba(0,0,0,0.6)",
    borderRadius: 6,
    paddingHorizontal: 12,
    paddingVertical: 6,
    zIndex: 4,
  },
  seekText: { color: "#fff", fontSize: 16, fontWeight: "600" },
  progressTrack: {
    height: BAR_H,
    backgroundColor: "rgba(0,0,0,0.08)",
    position: "relative",
  },
  progressLayer: {
    position: "absolute",
    top: 0,
    left: 0,
    height: BAR_H,
  },
  info: { padding: 8 },
  title: { fontSize: 14, color: "#212121", lineHeight: 18, marginBottom: 4 },
  meta: {
    position: "absolute",
    bottom: 4,
    left: 4,
    paddingHorizontal: 4,
    borderRadius: 5,
    backgroundColor: "rgba(0,0,0,0.6)",
    flexDirection: "row",
    alignItems: "center",
    paddingVertical: 0,
    gap: 2,
    zIndex: 2,
  },
  metaText: { fontSize: 10, color: "#fff" },
  owner: { fontSize: 11, color: "#999", marginTop: 2 },
});
