import React, { useEffect, useState } from "react";
import { Composition, Sequence, AbsoluteFill, Audio, staticFile, Img, useCurrentFrame, useVideoConfig, interpolate, continueRender, delayRender } from "remotion";
import type { TemplateType, AspectRatio } from "@pipeline/shared";
import type { WordTimestamp, GithubSceneData } from "@pipeline/shared";
import { formatCount } from "@pipeline/shared";
import NewsScene from "./news/index";
import KnowledgeScene from "./knowledge/index";
import OpinionScene from "./opinion/index";
import MarketingScene from "./marketing/index";
import GithubTrendingScene, { ColorDot } from "./github-trending/index";
import { THEMES } from "./base/theme/colors";

export interface RemotionScene {
  id: string;
  sceneType: "cover" | "content" | "summary" | "outro";
  startFrame: number;
  endFrame: number;
  narration: string;
  displayText?: string;
  title?: string;
  wordTimestamps: WordTimestamp[];
  keyframes: Array<{
    type: string;
    content: string;
    startFrame?: number;
    endFrame?: number;
    style?: Record<string, string>;
  }>;
  images?: Array<{ url?: string; query?: string; filename: string }>;
  audioFilename: string;
  backgroundAsset?: { id: string; filename: string };
  layoutHint: string;
  github?: GithubSceneData;
  // github-trending cover scene only: theme tags + trend line.
  coverTags?: string[];
  trendSummary?: string;
}

export interface RemotionProps {
  scenes: RemotionScene[];
  template: TemplateType;
  platform: string;
  title: string;
  subtitle?: string;
  outro?: {
    text: string;
    narration?: string;
    cta?: string;
  };
  channelName: string;
  globalStyle: {
    tone: string;
    pace: string;
  };
}

const SCENE_MAP: Record<TemplateType, React.FC<any>> = {
  news: NewsScene,
  knowledge: KnowledgeScene,
  opinion: OpinionScene,
  marketing: MarketingScene,
  "github-trending": GithubTrendingScene,
};

const RATIO_ID: Record<AspectRatio, string> = {
  "16:9": "landscape",
  "9:16": "portrait",
};

const ASPECT_RATIOS: Record<AspectRatio, { width: number; height: number }> = {
  "16:9": { width: 1920, height: 1080 },
  "9:16": { width: 1080, height: 1920 },
};

const TEMPLATES: TemplateType[] = ["news", "knowledge", "opinion", "marketing", "github-trending"];

const TemplateComposition: React.FC<RemotionProps> = (props) => {
  const SceneComponent = SCENE_MAP[props.template];
  const totalFrames = props.scenes.at(-1)?.endFrame ?? 90;
  // github-trending: the cover lists every repo (name / language / today's
  // star gain / one-line description). Content scenes each carry scene.github.
  const coverRepos = props.scenes.filter(
    (s) => s.sceneType === "content" && s.github
  );

  return (
    <AbsoluteFill style={{ backgroundColor: "#000" }}>
      {props.scenes.map((scene) => {
        const duration = scene.endFrame - scene.startFrame;
        const sceneAudio = scene.audioFilename ? (
          <Audio src={staticFile(scene.audioFilename)} />
        ) : null;

        if (scene.sceneType === "cover") {
          return (
            <Sequence
              key={scene.id}
              from={scene.startFrame}
              durationInFrames={duration}
            >
              {sceneAudio}
              <CoverScene
                template={props.template}
                title={props.title}
                subtitle={props.subtitle}
                keyframes={scene.keyframes}
                backgroundAsset={scene.backgroundAsset}
                coverRepos={coverRepos}
                trendSummary={scene.trendSummary}
                totalFrames={totalFrames}
              />
            </Sequence>
          );
        }
        if (scene.sceneType === "outro") {
          return (
            <Sequence
              key={scene.id}
              from={scene.startFrame}
              durationInFrames={duration}
            >
              {sceneAudio}
              <OutroScene
                text={props.outro?.text || scene.narration}
                cta={props.outro?.cta}
                totalFrames={totalFrames}
              />
            </Sequence>
          );
        }
        return (
          <Sequence
            key={scene.id}
            from={scene.startFrame}
            durationInFrames={duration}
          >
            {sceneAudio}
            <SceneComponent
              scene={scene}
              sceneIndex={props.scenes.filter((s) => s.sceneType === "content").indexOf(scene)}
              totalScenes={props.scenes.filter((s) => s.sceneType === "content").length}
              totalFrames={totalFrames}
              title={props.title}
              channelName={props.channelName}
            />
          </Sequence>
        );
      })}
      <GlobalProgressBar totalFrames={totalFrames} color={THEMES[props.template].primary} />
      {props.template === "github-trending" && <ChapterToc scenes={props.scenes} />}
    </AbsoluteFill>
  );
};

// --- Cover Scene ---

// Faint, sparse code/data motifs on the github-trending cover background.
// Low-opacity and edge-anchored so the cover stays clean — the motifs read
// only as subtle texture, never competing with the cards.
const CoverDecor: React.FC<{ accent: string }> = ({ accent }) => {
  const tok = (css: React.CSSProperties): React.CSSProperties => ({
    position: "absolute",
    fontFamily: "monospace",
    fontWeight: 700,
    color: "#0f172a",
    opacity: 0.06,
    letterSpacing: "0.02em",
    ...css,
  });
  return (
    <div style={{ position: "absolute", inset: 0, overflow: "hidden", pointerEvents: "none" }}>
      <span style={tok({ top: 44, left: 52, fontSize: 56 })}>{"</>"}</span>
      <span style={tok({ top: 58, right: 60, fontSize: 48 })}>{"{ }"}</span>
      <span style={tok({ bottom: 168, left: 54, fontSize: 28 })}>{"01 10 01"}</span>
      <span style={tok({ bottom: 150, right: 58, fontSize: 26 })}>{"git push"}</span>
      {/* Faint data line chart */}
      <svg width="180" height="84" viewBox="0 0 180 84" style={{ position: "absolute", top: 64, right: 168, opacity: 0.08 }}>
        <polyline points="0,66 34,50 68,56 102,28 136,34 180,8" fill="none" stroke={accent} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
      </svg>
      {/* Faint data bar cluster */}
      <div style={{ position: "absolute", bottom: 206, left: 80, display: "flex", alignItems: "flex-end", gap: 7, opacity: 0.08 }}>
        <div style={{ width: 12, height: 32, background: accent, borderRadius: 3 }} />
        <div style={{ width: 12, height: 58, background: accent, borderRadius: 3 }} />
        <div style={{ width: 12, height: 24, background: accent, borderRadius: 3 }} />
        <div style={{ width: 12, height: 76, background: accent, borderRadius: 3 }} />
        <div style={{ width: 12, height: 46, background: accent, borderRadius: 3 }} />
      </div>
    </div>
  );
};

const CoverScene: React.FC<{
  template: TemplateType;
  title: string;
  subtitle?: string;
  keyframes: Array<{ type: string; content: string }>;
  backgroundAsset?: { id: string; filename: string };
  coverRepos?: RemotionScene[];
  trendSummary?: string;
  totalFrames: number;
}> = ({ template, title, subtitle, keyframes, backgroundAsset, coverRepos, trendSummary }) => {
  const accent = THEMES[template].primaryLight;
  const isGithubTrending = template === "github-trending";
  const { width, height } = useVideoConfig();
  const isPortrait = height > width;

  // github-trending cover: a DARK title card (contrast over a light bg with
  // faint code/data motifs) centered near the top, then the TOP 3 repos by
  // today's star gain as large rounded "floating" cards — horizontal row in
  // landscape, vertical stack in portrait. Doubles as the opening narration.
  if (isGithubTrending) {
    const topRepos = [...(coverRepos ?? [])]
      .sort((a, b) => (b.github!.repo.todayStars ?? 0) - (a.github!.repo.todayStars ?? 0))
      .slice(0, 6);
    const primary = THEMES[template].primary;

    return (
      <AbsoluteFill style={{
        background: "linear-gradient(135deg, #f8fafc 0%, #eef2f7 55%, #e2e8f0 100%)",
      }}>
        <CoverDecor accent={accent} />
        {/* Top accent line */}
        <div style={{
          position: "absolute", top: 0, left: 0, width: "100%", height: 6,
          background: `linear-gradient(90deg, ${primary}, ${accent})`,
        }} />
        <div style={{
          position: "absolute", inset: 0, display: "flex", flexDirection: "column",
          alignItems: "center",
          justifyContent: isPortrait ? "center" : "flex-start",
          padding: isPortrait ? "72px 64px 130px" : "58px 80px 112px",
        }}>
          {/* DARK title card — centered, near the top (contrast over light bg) */}
          <div style={{
            display: "flex", flexDirection: "column", alignItems: "center", gap: 14,
            padding: isPortrait ? "38px 76px" : "32px 88px",
            borderRadius: 26,
            background: "linear-gradient(135deg, #0f172a 0%, #1e293b 100%)",
            border: `1.5px solid ${primary}55`,
            boxShadow: "0 18px 44px rgba(15,23,42,0.22)",
            marginBottom: isPortrait ? 44 : 38,
            maxWidth: isPortrait ? "84%" : "74%",
            textAlign: "center",
          }}>
            <div style={{
              fontSize: isPortrait ? 72 : 66, fontWeight: 800, color: "#ffffff",
              fontFamily: "Noto Sans SC", lineHeight: 1.15, letterSpacing: "-0.02em",
            }}>
              {title}
            </div>
            <div style={{
              display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap",
              justifyContent: "center",
            }}>
              {subtitle && (
                <span style={{
                  fontSize: isPortrait ? 30 : 26, fontWeight: 600, color: accent,
                  fontFamily: "Noto Sans SC",
                }}>
                  {subtitle}
                </span>
              )}
              {trendSummary && (
                <span style={{
                  fontSize: isPortrait ? 28 : 24, fontWeight: 500, color: "#cbd5e1",
                  fontFamily: "Noto Sans SC",
                }}>
                  · {trendSummary}
                </span>
              )}
            </div>
          </div>

          {/* TOP 6 repos by today's star gain (3 per row → wraps to a grid) */}
          {topRepos.length > 0 && (
            <div style={{
              display: "flex", flexWrap: "wrap",
              gap: 24,
              width: "100%",
              maxWidth: isPortrait ? "90%" : "94%",
            }}>
              {topRepos.map((s, i) => {
                const g = s.github!;
                const repo = g.repo;
                const language = repo.language || "";
                const languageColor = repo.languageColor || accent;
                const todayLabel = repo.todayStars != null ? `+${formatCount(repo.todayStars)}` : "";
                return (
                  <div key={s.id} style={{
                    flex: isPortrait ? "0 0 calc((100% - 24px) / 2)" : "0 0 calc((100% - 48px) / 3)",
                    minWidth: 0,
                    display: "flex", flexDirection: "column",
                    padding: isPortrait ? "28px 30px" : "30px",
                    borderRadius: 22,
                    background: "#ffffff",
                    border: `1px solid ${primary}22`,
                    boxShadow: "0 16px 38px rgba(15,23,42,0.12), 0 2px 8px rgba(15,23,42,0.06)",
                  }}>
                    {/* Rank badge + today's gain (the selection signal) */}
                    <div style={{
                      display: "flex", alignItems: "center", justifyContent: "space-between",
                      marginBottom: 18,
                    }}>
                      <div style={{
                        display: "inline-flex", alignItems: "center", justifyContent: "center",
                        width: isPortrait ? 56 : 52, height: isPortrait ? 56 : 52,
                        borderRadius: "50%",
                        background: `linear-gradient(135deg, ${accent}, ${primary})`,
                        color: "#fff", fontWeight: 800, fontFamily: "Noto Sans SC",
                        fontSize: isPortrait ? 30 : 26,
                        boxShadow: "0 4px 12px rgba(34,197,94,0.35)",
                      }}>
                        {i + 1}
                      </div>
                      {todayLabel && (
                        <span style={{
                          fontSize: isPortrait ? 56 : 50, fontWeight: 800, color: primary,
                          fontFamily: "Noto Sans SC", lineHeight: 1,
                        }}>{todayLabel}</span>
                      )}
                    </div>
                    {/* Repo full name */}
                    <div style={{
                      fontSize: isPortrait ? 40 : 34, fontWeight: 800, color: "#0f172a",
                      fontFamily: "Noto Sans SC", lineHeight: 1.2, marginBottom: 12,
                      wordBreak: "break-word",
                    }}>
                      {repo.fullName}
                    </div>
                    {/* Language + total stars */}
                    <div style={{
                      display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap",
                      marginBottom: 14, fontFamily: "Noto Sans SC",
                    }}>
                      {language && (
                        <span style={{
                          display: "inline-flex", alignItems: "center", gap: 8,
                          fontSize: isPortrait ? 24 : 21, fontWeight: 600, color: "#334155",
                          padding: "5px 14px", borderRadius: 999,
                          background: `${languageColor}1a`, border: `1px solid ${languageColor}55`,
                        }}>
                          <ColorDot color={languageColor} size={isPortrait ? 14 : 12} />
                          {language}
                        </span>
                      )}
                      {repo.stars != null && (
                        <span style={{
                          fontSize: isPortrait ? 23 : 20, color: "#64748b", fontWeight: 500,
                        }}>
                          {formatCount(repo.stars)} stars
                        </span>
                      )}
                    </div>
                    {/* One-line description (highlights) */}
                    <div style={{
                      fontSize: isPortrait ? 27 : 23, lineHeight: 1.4, color: "#475569",
                      fontFamily: "Noto Sans SC",
                      display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical",
                      overflow: "hidden",
                    }}>
                      {g.highlights}
                    </div>
                  </div>
                );
              })}
            </div>
          )}
        </div>
      </AbsoluteFill>
    );
  }

  return (
    <AbsoluteFill style={{ backgroundColor: "#0f172a" }}>
      {backgroundAsset && (
        <Img
          src={staticFile(backgroundAsset.filename)}
          style={{ position: "absolute", width: "100%", height: "100%", objectFit: "cover" }}
        />
      )}
      <div
        style={{
          position: "absolute",
          inset: 0,
          background:
            "linear-gradient(to bottom, rgba(0,0,0,0.25), rgba(0,0,0,0.55))",
        }}
      />
      <div style={{
        position: "absolute", inset: 0, display: "flex",
        flexDirection: "column", justifyContent: "center", alignItems: "center",
        padding: 80, textAlign: "center",
      }}>
        <div style={{
          fontSize: 72, fontWeight: 800, color: "white",
          fontFamily: "Noto Sans SC", lineHeight: 1.2, marginBottom: 20,
          maxWidth: "80%",
          textShadow: "0 2px 16px rgba(0,0,0,0.85), 0 0 4px rgba(0,0,0,0.85)",
        }}>
          {title}
        </div>
        {subtitle && (
          <div style={{
            fontSize: 28, color: "white",
            fontFamily: "Noto Sans SC", marginBottom: 40,
            textShadow: "0 2px 12px rgba(0,0,0,0.85)",
          }}>
            {subtitle}
          </div>
        )}
        {keyframes.length > 0 && (
          <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            {keyframes.map((kf, i) => {
              const isHighlight = kf.type === "highlight";
              return (
                <div key={i} style={{
                  fontSize: isHighlight ? 30 : 24,
                  fontWeight: isHighlight ? 700 : 500,
                  color: isHighlight ? accent : "white",
                  fontFamily: "Noto Sans SC",
                  textShadow: "0 1px 8px rgba(0,0,0,0.85)",
                }}>
                  {kf.content}
                </div>
              );
            })}
          </div>
        )}
      </div>
    </AbsoluteFill>
  );
};

// --- Outro Scene ---

const OutroScene: React.FC<{
  text: string;
  cta?: string;
  totalFrames: number;
}> = ({ text, cta }) => {
  const frame = useCurrentFrame();
  const opacity = interpolate(frame, [0, 15], [0, 1], { extrapolateRight: "clamp" });

  return (
    <AbsoluteFill style={{ opacity, backgroundColor: "#0f172a" }}>
      <div style={{
        position: "absolute", inset: 0, display: "flex",
        flexDirection: "column", justifyContent: "center", alignItems: "center",
        padding: 80, textAlign: "center",
      }}>
        <div style={{
          fontSize: 48, fontWeight: 700, color: "white",
          fontFamily: "Noto Sans SC", lineHeight: 1.4, marginBottom: 40,
          maxWidth: "80%",
        }}>
          {text}
        </div>
        {cta && (
          <div style={{
            fontSize: 28, fontWeight: 600, color: "#fbbf24",
            fontFamily: "Noto Sans SC",
            padding: "16px 48px",
            borderRadius: 12,
            border: "2px solid #fbbf24",
          }}>
            {cta}
          </div>
        )}
      </div>
    </AbsoluteFill>
  );
};

const GlobalProgressBar: React.FC<{
  totalFrames: number;
  color: string;
}> = ({ totalFrames, color }) => {
  const frame = useCurrentFrame();
  const progress = interpolate(frame, [0, totalFrames], [0, 100], {
    extrapolateRight: "clamp",
  });

  return (
    <div
      style={{
        position: "absolute",
        bottom: 0,
        left: 0,
        width: "100%",
        height: 4,
        backgroundColor: "rgba(255,255,255,0.1)",
      }}
    >
      <div
        style={{
          width: `${progress}%`,
          height: "100%",
          backgroundColor: color,
        }}
      />
    </div>
  );
};

const ChapterToc: React.FC<{ scenes: RemotionScene[] }> = ({ scenes }) => {
  // github-trending only: a chapter table of contents pinned just above the
  // global progress bar. Lists every repo's short name; the chapter whose
  // [startFrame, endFrame) contains the current frame is highlighted. A faint
  // dark gradient strip behind the row keeps labels legible over light
  // backgrounds (e.g. the cover) and visually groups the TOC with the bar.
  const frame = useCurrentFrame();
  const { width, height } = useVideoConfig();
  const isPortrait = height > width;
  const accent = THEMES["github-trending"].primary;

  const chapters = scenes.filter((s) => s.sceneType === "content" && s.github);
  const active = chapters.findIndex(
    (c) => frame >= c.startFrame && frame < c.endFrame
  );

  return (
    <>
      <div
        style={{
          position: "absolute",
          bottom: 0,
          left: 0,
          width: "100%",
          height: 72,
          background: "linear-gradient(to top, rgba(0,0,0,0.42), transparent)",
          pointerEvents: "none",
        }}
      />
      <div
        style={{
          position: "absolute",
          bottom: 12,
          left: 0,
          width: "100%",
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          gap: isPortrait ? 16 : 12,
          padding: "0 32px",
          fontFamily: "Noto Sans SC",
          pointerEvents: "none",
        }}
      >
        {chapters.map((c, i) => {
          const isActive = i === active;
          const repo = c.github!.repo;
          const label = repo.name || repo.fullName || c.displayText || "";
          return (
            <React.Fragment key={c.id}>
              {i > 0 && (
                <span
                  style={{
                    color: "rgba(255,255,255,0.3)",
                    fontSize: isPortrait ? 22 : 18,
                    flexShrink: 0,
                  }}
                >
                  ·
                </span>
              )}
              <span
                style={{
                  flex: "1 1 0",
                  minWidth: 0,
                  fontSize: isPortrait ? 26 : 22,
                  fontWeight: isActive ? 700 : 500,
                  color: isActive ? accent : "rgba(255,255,255,0.6)",
                  whiteSpace: "nowrap",
                  overflow: "hidden",
                  textOverflow: "ellipsis",
                  textAlign: "center",
                }}
              >
                {label}
              </span>
            </React.Fragment>
          );
        })}
      </div>
    </>
  );
};

const defaultProps: RemotionProps = {
  scenes: [
    {
      id: "cover",
      sceneType: "cover",
      startFrame: 0,
      endFrame: 90,
      narration: "",
      wordTimestamps: [],
      audioFilename: "",
      keyframes: [
        { type: "text", content: "文本转视频，一键生成" },
      ],
      layoutHint: "centered",
    },
    {
      id: "scene-1",
      sceneType: "content",
      startFrame: 90,
      endFrame: 180,
      narration: "欢迎使用 Pipeline 视频生成工具",
      wordTimestamps: [],
      audioFilename: "",
      keyframes: [
        { type: "text", content: "支持资讯、知识、观点、营销四大模板" },
      ],
      layoutHint: "centered",
    },
    {
      id: "outro",
      sceneType: "outro",
      startFrame: 180,
      endFrame: 240,
      narration: "感谢观看",
      wordTimestamps: [],
      audioFilename: "",
      keyframes: [],
      layoutHint: "centered",
    },
  ],
  template: "news",
  platform: "bilibili",
  title: "Pipeline Demo",
  subtitle: "结构化视频生成",
  outro: { text: "感谢观看", cta: "点赞 | 关注" },
  channelName: "Pipeline",
  globalStyle: { tone: "formal", pace: "normal" },
};

export const RemotionRoot: React.FC = () => {
  // Load bundled Noto Sans SC at component mount (staticFile() requires the
  // bundle context which is only ready after the component tree mounts).
  const [fontHandle] = useState(() => delayRender("Loading Noto Sans SC"));
  useEffect(() => {
    let cancelled = false;
    try {
      const style = document.createElement("style");
      style.textContent = `
        @font-face {
          font-family: "Noto Sans SC";
          src: local("Noto Sans CJK SC"),
               url(${staticFile("fonts/NotoSansSC-Regular.ttf")}) format("truetype");
          font-weight: 400;
          font-display: block;
        }
        @font-face {
          font-family: "Noto Sans SC";
          src: local("Noto Sans CJK SC"),
               url(${staticFile("fonts/NotoSansSC-Bold.ttf")}) format("truetype");
          font-weight: 700;
          font-display: block;
        }
      `;
      document.head.appendChild(style);

      const fonts = document.fonts as unknown as {
        load: (font: string) => Promise<unknown>;
      };
      Promise.all([
        fonts.load('400 16px "Noto Sans SC"'),
        fonts.load('700 16px "Noto Sans SC"'),
      ])
        .then(() => {
          if (!cancelled) continueRender(fontHandle);
        })
        .catch((err) => {
          console.error("Font loading failed:", err);
          if (!cancelled) continueRender(fontHandle);
        });
    } catch (err) {
      console.error("Font init failed:", err);
      continueRender(fontHandle);
    }
    return () => {
      cancelled = true;
    };
  }, [fontHandle]);

  return (
    <>
      {TEMPLATES.map((template) =>
        (
          Object.entries(ASPECT_RATIOS) as [
            AspectRatio,
            { width: number; height: number },
          ][]
        ).map(([ratio, dims]) => (
          <Composition
            key={`${template}-${RATIO_ID[ratio]}`}
            id={`${template}-${RATIO_ID[ratio]}`}
            component={TemplateComposition as unknown as React.FC<Record<string, unknown>>}
            durationInFrames={180}
            fps={30}
            width={dims.width}
            height={dims.height}
            defaultProps={defaultProps}
            calculateMetadata={async (params) => {
              const p = params.props as unknown as RemotionProps;
              return {
                durationInFrames: p.scenes.at(-1)?.endFrame ?? 90,
                props: params.props,
              };
            }}
          />
        ))
      )}
    </>
  );
};
