// Album Viewer — play rendered song videos or fall back to scene preview.

const VIEWER_STORAGE_BASE = window.SongfilmApiConfig.getBase("storage");
const VIEWER_RENDERER_BASE = window.SongfilmApiConfig.getBase("remotion");

function getViewerRenderedVideoUrl(song) {
  const videoFilename = String(song?.exportState?.videoFilename || "").trim();
  if (videoFilename) {
    return window.SongfilmApiConfig?.buildMediaUrl
      ? window.SongfilmApiConfig.buildMediaUrl("video", videoFilename)
      : `${VIEWER_STORAGE_BASE}/video/${encodeURIComponent(videoFilename)}`;
  }
  const jobId = String(song?.exportState?.jobId || "").trim();
  if (jobId) {
    return `${VIEWER_RENDERER_BASE}/render/${jobId}/file`;
  }
  return "";
}

function Viewer({ album, initialSongId, onExit, onDownloadAlbum }) {
  const { Icon, SceneImage, fmtTime, fmtTimeMs } = window;
  const readySongs = album.songs.filter((song) => song.status === "ready");
  const initialSongIndex = Math.max(0, readySongs.findIndex((song) => song.id === initialSongId));
  const [mode, setMode] = useState(initialSongId ? "single" : "album");
  const [currentIdx, setCurrentIdx] = useState(initialSongIndex);
  const [t, setT] = useState(0);
  const [playing, setPlaying] = useState(!!initialSongId);
  const [videoUrl, setVideoUrl] = useState("");
  const [videoLoading, setVideoLoading] = useState(false);
  const videoRef = useRef(null);
  const stageRef = useRef(null);
  const objectUrlRef = useRef("");
  const queueItemRefs = useRef({});
  const [isFullscreen, setIsFullscreen] = useState(false);

  const currentSong = readySongs[currentIdx];
  if (!currentSong) {
    return (
      <div className="viewer-shell">
        <div className="empty-state">완성된 노래가 없습니다. 먼저 노래를 만들어 주세요.</div>
        <div style={{textAlign:"center", marginTop: 20}}>
          <button className="pill-btn" onClick={onExit}><Icon.ArrowLeft/> 돌아가기</button>
        </div>
      </div>
    );
  }

  const fallbackSongDur = currentSong.cues[currentSong.cues.length - 1]?.end || currentSong.duration || 0;
  const songDur = videoRef.current?.duration || fallbackSongDur;
  const activeCue = currentSong.cues.find((cue) => t >= cue.start && t < cue.end);
  const activeSceneIdx = currentSong.scenes.findIndex((scene) => t >= scene.start && t < scene.end);
  const currentScene = currentSong.scenes[Math.max(0, activeSceneIdx)];
  const sceneProgress = currentScene
    ? Math.max(0, Math.min(1, (t - currentScene.start) / Math.max(0.001, (currentScene.end - currentScene.start))))
    : 0;
  const smoothProgress = sceneProgress * sceneProgress * (3 - 2 * sceneProgress);
  const motionSeed = Number(currentScene?.imageSeed || 1) + Math.max(0, activeSceneIdx) * 97;
  const motionRand = (n) => {
    const x = Math.sin(motionSeed * 31 + n * 17) * 10000;
    return x - Math.floor(x);
  };
  const startX = (motionRand(1) - 0.5) * 1.2;
  const endX = (motionRand(2) - 0.5) * 1.2;
  const startY = (motionRand(3) - 0.5) * 0.9;
  const endY = (motionRand(4) - 0.5) * 0.9;
  const panX = startX + (endX - startX) * smoothProgress;
  const panY = startY + (endY - startY) * smoothProgress;
  const imageMotionStyle = currentScene?.imageUrl
    ? {
        transform: `translate3d(${panX}%, ${panY}%, 0) scale(${1.012 + smoothProgress * 0.045})`,
        transformOrigin: `${35 + motionRand(5) * 30}% ${35 + motionRand(6) * 30}%`,
        transition: "transform 100ms linear",
      }
    : {};
  const hasRenderedVideo = !!videoUrl;

  useEffect(() => {
    const currentQueueItem = queueItemRefs.current[currentSong.id];
    if (!currentQueueItem) return;
    currentQueueItem.scrollIntoView({
      behavior: "smooth",
      block: "nearest",
      inline: "center",
    });
  }, [currentSong.id]);

  useEffect(() => {
    let cancelled = false;

    const cleanupObjectUrl = () => {
      if (objectUrlRef.current) {
        URL.revokeObjectURL(objectUrlRef.current);
        objectUrlRef.current = "";
      }
    };

    const loadVideo = async () => {
      cleanupObjectUrl();
      const storageVideoUrl = String(currentSong?.exportState?.videoFilename || "").trim();
      if (storageVideoUrl) {
        setVideoUrl(window.SongfilmApiConfig?.buildMediaUrl
          ? window.SongfilmApiConfig.buildMediaUrl("video", storageVideoUrl)
          : `${VIEWER_STORAGE_BASE}/video/${encodeURIComponent(storageVideoUrl)}`);
        setVideoLoading(false);
        return;
      }

      const fallbackVideoUrl = getViewerRenderedVideoUrl(currentSong);
      if (!fallbackVideoUrl) {
        setVideoUrl("");
        setVideoLoading(false);
        return;
      }

      setVideoLoading(true);
      try {
        const resp = await fetch(fallbackVideoUrl);
        if (!resp.ok) throw new Error(`비디오 로드 실패 (${resp.status})`);
        const blob = await resp.blob();
        const objectUrl = URL.createObjectURL(blob);
        objectUrlRef.current = objectUrl;
        if (!cancelled) {
          setVideoUrl(objectUrl);
        }
      } catch {
        if (!cancelled) {
          setVideoUrl("");
        }
      } finally {
        if (!cancelled) {
          setVideoLoading(false);
        }
      }
    };

    setT(0);
    loadVideo();

    return () => {
      cancelled = true;
      cleanupObjectUrl();
    };
  }, [currentSong.id, currentSong.exportState?.videoFilename, currentSong.exportState?.jobId]);

  useEffect(() => {
    const video = videoRef.current;
    if (!video || !hasRenderedVideo) return;

    const syncTime = () => setT(video.currentTime || 0);
    const syncPlay = () => setPlaying(true);
    const syncPause = () => {
      if (!video.ended) setPlaying(false);
    };
    const handleEnded = () => {
      if (mode === "album" && currentIdx < readySongs.length - 1) {
        setCurrentIdx((prev) => prev + 1);
        setT(0);
        return;
      }
      setPlaying(false);
      setT(0);
    };

    video.addEventListener("timeupdate", syncTime);
    video.addEventListener("play", syncPlay);
    video.addEventListener("pause", syncPause);
    video.addEventListener("ended", handleEnded);

    return () => {
      video.removeEventListener("timeupdate", syncTime);
      video.removeEventListener("play", syncPlay);
      video.removeEventListener("pause", syncPause);
      video.removeEventListener("ended", handleEnded);
    };
  }, [hasRenderedVideo, mode, currentIdx, readySongs.length]);

  useEffect(() => {
    const video = videoRef.current;
    if (!video || !hasRenderedVideo) return;
    if (playing) {
      const playPromise = video.play();
      if (playPromise && playPromise.catch) {
        playPromise.catch(() => setPlaying(false));
      }
      return;
    }
    video.pause();
  }, [playing, hasRenderedVideo, videoUrl, currentIdx]);

  useEffect(() => {
    if (hasRenderedVideo) return;
    if (!playing) return;
    let rafId;
    let last = performance.now();
    const tick = (now) => {
      setT((prev) => {
        const dt = (now - last) / 1000;
        last = now;
        const next = prev + dt;
        if (next >= fallbackSongDur) {
          if (mode === "album" && currentIdx < readySongs.length - 1) {
            setCurrentIdx((prevIdx) => prevIdx + 1);
            return 0;
          }
          setPlaying(false);
          return 0;
        }
        return next;
      });
      rafId = requestAnimationFrame(tick);
    };
    rafId = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafId);
  }, [playing, hasRenderedVideo, fallbackSongDur, mode, currentIdx, readySongs.length]);

  useEffect(() => {
    const syncFullscreen = () => {
      setIsFullscreen(document.fullscreenElement === stageRef.current);
    };

    document.addEventListener("fullscreenchange", syncFullscreen);
    return () => document.removeEventListener("fullscreenchange", syncFullscreen);
  }, []);

  const scrub = (e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    const nextTime = pct * Math.max(songDur, 0.001);
    setT(nextTime);
    if (hasRenderedVideo && videoRef.current) {
      videoRef.current.currentTime = nextTime;
    }
  };

  const playSong = (index) => {
    setCurrentIdx(index);
    setMode("single");
    setT(0);
    setPlaying(true);
  };

  const toggleFullscreen = async () => {
    const stage = stageRef.current;
    if (!stage || typeof stage.requestFullscreen !== "function") return;

    try {
      if (document.fullscreenElement === stage) {
        await document.exitFullscreen();
      } else {
        await stage.requestFullscreen();
      }
    } catch (error) {
      console.warn("[Viewer] fullscreen toggle failed", error);
    }
  };

  const albumDur = readySongs.reduce((acc, song) => acc + (song.cues[song.cues.length - 1]?.end || song.duration || 0), 0);
  const albumElapsed = readySongs.slice(0, currentIdx).reduce((acc, song) => acc + (song.cues[song.cues.length - 1]?.end || song.duration || 0), 0) + t;

  return (
    <div className="viewer-shell">
      <div style={{display:"flex", alignItems:"flex-end", justifyContent:"space-between", marginBottom: 22}}>
        <div>
          <div style={{fontSize: 11, color:"var(--ink-3)", textTransform:"uppercase", letterSpacing:"0.1em", fontWeight:600}}>
            {mode === "album" ? "앨범 재생" : "개별 재생"}
          </div>
          <div style={{fontFamily:"var(--font-display)", fontWeight:600, fontSize: 36, letterSpacing:"-0.02em", marginTop: 4}}>
            {album.title}
          </div>
          <div style={{color:"var(--ink-3)", fontSize: 13, marginTop: 2}}>
            {album.artist} · {readySongs.length}곡
          </div>
        </div>
        <div style={{display:"flex", gap: 8}}>
          <div className="seg">
            <button className={mode === "album" ? "active" : ""} onClick={() => setMode("album")}>앨범 모드</button>
            <button className={mode === "single" ? "active" : ""} onClick={() => setMode("single")}>단일 모드</button>
          </div>
          <button className="pill-btn" onClick={onDownloadAlbum}><Icon.Download/> 앨범 다운로드</button>
          <button className="pill-btn" onClick={onExit}><Icon.X/> 닫기</button>
        </div>
      </div>

      <div className="viewer-stage" ref={stageRef}>
        {hasRenderedVideo ? (
          <video className="viewer-video" ref={videoRef} preload="metadata" src={videoUrl} />
        ) : (
          <>
            <div key={currentScene?.id} style={{
              position:"absolute", inset:0,
              ...imageMotionStyle,
            }}>
              {currentScene && <SceneImage scene={currentScene} paletteName={currentSong.paletteName || "midnight-violet"}/>}
            </div>

            {activeCue && (
              <div className="video-subtitle" key={activeCue.start} style={{position:"absolute", bottom:"14%"}}>
                <div className="text" style={{fontSize: 44}}>{activeCue.text}</div>
              </div>
            )}
          </>
        )}

        <div style={{
          position:"absolute", top: 24, left: 28, right: 28,
          display:"flex", justifyContent:"space-between", alignItems:"flex-start",
          color:"#fff", textShadow:"0 2px 12px rgba(0,0,0,0.6)", pointerEvents:"none",
        }}>
          <div>
            <div style={{fontSize: 11, opacity: 0.7, textTransform:"uppercase", letterSpacing:"0.1em", fontWeight:600}}>
              {mode === "album" ? `트랙 ${currentIdx + 1}/${readySongs.length}` : "지금 재생 중"}
            </div>
            <div style={{fontFamily:"var(--font-display)", fontSize: 22, fontWeight: 600, marginTop: 4, letterSpacing:"-0.01em"}}>
              {currentSong.title}
            </div>
          </div>
          <div style={{fontFamily:"var(--font-mono)", fontSize: 11, opacity: 0.7}}>
            씬 {Math.max(0, activeSceneIdx) + 1}/{currentSong.scenes.length}
          </div>
        </div>

        {!playing && !videoLoading && (
          <button
            onClick={() => setPlaying(true)}
            style={{position:"absolute", inset: 0, display:"flex", alignItems:"center", justifyContent:"center", background:"rgba(0,0,0,0.25)", color:"#fff"}}
          >
            <div style={{width: 80, height: 80, borderRadius:"50%", background:"rgba(255,255,255,0.95)", color:"var(--bg)", display:"flex", alignItems:"center", justifyContent:"center", boxShadow:"0 20px 60px rgba(0,0,0,0.6)"}}>
              <Icon.Play size={32}/>
            </div>
          </button>
        )}
      </div>

      <div className="viewer-controls">
        <button className="play-circle-btn" onClick={() => setPlaying((prev) => !prev)}>
          {playing ? <Icon.Pause size={14}/> : <Icon.Play size={14}/>}
        </button>
        <div className="preview-time mono">
          {mode === "album"
            ? `${fmtTimeMs(albumElapsed).slice(0,8)} / ${fmtTimeMs(albumDur).slice(0,8)}`
            : `${fmtTimeMs(t).slice(0,8)} / ${fmtTimeMs(songDur).slice(0,8)}`}
        </div>
        <div className="preview-scrub" onClick={scrub}>
          <div className="fill" style={{width: `${songDur > 0 ? (t / songDur) * 100 : 0}%`}}/>
        </div>
        <button className="pill-btn" onClick={() => {
          if (currentIdx > 0) { setCurrentIdx(currentIdx - 1); setT(0); }
        }} disabled={currentIdx === 0}>◀◀</button>
        <button className="pill-btn" onClick={() => {
          if (currentIdx < readySongs.length - 1) { setCurrentIdx(currentIdx + 1); setT(0); }
        }} disabled={currentIdx >= readySongs.length - 1}>▶▶</button>
        <button className="pill-btn" onClick={toggleFullscreen} title="전체 화면">
          <Icon.Expand/>{isFullscreen ? "전체화면 종료" : "전체화면"}
        </button>
      </div>

      {mode === "album" && (
        <div style={{marginTop: 14, display:"flex", gap: 4, height: 6}}>
          {readySongs.map((song, index) => {
            const isPast = index < currentIdx;
            const isCurrent = index === currentIdx;
            const dur = song.cues[song.cues.length - 1]?.end || song.duration || 1;
            return (
              <div key={song.id} style={{flex: dur, background:"var(--bg-2)", borderRadius: 3, overflow:"hidden"}}>
                <div style={{
                  height:"100%",
                  width: isPast ? "100%" : isCurrent ? `${songDur > 0 ? (t / songDur) * 100 : 0}%` : "0%",
                  background: "var(--accent)",
                  transition: "width 100ms linear",
                }}/>
              </div>
            );
          })}
        </div>
      )}

      <div style={{marginTop: 20, fontSize: 11, color:"var(--ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600}}>
        트랙 리스트
      </div>
      <div className="viewer-queue">
        {readySongs.map((song, index) => (
          <div
            key={song.id}
            ref={(node) => {
              if (node) {
                queueItemRefs.current[song.id] = node;
              } else {
                delete queueItemRefs.current[song.id];
              }
            }}
            className={`queue-item ${index === currentIdx ? "current" : ""}`}
            onClick={() => playSong(index)}
          >
            <div className="thumb">
              {song.scenes[0] && <SceneImage scene={song.scenes[0]} paletteName={song.paletteName || "midnight-violet"}/>}
              <div style={{position:"absolute", bottom: 4, right: 6, fontFamily:"var(--font-mono)", fontSize: 10, color:"#fff", textShadow:"0 1px 2px rgba(0,0,0,0.8)"}}>
                {fmtTime(song.cues[song.cues.length - 1]?.end || song.duration)}
              </div>
              {index === currentIdx && playing && (
                <div style={{position:"absolute", top: 6, left: 6, color:"#fff"}}>
                  <Icon.Play size={12}/>
                </div>
              )}
            </div>
            <div className="name">{index + 1}. {song.title}</div>
            <div className="sub">{song.scenes.length} 씬</div>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { Viewer });
