import React, {
  useState,
  useRef,
  useEffect,
  useCallback,
  forwardRef,
  useImperativeHandle,
} from "react";
import { formatDuration } from "../utils/format";
import {
  View,
  StyleSheet,
  TouchableOpacity,
  TouchableWithoutFeedback,
  Text,
  Modal,
  Image,
  PanResponder,
  useWindowDimensions,
} from "react-native";
import Video, { VideoRef } from "react-native-video";
import { LinearGradient } from "expo-linear-gradient";
import { Ionicons } from "@expo/vector-icons";
import type {
  PlayUrlResponse,
  VideoShotData,
  DanmakuItem,
} from "../services/types";
import { buildDashMpdUri } from "../utils/dash";
import { getVideoShot } from "../services/bilibili";
import DanmakuOverlay from "./DanmakuOverlay";

const BAR_H = 3;
// 进度球尺寸
const BALL = 12;
// 活跃状态下的拖动球增大尺寸，提升触控体验
const BALL_ACTIVE = 16;
const HIDE_DELAY = 3000;

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",
};

function clamp(v: number, lo: number, hi: number) {
  return Math.max(lo, Math.min(hi, v));
}
//
function findFrameByTime(index: number[], seekTime: number): number {
  let lo = 0,
    hi = index.length - 1;
  while (lo < hi) {
    const mid = (lo + hi + 1) >> 1;
    if (index[mid] <= seekTime) lo = mid;
    else hi = mid - 1;
  }
  return lo;
}

export interface NativeVideoPlayerRef {
  seek: (t: number) => void;
  setPaused: (v: boolean) => void;
}

interface Props {
  playData: PlayUrlResponse | null;
  qualities: { qn: number; desc: string }[];
  currentQn: number;
  onQualityChange: (qn: number) => void;
  onFullscreen: () => void;
  style?: object;
  bvid?: string;
  cid?: number;
  danmakus?: DanmakuItem[];
  isFullscreen?: boolean;
  onTimeUpdate?: (t: number) => void;
  initialTime?: number;
  forcePaused?: boolean;
}

export const NativeVideoPlayer = forwardRef<NativeVideoPlayerRef, Props>(
  function NativeVideoPlayer(
    {
      playData,
      qualities,
      currentQn,
      onQualityChange,
      onFullscreen,
      style,
      bvid,
      cid,
      danmakus,
      isFullscreen,
      onTimeUpdate,
      initialTime,
      forcePaused,
    }: Props,
    ref,
  ) {
    const { width: SCREEN_W, height: SCREEN_H } = useWindowDimensions();
    const VIDEO_H = SCREEN_W * 0.5625;

    const [resolvedUrl, setResolvedUrl] = useState<string | undefined>();
    const isDash = !!playData?.dash;

    const [showControls, setShowControls] = useState(true);
    const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

    const [paused, setPaused] = useState(false);
    const [currentTime, setCurrentTime] = useState(0);
    const currentTimeRef = useRef(0);
    const [duration, setDuration] = useState(0);
    const durationRef = useRef(0);
    const lastProgressUpdate = useRef(0);

    const [showQuality, setShowQuality] = useState(false);

    const [buffered, setBuffered] = useState(0);
    const [isSeeking, setIsSeeking] = useState(false);
    const isSeekingRef = useRef(false);
    const [touchX, setTouchX] = useState<number | null>(null);
    const touchXRef = useRef<number | null>(null);
    const rafRef = useRef<number | null>(null);
    const barOffsetX = useRef(0);
    const barWidthRef = useRef(300);
    const trackRef = useRef<View>(null);

    const [shots, setShots] = useState<VideoShotData | null>(null);
    const [showDanmaku, setShowDanmaku] = useState(true);

    const videoRef = useRef<VideoRef>(null);

    useImperativeHandle(ref, () => ({
      seek: (t: number) => {
        videoRef.current?.seek(t);
      },
      setPaused: (v: boolean) => {
        setPaused(v);
      },
    }));

    const currentDesc =
      qualities.find((q) => q.qn === currentQn)?.desc ??
      String(currentQn || "HD");

    // 解析播放链接，dash 需要构建 mpd uri，普通链接直接取第一个 durl。使用 useEffect 监听 playData 和 currentQn 变化，确保每次切换视频或清晰度时都能正确更新播放链接。错误处理逻辑保证即使 dash mpd 构建失败也能回退到普通链接，提升兼容性。
    useEffect(() => {
      if (!playData) {
        setResolvedUrl(undefined);
        return;
      }
      if (isDash) {
        buildDashMpdUri(playData, currentQn)
          .then(setResolvedUrl)
          .catch(() => setResolvedUrl(playData.dash!.video[0]?.baseUrl));
      } else {
        setResolvedUrl(playData.durl?.[0]?.url);
      }
    }, [playData, currentQn]);
    // 获取视频截图数据，供进度条预览使用。依赖 bvid 和 cid，确保在视频切换时重新获取截图。使用 cancelled 标志避免在组件卸载后更新状态，防止内存泄漏和潜在的错误。
    useEffect(() => {
      if (!bvid || !cid) return;
      let cancelled = false;
      getVideoShot(bvid, cid).then((shotData) => {
        if (cancelled) return;
        if (shotData?.image?.length) {
          setShots(shotData);
        }
      });
      return () => {
        cancelled = true;
      };
    }, [bvid, cid]);

    useEffect(() => {
      durationRef.current = duration;
    }, [duration]);

    // 控制栏自动隐藏逻辑：每次用户交互后重置计时器，3秒无交互则隐藏。使用 useRef 存储计时器 ID 和拖动状态，避免闭包问题导致的计时器失效或误触发。
    const resetHideTimer = useCallback(() => {
      if (hideTimer.current) clearTimeout(hideTimer.current);
      if (!isSeekingRef.current) {
        hideTimer.current = setTimeout(
          () => setShowControls(false),
          HIDE_DELAY,
        );
      }
    }, []);
    // 显示控制栏并重置隐藏计时器，确保用户每次交互后都有足够时间查看控制栏。依赖 resetHideTimer 保持稳定引用，避免不必要的重新渲染。
    const showAndReset = useCallback(() => {
      setShowControls(true);
      resetHideTimer();
    }, [resetHideTimer]);

    // 点击视频区域切换控制栏显示状态，显示时重置隐藏计时器，隐藏时直接隐藏。使用 useCallback 优化性能，避免不必要的函数重新创建。
    const handleTap = useCallback(() => {
      setShowControls((prev) => {
        if (!prev) {
          resetHideTimer();
          return true;
        }
        if (hideTimer.current) clearTimeout(hideTimer.current);
        return false;
      });
    }, [resetHideTimer]);

    // 组件卸载时清理隐藏计时器，避免内存泄漏和潜在的状态更新错误。依赖项为空数组确保只在挂载和卸载时执行一次。
    useEffect(() => {
      resetHideTimer();
      return () => {
        if (hideTimer.current) clearTimeout(hideTimer.current);
      };
    }, []);

    const measureTrack = useCallback(() => {
      trackRef.current?.measureInWindow((x, _y, w) => {
        if (w > 0) {
          barOffsetX.current = x;
          barWidthRef.current = w;
        }
      });
    }, []);
    //  使用 PanResponder 实现进度条拖动，支持在拖动过程中显示预览图。通过 touchXRef 和 rafRef 优化拖动性能，避免频繁更新状态导致的卡顿。用户松开拖动时，根据最终位置计算对应的时间点并跳转，同时清理状态和隐藏预览图。
    const panResponder = useRef(
      PanResponder.create({
        onStartShouldSetPanResponder: () => true,
        onMoveShouldSetPanResponder: () => true,
        onPanResponderGrant: (_, gs) => {
          isSeekingRef.current = true;
          setIsSeeking(true);
          setShowControls(true);
          if (hideTimer.current) clearTimeout(hideTimer.current);
          const x = clamp(gs.x0 - barOffsetX.current, 0, barWidthRef.current);
          touchXRef.current = x;
          setTouchX(x);
        },
        onPanResponderMove: (_, gs) => {
          touchXRef.current = clamp(
            gs.moveX - barOffsetX.current,
            0,
            barWidthRef.current,
          );
          if (!rafRef.current) {
            rafRef.current = requestAnimationFrame(() => {
              setTouchX(touchXRef.current);
              rafRef.current = null;
            });
          }
        },
        //  用户松开拖动，或拖动被中断（如来电），都视为结束拖动，需要清理状态和隐藏预览
        onPanResponderRelease: (_, gs) => {
          if (rafRef.current) {
            cancelAnimationFrame(rafRef.current);
            rafRef.current = null;
          }
          const ratio = clamp(
            (gs.moveX - barOffsetX.current) / barWidthRef.current,
            0,
            1,
          );
          const t = ratio * durationRef.current;
          videoRef.current?.seek(t);
          setCurrentTime(t);
          touchXRef.current = null;
          setTouchX(null);
          isSeekingRef.current = false;
          setIsSeeking(false);
          if (hideTimer.current) clearTimeout(hideTimer.current);
          hideTimer.current = setTimeout(
            () => setShowControls(false),
            HIDE_DELAY,
          );
        },
        onPanResponderTerminate: () => {
          if (rafRef.current) {
            cancelAnimationFrame(rafRef.current);
            rafRef.current = null;
          }
          touchXRef.current = null;
          setTouchX(null);
          isSeekingRef.current = false;
          setIsSeeking(false);
        },
      }),
    ).current;
    // 进度条上触摸位置对应的时间点比例，0-1。非拖动状态为 null
    const touchRatio =
      touchX !== null ? clamp(touchX / barWidthRef.current, 0, 1) : null;
    const progressRatio =
      duration > 0 ? clamp(currentTime / duration, 0, 1) : 0;
    const bufferedRatio = duration > 0 ? clamp(buffered / duration, 0, 1) : 0;

    const THUMB_DISPLAY_W = 120; // scaled display width

    const renderThumbnail = () => {
      if (touchRatio === null || !shots || !isSeeking) return null;
      const {
        img_x_size: TW,
        img_y_size: TH,
        img_x_len,
        img_y_len,
        image,
        index,
      } = shots;
      const framesPerSheet = img_x_len * img_y_len;
      const totalFrames = framesPerSheet * image.length;
      const seekTime = touchRatio * duration;
      // 通过时间戳索引找到最接近的帧，若无索引则均匀映射到总帧数上
      const frameIdx =
        index?.length && duration > 0
          ? clamp(findFrameByTime(index, seekTime), 0, index.length - 1)
          : clamp(
              Math.floor(touchRatio * (totalFrames - 1)),
              0,
              totalFrames - 1,
            );

      const sheetIdx = Math.floor(frameIdx / framesPerSheet);
      const local = frameIdx % framesPerSheet;
      const col = local % img_x_len;
      const row = Math.floor(local / img_x_len);
      //  根据单帧图尺寸和预设的显示宽度计算缩放后的显示尺寸，保持宽高比
      const scale = THUMB_DISPLAY_W / TW;
      const DW = THUMB_DISPLAY_W;
      const DH = Math.round(TH * scale);

      const trackLeft = barOffsetX.current;
      const absLeft = clamp(
        trackLeft + (touchX ?? 0) - DW / 2,
        0,
        SCREEN_W - DW,
      );
      // 兼容处理图床地址，确保以 http(s) 协议开头
      const sheetUrl = image[sheetIdx].startsWith("//")
        ? `https:${image[sheetIdx]}`
        : image[sheetIdx];
      return (
        <View
          style={[styles.thumbPreview, { left: absLeft, width: DW }]}
          pointerEvents="none"
        >
          <View
            style={{
              width: DW,
              height: DH,
              overflow: "hidden",
              borderRadius: 4,
            }}
          >
            <Image
              source={{ uri: sheetUrl, headers: HEADERS }}
              style={{
                position: "absolute",
                width: TW * img_x_len * scale,
                height: TH * img_y_len * scale,
                left: -col * DW,
                top: -row * DH,
              }}
            />
          </View>
          <Text style={styles.thumbTime}>
            {formatDuration(Math.floor(seekTime))}
          </Text>
        </View>
      );
    };

    return (
      <View
        style={[
          isFullscreen
            ? styles.fsContainer
            : [styles.container, { width: SCREEN_W, height: VIDEO_H }],
          style,
        ]}
      >
        {resolvedUrl ? (
          <Video
            key={resolvedUrl}
            ref={videoRef}
            source={
              isDash
                ? { uri: resolvedUrl, type: "mpd", headers: HEADERS }
                : { uri: resolvedUrl, headers: HEADERS }
            }
            style={StyleSheet.absoluteFill}
            resizeMode="contain"
            controls={false}
            paused={!!(forcePaused || paused)}
            progressUpdateInterval={500}
            onProgress={({
              currentTime: ct,
              seekableDuration: dur,
              playableDuration: buf,
            }) => {
              currentTimeRef.current = ct;
              onTimeUpdate?.(ct);
              // 拖动进度条时跳过 UI 更新，避免与用户拖动冲突
              if (isSeekingRef.current) return;
              const now = Date.now();
              if (now - lastProgressUpdate.current < 450) return;
              lastProgressUpdate.current = now;
              setCurrentTime(ct);
              if (dur > 0 && Math.abs(dur - durationRef.current) > 1) setDuration(dur);
              setBuffered(buf);
            }}
            onLoad={() => {
              if (initialTime && initialTime > 0) {
                videoRef.current?.seek(initialTime);
              }
              // seek 后部分播放器不恢复播放，先暂停再恢复，强制触发 prop 变化
              if (!forcePaused) {
                setPaused(true);
                requestAnimationFrame(() => setPaused(false));
              }
            }}
            onError={(e) => {
              // 杜比视界播放失败时自动降级到 1080P
              if (currentQn === 126) {
                onQualityChange(80);
                return;
              }
              console.warn("Video playback error:", e);
            }}
          />
        ) : (
          <View style={styles.placeholder} />
        )}

        {isFullscreen && !!danmakus?.length && (
          <DanmakuOverlay
            danmakus={danmakus}
            currentTime={currentTime}
            screenWidth={SCREEN_W}
            screenHeight={SCREEN_H}
            visible={showDanmaku}
          />
        )}

        <TouchableWithoutFeedback onPress={handleTap}>
          <View style={StyleSheet.absoluteFill} />
        </TouchableWithoutFeedback>

        {showControls && (
          <>
            {/*  小窗口 */}
            <LinearGradient
              colors={["rgba(0,0,0,0.55)", "transparent"]}
              style={styles.topBar}
              pointerEvents="box-none"
            ></LinearGradient>

            <TouchableOpacity
              style={styles.centerBtn}
              onPress={() => {
                setPaused((p) => !p);
                showAndReset();
              }}
            >
              <View style={styles.centerBtnBg}>
                <Ionicons
                  name={paused ? "play" : "pause"}
                  size={28}
                  color="#fff"
                />
              </View>
            </TouchableOpacity>

            <LinearGradient
              colors={["transparent", "rgba(0,0,0,0.7)"]}
              style={styles.bottomBar}
              pointerEvents="box-none"
            >
              <View
                ref={trackRef}
                style={styles.trackWrapper}
                onLayout={measureTrack}
                {...panResponder.panHandlers}
              >
                <View style={styles.track}>
                  <View
                    style={[
                      styles.trackLayer,
                      {
                        width: `${bufferedRatio * 100}%` as any,
                        backgroundColor: "rgba(255,255,255,0.35)",
                      },
                    ]}
                  />
                  <View
                    style={[
                      styles.trackLayer,
                      {
                        width: `${progressRatio * 100}%` as any,
                        backgroundColor: "#00AEEC",
                      },
                    ]}
                  />
                </View>
                {isSeeking && touchX !== null ? (
                  <View
                    style={[
                      styles.ball,
                      styles.ballActive,
                      { left: touchX - BALL_ACTIVE / 2 },
                    ]}
                  />
                ) : (
                  <View
                    style={[
                      styles.ball,
                      { left: progressRatio * barWidthRef.current - BALL / 2 },
                    ]}
                  />
                )}
              </View>
              {/* Controls */}

              <View style={styles.ctrlRow}>
                <TouchableOpacity
                  onPress={() => {
                    setPaused((p) => !p);
                    showAndReset();
                  }}
                  style={styles.ctrlBtn}
                >
                  <Ionicons
                    name={paused ? "play" : "pause"}
                    size={16}
                    color="#fff"
                  />
                </TouchableOpacity>
                <Text style={styles.timeText}>
                  {formatDuration(Math.floor(currentTime))}
                </Text>
                <View style={{ flex: 1 }} />
                <Text style={styles.timeText}>{formatDuration(duration)}</Text>
                <TouchableOpacity
                  style={styles.ctrlBtn}
                  onPress={() => setShowQuality(true)}
                >
                  <Text style={styles.qualityText}>{currentDesc}</Text>
                </TouchableOpacity>
                {isFullscreen && (
                  <TouchableOpacity
                    style={styles.ctrlBtn}
                    onPress={() => setShowDanmaku((v) => !v)}
                  >
                    <Ionicons
                      name={showDanmaku ? "chatbubbles" : "chatbubbles-outline"}
                      size={16}
                      color="#fff"
                    />
                  </TouchableOpacity>
                )}
                <TouchableOpacity style={styles.ctrlBtn} onPress={onFullscreen}>
                  <Ionicons name="expand" size={18} color="#fff" />
                </TouchableOpacity>
              </View>
            </LinearGradient>
          </>
        )}

        {renderThumbnail()}
        {/* 选清晰度 */}
        <Modal visible={showQuality} transparent animationType="fade">
          <TouchableOpacity
            style={styles.modalOverlay}
            onPress={() => setShowQuality(false)}
          >
            <View style={styles.qualityList}>
              <Text style={styles.qualityTitle}>选择清晰度</Text>
              {qualities.map((q) => (
                <TouchableOpacity
                  key={q.qn}
                  style={styles.qualityItem}
                  onPress={() => {
                    setShowQuality(false);
                    onQualityChange(q.qn);
                    showAndReset();
                  }}
                >
                  <Text
                    style={[
                      styles.qualityItemText,
                      q.qn === currentQn && styles.qualityItemActive,
                    ]}
                  >
                    {q.desc}
                    {q.qn === 126 ? " DV" : ""}
                  </Text>
                  {q.qn === currentQn && (
                    <Ionicons name="checkmark" size={16} color="#00AEEC" />
                  )}
                </TouchableOpacity>
              ))}
            </View>
          </TouchableOpacity>
        </Modal>
      </View>
    );
  },
);

const styles = StyleSheet.create({
  container: { backgroundColor: "#000" },
  fsContainer: { flex: 1, backgroundColor: "#000" },
  placeholder: { ...StyleSheet.absoluteFillObject, backgroundColor: "#000" },
  topBar: {
    position: "absolute",
    top: 0,
    left: 0,
    right: 0,
    height: 56,
    paddingHorizontal: 12,
    paddingTop: 10,
    flexDirection: "row",
    justifyContent: "flex-end",
  },
  topBtn: { padding: 6 },
  centerBtn: {
    position: "absolute",
    top: "50%",
    left: "50%",
    transform: [{ translateX: -28 }, { translateY: -28 }],
  },
  centerBtnBg: {
    width: 56,
    height: 56,
    borderRadius: 28,
    backgroundColor: "rgba(0,0,0,0.45)",
    alignItems: "center",
    justifyContent: "center",
  },
  bottomBar: {
    position: "absolute",
    bottom: 0,
    left: 0,
    right: 0,
    paddingBottom: 8,
    paddingTop: 32,
  },
  thumbPreview: { position: "absolute", bottom: 64, alignItems: "center" },
  thumbTime: {
    color: "#fff",
    fontSize: 11,
    fontWeight: "600",
    marginTop: 2,
    textShadowColor: "rgba(0,0,0,0.7)",
    textShadowOffset: { width: 0, height: 1 },
    textShadowRadius: 2,
  },
  trackWrapper: {
    marginHorizontal: 8,
    height: BAR_H + BALL_ACTIVE,
    justifyContent: "center",
    position: "relative",
  },
  track: {
    height: BAR_H,
    borderRadius: 2,
    overflow: "hidden",
    backgroundColor: "rgba(255,255,255,0.2)",
  },
  trackLayer: {
    position: "absolute",
    top: 0,
    left: 0,
    height: BAR_H,
  },
  ball: {
    position: "absolute",
    top: (BAR_H + BALL_ACTIVE) / 2 - BALL / 2,
    width: BALL,
    height: BALL,
    borderRadius: BALL / 2,
    backgroundColor: "#fff",
    elevation: 3,
  },
  ballActive: {
    width: BALL_ACTIVE,
    height: BALL_ACTIVE,
    borderRadius: BALL_ACTIVE / 2,
    backgroundColor: "#00AEEC",
    top: 0,
  },
  ctrlRow: {
    flexDirection: "row",
    alignItems: "center",
    paddingHorizontal: 8,
    marginTop: 4,
  },
  ctrlBtn: { paddingHorizontal: 8, paddingVertical: 4 },
  timeText: {
    color: "#fff",
    fontSize: 11,
    marginHorizontal: 2,
    fontWeight: "600",
  },
  qualityText: { color: "#fff", fontSize: 11, fontWeight: "600" },
  modalOverlay: {
    flex: 1,
    backgroundColor: "rgba(0,0,0,0.5)",
    justifyContent: "center",
    alignItems: "center",
  },
  qualityList: {
    backgroundColor: "#fff",
    borderRadius: 12,
    paddingVertical: 8,
    paddingHorizontal: 16,
    minWidth: 180,
  },
  qualityTitle: {
    fontSize: 15,
    fontWeight: "700",
    color: "#212121",
    paddingVertical: 10,
    textAlign: "center",
  },
  qualityItem: {
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    paddingVertical: 12,
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: "#eee",
  },
  qualityItemText: { fontSize: 14, color: "#333" },
  qualityItemActive: { color: "#00AEEC", fontWeight: "700" },
});
