import React, { useState, useEffect, useCallback, useMemo } from "react";
import {
  AbsoluteFill,
  staticFile,
  useCurrentFrame,
  useVideoConfig,
  Sequence,
  useDelayRender,
} from "remotion";
import { parseSrt } from "@remotion/captions";
import { FONT_FAMILY } from "./fonts";
import type { Caption } from "@remotion/captions";

const HIGHLIGHT_COLOR = "#FFD93D";
const CTA_START_MS = 14400;
const MAX_WORDS_PER_CHUNK = 5;

/**
 * Split long SRT entries into smaller chunks of ~MAX_WORDS_PER_CHUNK words.
 * Time is distributed proportionally by word count.
 */
function splitLongCaptions(captions: Caption[]): Caption[] {
  const result: Caption[] = [];

  for (const cap of captions) {
    const words = cap.text.split(/\s+/).filter((w) => w.length > 0);

    if (words.length <= MAX_WORDS_PER_CHUNK) {
      result.push(cap);
      continue;
    }

    const totalDurationMs = cap.endMs - cap.startMs;
    const msPerWord = totalDurationMs / words.length;

    for (let i = 0; i < words.length; i += MAX_WORDS_PER_CHUNK) {
      const chunkWords = words.slice(i, i + MAX_WORDS_PER_CHUNK);
      const chunkStartMs = cap.startMs + i * msPerWord;
      const chunkEndMs = cap.startMs + (i + chunkWords.length) * msPerWord;

      result.push({
        text: chunkWords.join(" "),
        startMs: Math.round(chunkStartMs),
        endMs: Math.round(chunkEndMs),
        timestampMs: null,
        confidence: null,
      });
    }
  }

  return result;
}

const CaptionEntry: React.FC<{
  text: string;
  durationMs: number;
}> = ({ text, durationMs }) => {
  const frame = useCurrentFrame();
  const { fps, height, width } = useVideoConfig();

  const isVertical = height / width > 1.5;
  const safeTop = isVertical ? Math.round(height * 0.14) : 30;
  const safeHorizontal = isVertical ? Math.round(width * 0.06) : 20;

  const currentMs = (frame / fps) * 1000;
  const words = text.split(/(\s+)/);
  const realWords = words.filter((w) => w.trim().length > 0);
  const wordCount = realWords.length;
  const msPerWord = wordCount > 0 ? durationMs / wordCount : durationMs;

  let wordIndex = 0;

  return (
    <AbsoluteFill
      style={{
        justifyContent: "flex-start",
        alignItems: "center",
        paddingTop: safeTop,
        paddingLeft: safeHorizontal,
        paddingRight: safeHorizontal,
      }}
    >
      <div
        style={{
          backgroundColor: "rgba(0, 0, 0, 0.6)",
          borderRadius: 16,
          padding: "14px 28px",
          maxWidth: width - safeHorizontal * 2 - 40,
        }}
      >
        <div
          style={{
            fontSize: 54,
            fontWeight: 800,
            fontFamily: FONT_FAMILY,
            textAlign: "center",
            whiteSpace: "pre-wrap",
            lineHeight: 1.35,
          }}
        >
          {words.map((token, i) => {
            if (token.trim().length === 0) {
              return (
                <span key={`ws-${i}`} style={{ color: "white" }}>
                  {token}
                </span>
              );
            }

            const thisWordIndex = wordIndex;
            wordIndex++;

            const wordStartMs = thisWordIndex * msPerWord;
            const wordEndMs = (thisWordIndex + 1) * msPerWord;
            const isActive = currentMs >= wordStartMs && currentMs < wordEndMs;

            return (
              <span
                key={`w-${i}`}
                style={{
                  color: isActive ? HIGHLIGHT_COLOR : "white",
                }}
              >
                {token}
              </span>
            );
          })}
        </div>
      </div>
    </AbsoluteFill>
  );
};

export const Captions: React.FC<{ srtFile?: string }> = ({
  srtFile = "voiceover/voiceover.srt",
}) => {
  const [captions, setCaptions] = useState<Caption[] | null>(null);
  const { delayRender, continueRender, cancelRender } = useDelayRender();
  const [handle] = useState(() => delayRender());
  const { fps } = useVideoConfig();

  const fetchCaptions = useCallback(async () => {
    try {
      const response = await fetch(staticFile(srtFile));
      const text = await response.text();
      const { captions: parsed } = parseSrt({ input: text });
      const cleaned = parsed
        .map((c) => ({
          ...c,
          text: c.text.replace(/\[.*?\]\s*/g, "").trim(),
        }))
        .filter((c) => c.text.length > 0);
      setCaptions(cleaned);
      continueRender(handle);
    } catch (e) {
      cancelRender(e);
    }
  }, [continueRender, cancelRender, handle]);

  useEffect(() => {
    fetchCaptions();
  }, [fetchCaptions]);

  // Split long entries into ≤5-word chunks
  const chunks = useMemo(() => {
    if (!captions) return [];
    return splitLongCaptions(captions);
  }, [captions]);

  if (!captions) return null;

  return (
    <AbsoluteFill>
      {chunks.map((caption, index) => {
        if (caption.startMs >= CTA_START_MS) return null;

        const startFrame = Math.round((caption.startMs / 1000) * fps);

        const nextCaption = chunks[index + 1] ?? null;
        let endMs: number;
        if (nextCaption) {
          endMs = nextCaption.startMs;
        } else {
          endMs = caption.endMs;
        }

        if (endMs > CTA_START_MS) {
          endMs = CTA_START_MS;
        }

        const endFrame = Math.round((endMs / 1000) * fps);
        const durationInFrames = endFrame - startFrame;

        if (durationInFrames <= 0) return null;

        const durationMs = endMs - caption.startMs;

        return (
          <Sequence
            key={index}
            from={startFrame}
            durationInFrames={durationInFrames}
          >
            <CaptionEntry text={caption.text} durationMs={durationMs} />
          </Sequence>
        );
      })}
    </AbsoluteFill>
  );
};
