import React from "react";
import {Video} from "@remotion/media";
import {
  AbsoluteFill,
  Sequence,
  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";
import {
  VQLipSyncedStandeeImage,
  VQSpeechOverlay,
  VQWarmGradientBackground,
  vqSpeakingAvatarAnimations,
} from "./lib/VQRemotionLib";
import {getMouthForSpeechFrame} from "./lipsync/manifest";

const BACKGROUND_VIDEO_PATH = "video/pizza-kiln-background.mp4";
const STAGE_STANDEE_WIDTH = 610;
const STAGE_STANDEE_HEIGHT = 760;
const STAGE_STANDEE_RIGHT = 315;
const STAGE_STANDEE_BOTTOM = -118;
const CORNER_STANDEE_WIDTH = 420;
const CORNER_STANDEE_HEIGHT = 360;
const CORNER_IMAGE_WIDTH = 470;
const CORNER_IMAGE_HEIGHT = 705;

const pizzaSubtitleOptions = {
  fontFamily: roundedFontFamily,
  fontSize: 34,
  lineHeight: 1.45,
  backgroundColor: "rgba(255, 255, 255, 0.9)",
} as const;

const SayoStandee: React.FC<
  Readonly<{
    mode: "stage" | "corner";
    speaking: boolean;
    localFrame: number;
    fps: number;
    speechId?: string;
  }>
> = ({mode, speaking, localFrame, fps, speechId}) => {
  const {avatar} = characters.sayo;
  const speakingAnimationType = avatar.speakingAnimationType ?? "none";
  const translateY = speaking
    ? vqSpeakingAvatarAnimations[speakingAnimationType]({
        frame: localFrame,
        fps,
        focused: true,
        hasMultipleCharacters: false,
      })
    : 0;
  const mouth =
    speaking && speakingAnimationType === "rhubarbLipSync"
      ? getMouthForSpeechFrame(speechId, localFrame, fps)
      : "rest";
  const isCorner = mode === "corner";
  const frameWidth = isCorner ? CORNER_STANDEE_WIDTH : STAGE_STANDEE_WIDTH;
  const frameHeight = isCorner ? CORNER_STANDEE_HEIGHT : STAGE_STANDEE_HEIGHT;

  return (
    <div
      style={{
        position: "absolute",
        right: isCorner ? 18 : STAGE_STANDEE_RIGHT,
        bottom: isCorner ? 0 : STAGE_STANDEE_BOTTOM,
        width: frameWidth,
        height: frameHeight,
        display: "flex",
        alignItems: isCorner ? "flex-start" : "flex-end",
        justifyContent: "center",
        overflow: isCorner ? "hidden" : "visible",
        transform: `translateY(${translateY}px)`,
        zIndex: 3,
      }}
    >
      <VQLipSyncedStandeeImage
        imagePath={avatar.imagePath}
        mouthImageDir={avatar.mouthImageDir}
        mouth={mouth}
        width={isCorner ? CORNER_IMAGE_WIDTH : "100%"}
        height={isCorner ? CORNER_IMAGE_HEIGHT : "100%"}
        maxHeight={isCorner ? CORNER_IMAGE_HEIGHT : "100%"}
        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 character = characters[speech.character];

  return (
    <VQSpeechOverlay
      speech={speech}
      speakerName={character.displayName}
      accentColor={character.avatar.accentColor}
      hasAudio={hasAudioForSpeech}
      getAudioPath={audioFileForSpeech}
      subtitleOptions={pizzaSubtitleOptions}
      containerStyle={{zIndex: 4}}
    />
  );
};

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 activeSpeech =
    frame < introFrames ? introSpeech : isOutro ? outroSpeech : undefined;
  const speechLocalFrame = activeSpeech === outroSpeech ? frame - outroFrom : frame;

  return (
    <AbsoluteFill style={{backgroundColor: "#1a1a1a"}}>
      <VQWarmGradientBackground />
      <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={Boolean(activeSpeech)}
        localFrame={speechLocalFrame}
        fps={fps}
        speechId={activeSpeech?.id}
      />
      <Sequence durationInFrames={introFrames} premountFor={0}>
        <SpeechOverlay speech={introSpeech} />
      </Sequence>
      <Sequence
        from={outroFrom}
        durationInFrames={outroFrames}
        premountFor={Math.min(fps, outroFrom)}
      >
        <SpeechOverlay speech={outroSpeech} />
      </Sequence>
    </AbsoluteFill>
  );
};
