import React, { useState, useRef, useEffect, useCallback } 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;
}

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

export function NativeVideoPlayer({
  playData,
  qualities,
  currentQn,
  onQualityChange,
  onFullscreen,
  onMiniPlayer,
  style,
  bvid,
  cid,
  danmakus,
  isFullscreen,
  onTimeUpdate,
  initialTime,
}: Props) {
  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 [duration, setDuration] = useState(0);
  const durationRef = 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);
  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);
    console.log("[thumb]", {
      seekTime,
      duration,
      indexLen: index?.length,
      frameIdx,
      totalFrames,
      sheetIdx,
      col,
      row,
    });
    //  根据单帧图尺寸和预设的显示宽度计算缩放后的显示尺寸，保持宽高比
    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={paused}
          onProgress={({
            currentTime: ct,
            seekableDuration: dur,
            playableDuration: buf,
          }) => {
            setCurrentTime(ct);
            if (dur > 0) setDuration(dur);
            setBuffered(buf);
            onTimeUpdate?.(ct);
          }}
          onLoad={() => {
            if (initialTime && initialTime > 0) {
              videoRef.current?.seek(initialTime);
            }
          }}
        />
      ) : (
        <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"
          >
            {onMiniPlayer && (
              <TouchableOpacity onPress={onMiniPlayer} style={styles.topBtn}>
                <Ionicons
                  name="tablet-portrait-outline"
                  size={20}
                  color="#fff"
                />
              </TouchableOpacity>
            )}
          </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={16} 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}
                </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 },
  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" },
});
