import React from "react";
import {Audio, Video} from "@remotion/media";
import {
  AbsoluteFill,
  Img,
  interpolate,
  Sequence,
  spring,
  staticFile,
  useCurrentFrame,
  useVideoConfig,
} from "remotion";
import {
  audioFileForSpeech,
  durationForSpeech,
  hasAudioForSpeech,
  PIZZA_KILN_GAP_FRAMES,
  PIZZA_KILN_VIDEO_FRAMES,
} from "./data/pizza-kiln/timing";
import {characters, timeline, type SpeechEvent} from "./data/pizza-kiln/script";
import {roundedFontFamily} from "./fonts";

const BACKGROUND_VIDEO_PATH = "video/pizza-kiln-background.mp4";

const clampInterpolation = {
  extrapolateLeft: "clamp",
  extrapolateRight: "clamp",
} as const;

const UsualBackground: React.FC = () => (
  <>
    <AbsoluteFill
      style={{
        background:
          "radial-gradient(circle at top, #ffe8c7 0%, #ffd3b4 45%, #ffb6b6 100%)",
      }}
    />
    <AbsoluteFill
      style={{
        backgroundImage:
          "radial-gradient(circle at 20% 20%, rgba(255,255,255,0.6) 0, rgba(255,255,255,0) 40%), radial-gradient(circle at 80% 30%, rgba(255,255,255,0.5) 0, rgba(255,255,255,0) 45%), radial-gradient(circle at 30% 80%, rgba(255,255,255,0.4) 0, rgba(255,255,255,0) 50%)",
        opacity: 0.8,
      }}
    />
  </>
);

const Subtitle: React.FC<
  Readonly<{
    text: string;
    progress: number;
    speakerName: string;
    accentColor: string;
  }>
> = ({text, progress, speakerName, accentColor}) => {
  const opacity = interpolate(progress, [0, 1], [0, 1], clampInterpolation);
  const translateY = interpolate(progress, [0, 1], [16, 0], clampInterpolation);

  return (
    <div
      style={{
        fontFamily: roundedFontFamily,
        fontSize: 34,
        fontWeight: 700,
        color: "#1a1a1a",
        lineHeight: 1.45,
        padding: "18px 28px",
        backgroundColor: "rgba(255, 255, 255, 0.9)",
        borderRadius: 18,
        border: `2px solid ${accentColor}33`,
        boxShadow: "0 10px 30px rgba(31, 42, 68, 0.15)",
        maxWidth: 980,
        opacity,
        transform: `translateY(${translateY}px)`,
      }}
    >
      <div
        style={{
          display: "inline-block",
          fontSize: 20,
          color: "#ffffff",
          backgroundColor: accentColor,
          borderRadius: 999,
          padding: "4px 14px",
          marginBottom: 8,
        }}
      >
        {speakerName}
      </div>
      <div>{text}</div>
    </div>
  );
};

const SayoStandee: React.FC<
  Readonly<{
    mode: "stage" | "corner";
    speaking: boolean;
    localFrame: number;
    fps: number;
  }>
> = ({mode, speaking, localFrame, fps}) => {
  const cycleFrames = Math.max(1, Math.round(fps * 0.25));
  const progress = (localFrame % cycleFrames) / cycleFrames;
  const speakingHop = -Math.sin(progress * Math.PI) * 7;
  const translateY = speaking ? speakingHop : 0;
  const isCorner = mode === "corner";

  return (
    <div
      style={{
        position: "absolute",
        right: isCorner ? 20 : 360,
        bottom: isCorner ? -38 : -74,
        width: isCorner ? 270 : 560,
        height: isCorner ? 430 : 700,
        display: "flex",
        alignItems: "flex-end",
        justifyContent: "center",
        transform: `translateY(${translateY}px)`,
        zIndex: 3,
      }}
    >
      <Img
        src={staticFile(characters.sayo.avatar.imagePath)}
        style={{
          width: "100%",
          maxHeight: "100%",
          objectFit: "contain",
          filter: isCorner
            ? "drop-shadow(0 12px 24px rgba(0, 0, 0, 0.32))"
            : "drop-shadow(0 18px 40px rgba(31, 42, 68, 0.22))",
        }}
      />
    </div>
  );
};

const SpeechOverlay: React.FC<Readonly<{speech: SpeechEvent}>> = ({speech}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();
  const character = characters[speech.character];
  const subtitleProgress = spring({
    frame,
    fps,
    config: {damping: 20, mass: 0.7},
  });

  return (
    <>
      <div
        style={{
          position: "absolute",
          bottom: 40,
          left: 0,
          right: 0,
          display: "flex",
          justifyContent: "center",
          zIndex: 4,
        }}
      >
        <Subtitle
          text={speech.subtitle ?? speech.text}
          progress={subtitleProgress}
          speakerName={character.displayName}
          accentColor={character.avatar.accentColor}
        />
      </div>
      {hasAudioForSpeech(speech) ? (
        <Audio src={staticFile(audioFileForSpeech(speech))} />
      ) : null}
    </>
  );
};

export const PizzaKilnSayoComposition: React.FC = () => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();
  const introSpeech = timeline[0];
  const outroSpeech = timeline[1];
  const introFrames = durationForSpeech(introSpeech, fps);
  const outroFrames = durationForSpeech(outroSpeech, fps);
  const videoFrom = introFrames + PIZZA_KILN_GAP_FRAMES;
  const outroFrom = videoFrom + PIZZA_KILN_VIDEO_FRAMES + PIZZA_KILN_GAP_FRAMES;
  const isVideoVisible =
    frame >= videoFrom && frame < videoFrom + PIZZA_KILN_VIDEO_FRAMES;
  const isOutro = frame >= outroFrom;
  const isSpeaking = frame < introFrames || isOutro;
  const speechLocalFrame = isOutro ? frame - outroFrom : frame;

  return (
    <AbsoluteFill style={{backgroundColor: "#1a1a1a"}}>
      <UsualBackground />
      <Sequence
        from={videoFrom}
        durationInFrames={PIZZA_KILN_VIDEO_FRAMES}
        premountFor={Math.min(fps, videoFrom)}
      >
        <Video
          muted
          objectFit="cover"
          src={staticFile(BACKGROUND_VIDEO_PATH)}
          style={{
            width: "100%",
            height: "100%",
          }}
        />
      </Sequence>
      <SayoStandee
        mode={isVideoVisible ? "corner" : "stage"}
        speaking={isSpeaking}
        localFrame={speechLocalFrame}
        fps={fps}
      />
      <Sequence durationInFrames={introFrames} premountFor={0}>
        <SpeechOverlay speech={introSpeech} />
      </Sequence>
      <Sequence
        from={outroFrom}
        durationInFrames={outroFrames}
        premountFor={Math.min(fps, outroFrom)}
      >
        <SpeechOverlay speech={outroSpeech} />
      </Sequence>
    </AbsoluteFill>
  );
};
