import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Button, Modal, Slider, Spin, Typography } from 'antd';
import { PauseCircleFilled, PlayCircleFilled } from '@ant-design/icons';

const { Text } = Typography;

const MIN_TRIM_SECONDS = 2;
const MAX_TRIM_SECONDS = 15;
const DEFAULT_TRIM_SECONDS = 7;
const FRAME_WIDTH = 160;
const FRAME_HEIGHT = 90;

type VideoTrimPickerProps = {
  open: boolean;
  videoUrl: string;
  title?: string;
  loading?: boolean;
  minDuration?: number;
  maxDuration?: number;
  onCancel: () => void;
  onConfirm: (range: { startSecond: number; endSecond: number; durationSecond: number }) => void | Promise<void>;
};

type FrameItem = {
  second: number;
  captureSecond?: number;
  image: string;
  status: 'loading' | 'success' | 'failed';
  fallback?: boolean;
};

function pad2(value: number): string {
  return String(value).padStart(2, '0');
}

function toIntegerSecond(value: number): number {
  if (!Number.isFinite(value)) return 0;
  return Math.max(0, Math.round(value));
}

function formatTime(secondValue: number): string {
  const total = toIntegerSecond(secondValue);
  const minutes = Math.floor(total / 60);
  const seconds = total % 60;
  return `${pad2(minutes)}:${pad2(seconds)}`;
}

function clamp(value: number, min: number, max: number): number {
  return Math.min(Math.max(value, min), max);
}

function sameRange(a: [number, number], b: [number, number]): boolean {
  return a[0] === b[0] && a[1] === b[1];
}

function waitForEvent(target: EventTarget, eventName: string, timeout = 8000): Promise<void> {
  return new Promise((resolve, reject) => {
    let timer: number | undefined;

    const cleanup = () => {
      if (timer) window.clearTimeout(timer);
      target.removeEventListener(eventName, onOk);
      target.removeEventListener('error', onError);
    };

    const onOk = () => {
      cleanup();
      resolve();
    };

    const onError = () => {
      cleanup();
      reject(new Error(`视频${eventName}失败`));
    };

    target.addEventListener(eventName, onOk, { once: true });
    target.addEventListener('error', onError, { once: true });
    timer = window.setTimeout(() => {
      cleanup();
      reject(new Error(`视频${eventName}超时`));
    }, timeout);
  });
}

function waitNextFrame(): Promise<void> {
  return new Promise((resolve) => {
    window.requestAnimationFrame(() => {
      window.requestAnimationFrame(() => resolve());
    });
  });
}

async function seekVideo(video: HTMLVideoElement, targetSecond: number): Promise<void> {
  const safeTarget = Math.max(0, targetSecond);
  if (Math.abs(video.currentTime - safeTarget) > 0.01) {
    const seeked = waitForEvent(video, 'seeked');
    video.currentTime = safeTarget;
    await seeked;
  }
  await waitNextFrame();
}

function isMostlyBlackFrame(ctx: CanvasRenderingContext2D, width: number, height: number): boolean {
  let data: Uint8ClampedArray;
  try {
    data = ctx.getImageData(0, 0, width, height).data;
  } catch {
    return false;
  }

  let sampled = 0;
  let dark = 0;
  const pixelStride = 8;
  for (let y = 0; y < height; y += pixelStride) {
    for (let x = 0; x < width; x += pixelStride) {
      const index = (y * width + x) * 4;
      const r = data[index];
      const g = data[index + 1];
      const b = data[index + 2];
      const a = data[index + 3];
      if (a < 20) continue;
      const luma = 0.299 * r + 0.587 * g + 0.114 * b;
      sampled += 1;
      if (luma < 18) dark += 1;
    }
  }

  return sampled > 0 && dark / sampled >= 0.85;
}

async function captureOneFrame(
  video: HTMLVideoElement,
  ctx: CanvasRenderingContext2D,
  canvas: HTMLCanvasElement,
  displaySecond: number,
  realDuration: number,
): Promise<FrameItem> {
  const lastSafeSecond = Math.max(0, realDuration - 0.05);
  const candidates = Array.from(
    new Set(
      [displaySecond, displaySecond + 0.2, displaySecond + 0.5, displaySecond + 1, displaySecond + 2]
        .map((value) => Math.min(value, lastSafeSecond))
        .filter((value) => value >= 0 && value <= lastSafeSecond),
    ),
  );

  for (const captureSecond of candidates) {
    try {
      await seekVideo(video, captureSecond);
      ctx.clearRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT);
      ctx.drawImage(video, 0, 0, FRAME_WIDTH, FRAME_HEIGHT);

      if (isMostlyBlackFrame(ctx, FRAME_WIDTH, FRAME_HEIGHT)) {
        continue;
      }

      return {
        second: displaySecond,
        captureSecond,
        image: canvas.toDataURL('image/jpeg', 0.76),
        status: 'success',
        fallback: Math.abs(captureSecond - displaySecond) > 0.01,
      };
    } catch {
      // 当前候选时间失败时继续尝试后面的候选时间。
    }
  }

  return {
    second: displaySecond,
    image: '',
    status: 'failed',
  };
}

function normalizeRange(
  rawRange: [number, number],
  prevRange: [number, number],
  integerDuration: number,
  minDuration: number,
  maxDuration: number,
): [number, number] {
  if (!integerDuration || integerDuration <= 0) {
    return [0, 0];
  }

  const maxSelectableDuration = Math.min(maxDuration, integerDuration);
  const minSelectableDuration = Math.min(minDuration, maxSelectableDuration);

  let [start, end] = rawRange;
  start = toIntegerSecond(clamp(start, 0, integerDuration));
  end = toIntegerSecond(clamp(end, 0, integerDuration));

  if (end < start) {
    [start, end] = [end, start];
  }

  const movedStart = Math.abs(start - prevRange[0]) >= Math.abs(end - prevRange[1]);
  let selectedDuration = end - start;

  if (selectedDuration < minSelectableDuration) {
    if (movedStart) {
      start = toIntegerSecond(clamp(end - minSelectableDuration, 0, Math.max(0, integerDuration - minSelectableDuration)));
      end = start + minSelectableDuration;
    } else {
      end = toIntegerSecond(clamp(start + minSelectableDuration, minSelectableDuration, integerDuration));
      start = end - minSelectableDuration;
    }
  }

  selectedDuration = end - start;
  if (selectedDuration > maxSelectableDuration) {
    if (movedStart) {
      start = toIntegerSecond(clamp(end - maxSelectableDuration, 0, Math.max(0, integerDuration - maxSelectableDuration)));
    } else {
      end = toIntegerSecond(clamp(start + maxSelectableDuration, maxSelectableDuration, integerDuration));
    }
  }

  start = toIntegerSecond(clamp(start, 0, integerDuration));
  end = toIntegerSecond(clamp(end, start, integerDuration));

  return [start, end];
}

function buildInitialRange(integerDuration: number, minDuration: number, maxDuration: number): [number, number] {
  if (!integerDuration || integerDuration <= 0) return [0, minDuration];
  const initialEnd = Math.min(integerDuration, Math.max(minDuration, Math.min(DEFAULT_TRIM_SECONDS, maxDuration)));
  return [0, initialEnd];
}

const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
  open,
  videoUrl,
  title = '手动拆镜',
  loading = false,
  minDuration = MIN_TRIM_SECONDS,
  maxDuration = MAX_TRIM_SECONDS,
  onCancel,
  onConfirm,
}) => {
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const abortRef = useRef(false);
  const rangeRef = useRef<[number, number]>([0, minDuration]);
  const currentTimeRef = useRef(0);
  const frameContainerRef = useRef<HTMLDivElement>(null);

  const [duration, setDuration] = useState(0);
  const [currentTime, setCurrentTime] = useState(0);
  const [range, setRangeState] = useState<[number, number]>([0, minDuration]);
  const [frames, setFrames] = useState<FrameItem[]>([]);
  const [frameLoading, setFrameLoading] = useState(false);
  const [playing, setPlaying] = useState(false);
  const [localError, setLocalError] = useState('');

  const integerDuration = useMemo(() => Math.max(0, Math.floor(duration || 0)), [duration]);
  const selectedDuration = useMemo(() => Math.max(0, range[1] - range[0]), [range]);
  const disabledByDuration = integerDuration > 0 && integerDuration < minDuration;

  const setRange = useCallback((next: [number, number] | ((prev: [number, number]) => [number, number])) => {
    setRangeState((prev) => {
      const resolved = typeof next === 'function' ? next(prev) : next;
      if (sameRange(prev, resolved)) return prev;
      rangeRef.current = resolved;
      return resolved;
    });
  }, []);

  const setDisplayedTime = useCallback((second: number) => {
    const next = toIntegerSecond(second);
    if (currentTimeRef.current === next) return;
    currentTimeRef.current = next;
    setCurrentTime(next);
  }, []);

  const seekPreview = useCallback((second: number) => {
    const video = videoRef.current;
    if (!video || !integerDuration) return;
    const next = toIntegerSecond(clamp(second, 0, integerDuration));
    try {
      video.currentTime = next;
    } catch {
      // ignore seek error
    }
    setDisplayedTime(next);
  }, [integerDuration, setDisplayedTime]);

  useEffect(() => {
    if (!open) {
      abortRef.current = true;
      setPlaying(false);
      setFrames([]);
      setFrameLoading(false);
      setDuration(0);
      setDisplayedTime(0);
      setLocalError('');
      setRange([0, minDuration]);
      if (videoRef.current) {
        videoRef.current.pause();
      }
      return;
    }

    abortRef.current = false;
    setPlaying(false);
    setFrameLoading(true);
    setFrames([]);
    setDuration(0);
    setDisplayedTime(0);
    setLocalError('');
    setRange([0, minDuration]);

    const extractor = document.createElement('video');
    extractor.crossOrigin = 'anonymous';
    extractor.muted = true;
    extractor.playsInline = true;
    extractor.preload = 'auto';
    extractor.src = videoUrl;

    const buildFrames = async () => {
      try {
        await waitForEvent(extractor, 'loadedmetadata');
        await waitForEvent(extractor, 'loadeddata').catch(() => undefined);
        if (abortRef.current) return;

        const realDuration = Number.isFinite(extractor.duration) ? extractor.duration : 0;
        const nextIntegerDuration = Math.max(0, Math.floor(realDuration));
        setDuration(realDuration);
        setRange(buildInitialRange(nextIntegerDuration, minDuration, maxDuration));

        if (!realDuration || nextIntegerDuration <= 0) {
          setLocalError('视频时长异常，无法抽取帧');
          return;
        }

        const canvas = document.createElement('canvas');
        canvas.width = FRAME_WIDTH;
        canvas.height = FRAME_HEIGHT;
        const ctx = canvas.getContext('2d', { willReadFrequently: true });
        if (!ctx) {
          setLocalError('当前浏览器不支持 Canvas 抽帧');
          return;
        }

        const initialFrames: FrameItem[] = Array.from({ length: nextIntegerDuration }, (_, index) => ({
          second: index,
          image: '',
          status: 'loading',
        }));
        setFrames(initialFrames);

        const collected = [...initialFrames];
        for (let second = 0; second < nextIntegerDuration; second += 1) {
          if (abortRef.current) return;
          const frame = await captureOneFrame(extractor, ctx, canvas, second, realDuration);
          if (abortRef.current) return;
          collected[second] = frame;
          setFrames([...collected]);
        }
      } catch (error: any) {
        if (!abortRef.current) {
          setLocalError(error?.message || '视频帧抽取失败，请确认视频资源允许跨域访问');
        }
      } finally {
        if (!abortRef.current) {
          setFrameLoading(false);
        }
      }
    };

    buildFrames();

    return () => {
      abortRef.current = true;
      extractor.pause();
      extractor.removeAttribute('src');
      extractor.load();
    };
  }, [open, videoUrl, minDuration, maxDuration, setDisplayedTime, setRange]);

  const handleRangeChange = useCallback((value: number[]) => {
    if (!integerDuration) return;
    setLocalError('');
    const nextRange = [Number(value[0]), Number(value[1])] as [number, number];
    const clamped = [
      Math.max(0, Math.min(integerDuration, nextRange[0])),
      Math.max(nextRange[0], Math.min(integerDuration, nextRange[1])),
    ] as [number, number];
    setRange(clamped);
  }, [integerDuration, setRange]);

  const handleRangeChangeComplete = useCallback(() => {
    seekPreview(rangeRef.current[0]);
  }, [seekPreview]);

  const handleFrameClick = useCallback((second: number) => {
    if (!integerDuration || disabledByDuration) return;
    const keepDuration = clamp(selectedDuration || Math.min(DEFAULT_TRIM_SECONDS, maxDuration), minDuration, Math.min(maxDuration, integerDuration));
    let start = clamp(second, 0, Math.max(0, integerDuration - keepDuration));
    start = toIntegerSecond(start);
    const next: [number, number] = [start, start + keepDuration];
    setRange(next);
    seekPreview(next[0]);
  }, [disabledByDuration, integerDuration, maxDuration, minDuration, seekPreview, selectedDuration, setRange]);

  const handlePlaySelected = async () => {
    const video = videoRef.current;
    if (!video || !integerDuration) return;

    if (playing) {
      video.pause();
      setPlaying(false);
      return;
    }

    if (video.currentTime < range[0] || video.currentTime >= range[1]) {
      video.currentTime = range[0];
      setDisplayedTime(range[0]);
    }

    try {
      await video.play();
      setPlaying(true);
      setLocalError('');
    } catch {
      setLocalError('视频播放失败，请检查视频地址');
    }
  };

  const handleTimeUpdate = () => {
    const video = videoRef.current;
    if (!video) return;

    const current = toIntegerSecond(video.currentTime || 0);
    setDisplayedTime(current);
    if (playing && video.currentTime >= rangeRef.current[1]) {
      video.pause();
      video.currentTime = rangeRef.current[1];
      setDisplayedTime(rangeRef.current[1]);
      setPlaying(false);
    }
  };

  const handleConfirm = async () => {
    if (!integerDuration || integerDuration <= 0) {
      setLocalError('请等待视频加载完成');
      return;
    }
    if (disabledByDuration) {
      setLocalError(`视频总时长不足 ${minDuration} 秒，无法手动拆镜`);
      return;
    }
    if (selectedDuration < minDuration) {
      setLocalError(`拆镜片段不能低于 ${minDuration} 秒`);
      return;
    }
    if (selectedDuration > maxDuration) {
      setLocalError(`拆镜片段不能超过 ${maxDuration} 秒`);
      return;
    }

    setLocalError('');
    await onConfirm({
      startSecond: range[0],
      endSecond: range[1],
      durationSecond: selectedDuration,
    });
  };

  return (
    <Modal
      title={title}
      open={open}
      onCancel={onCancel}
      width={Math.min(window.innerWidth - 40, 1400)}
      destroyOnHidden
      footer={[
        <Button key="cancel" onClick={onCancel} disabled={loading} style={{ borderRadius: 8, border: '1px solid rgba(99, 102, 241, 0.2)', color: '#6366f1' }}>
          取消
        </Button>,
        <Button key="confirm" type="primary" onClick={handleConfirm} loading={loading} disabled={loading || disabledByDuration || !integerDuration} style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}>
          确认拆镜
        </Button>,
      ]}
    >
      <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
        <style>{`
          .ant-slider-handle::after {
            height: 80px !important;
            width: 10px !important;
            border-radius: 10px !important;
            top: 95% !important;
            transform: translateY(-50%) !important;
          }
          .ant-slider-handle {
            height: 80px !important;
            margin-top: -37px !important;
          }
        `}</style>
        <div style={{ display: 'flex', justifyContent: 'center', background: '#fff', borderRadius: 16, overflow: 'hidden' }}>
          <video
            ref={videoRef}
            src={videoUrl}
            crossOrigin="anonymous"
            preload="metadata"
            playsInline
            onLoadedMetadata={(event) => {
              const realDuration = Number.isFinite(event.currentTarget.duration) ? event.currentTarget.duration : 0;
              if (!realDuration) return;
              const nextIntegerDuration = Math.max(0, Math.floor(realDuration));
              setDuration((prev) => (Math.floor(prev || 0) === nextIntegerDuration ? prev : realDuration));
              setRange((prev) => {
                if (prev[1] > minDuration || prev[0] !== 0) return prev;
                return buildInitialRange(nextIntegerDuration, minDuration, maxDuration);
              });
            }}
            onTimeUpdate={handleTimeUpdate}
            onPause={() => setPlaying(false)}
            style={{  maxHeight: 320, objectFit: 'contain', background: '#111' }}
          />
        </div>

        <div
          style={{
            borderRadius: 20,
            background: '#f0f5ff',
            padding: '20px 24px',
          }}
        >
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 16, marginBottom: 18 }}>
            <Button
              type="text"
              icon={playing ? <PauseCircleFilled /> : <PlayCircleFilled />}
              onClick={handlePlaySelected}
              disabled={!integerDuration || disabledByDuration}
              style={{ fontSize: 20, color: '#1e293b', padding: 0 }}
            />
            <span style={{ fontSize: 18, color: '#1e293b', fontFamily: 'monospace' }}>
              {formatTime(currentTime)} / {formatTime(integerDuration)}
            </span>
          </div>

          <div
            ref={frameContainerRef}
            style={{
              overflowX: 'auto',
              overflowY: 'hidden',
              scrollbarWidth: 'none',
              msOverflowStyle: 'none',
              padding: '0 6px',
            }}
          >
            <style>{`
              div::-webkit-scrollbar {
                display: none;
              }
            `}</style>
            <div
              style={{
                margin: 'auto',
                display: 'flex',
                flexDirection: 'column',
                width: frames.length > 0 ? `${frames.length * 80 + Math.max(0, frames.length - 1) * 1}px` : '100%',
              }}
            >
              <div
                style={{
                  display: 'flex',
                  height: 86,
                  borderRadius: 12,
                  background: '#fff',
                  gap: 1,
                  position: 'relative',
                }}
              >
                {frameLoading && frames.length === 0 ? (
                  <div style={{ flex: '1 1 auto', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                    <Spin size="small" />
                    <Text style={{ marginLeft: 8, color: '#64748b' }}>正在抽取每秒帧...</Text>
                  </div>
                ) : frames.length > 0 ? (
                  frames.map((frame) => {
                    const isSelected = frame.second >= range[0] && frame.second < range[1];
                    return (
                      <button
                        key={frame.second}
                        type="button"
                        onClick={() => handleFrameClick(frame.second)}
                        title={`${frame.second}s${frame.fallback ? `，实际取帧 ${frame.captureSecond?.toFixed(1)}s` : ''}`}
                        style={{
                          minWidth: 80,
                          flex: '0 0 80px',
                          height: 86,
                          borderTop: isSelected ? '5px solid #6366f1' : 'none',
                          borderBottom: isSelected ? '5px solid #6366f1' : 'none',
                          padding: 0,
                          background: 'transparent',
                          overflow: 'hidden',
                          cursor: disabledByDuration ? 'not-allowed' : 'pointer',
                          position: 'relative',
                        }}
                      >
                      {frame.status === 'success' && frame.image ? (
                        <img src={frame.image} alt={`${frame.second}s`} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                      ) : frame.status === 'loading' ? (
                        <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                          <Spin size="small" />
                        </div>
                      ) : (
                        <div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#64748b', fontSize: 12, background: '#fff' }}>
                          <span>{frame.second}s</span>
                          <span style={{ fontSize: 11 }}>无有效帧</span>
                        </div>
                      )}
                      <span
                        style={{
                          position: 'absolute',
                          left: 0,
                          bottom: 3,
                          color: '#ffffffff',
                          fontSize: 16,
                          textShadow: '0 1px 3px rgba(0,0,0,.7)',
                        }}
                      >
                        {frame.second}s
                      </span>
                    </button>
                    );
                  })
                ) : (
                  <div style={{ flex: '1 1 auto', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#64748b' }}>
                    暂无帧预览，请确认视频是否加载完成
                  </div>
                )}

                <div
                  style={{
                    position: 'absolute',
                    top: -10,
                    left: 0,
                    right: 0,
                    bottom: 0,
                  }}
                >
                  <Slider
                    range
                    min={0}
                    max={integerDuration || maxDuration}
                    step={1}
                    value={range}
                    onChange={handleRangeChange}
                    onChangeComplete={handleRangeChangeComplete}
                    tooltip={{ formatter: (value) => `${toIntegerSecond(Number(value || 0))}s` }}
                    disabled={!integerDuration || disabledByDuration}
                    styles={{
                      track: {
                        background: '#6969dd63',
                        height: 86,
                        borderRadius: 12,
                        margin: '0 ',
                      },
                      rail: {
                        height: 86,
                        borderRadius: 12,
                      },
                      handle: {
                        width: 20,
                        height: 86,
                        marginTop: 0,
                        borderRadius: '50%',
                      },
                    }}
                  />
                </div>
              </div>
            </div>
          </div>

          <div style={{ marginTop: 16, textAlign: 'center', color: '#64748b', fontSize: 14 }}>
            已选取 {selectedDuration}s
          </div>

          {localError && (
            <div style={{ marginTop: 12, padding: '10px 14px', borderRadius: 8, background: 'rgba(239, 68, 68, 0.08)', border: '1px solid rgba(239, 68, 68, 0.15)', color: '#ef4444', fontSize: 13, textAlign: 'center' }}>
              {localError}
            </div>
          )}
        </div>
      </div>
    </Modal>
  );
};

export default VideoTrimPicker;
