// App-level modal dialogs: batch render, new album, download

const { useState: useStateModal, useEffect: useEffectModal } = React;

function BatchRenderModal({
  state,
  onStart,
  onStop,
  onClose,
  onToggleSong,
  onSelectAll,
  onClearSelection,
  onForceRerenderChange,
}) {
  const { Icon } = window;
  const statusLabel = {
    queued: "대기",
    excluded: "제외",
    skipped: "건너뜀",
    checking: "확인",
    pending: "대기",
    uploading: "오디오",
    bundling: "번들링",
    rendering: "렌더링",
    "uploading-video": "업로드",
    done: "완료",
    error: "실패",
    stopped: "중지",
  };
  const doneCount = state.items.filter((item) => item.status === "done" || item.status === "skipped").length;
  const errorCount = state.items.filter((item) => item.status === "error").length;
  const isFinished = !state.running && ["done", "stopped", "error"].includes(state.status);
  const isConfig = !state.running && state.status === "config";
  const selectedCount = (state.selectedSongIds || []).length;

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal batch-render-modal" onClick={e => e.stopPropagation()}>
        <div className="batch-render-head">
          <div>
            <h3>배치 비디오 렌더링</h3>
            <p>
              {isConfig
                ? `완성곡 ${state.total}곡 중 다시 렌더링할 곡을 선택하세요.`
                : `선택한 ${state.total}곡을 앨범 순서대로 렌더링합니다. 중지해도 이미 시작된 서버 렌더는 계속될 수 있습니다.`}
            </p>
          </div>
          <div className={`batch-render-status ${state.status}`}>
            {state.running ? "실행 중" : state.status === "config" ? "설정" : state.status === "done" ? "완료" : state.status === "stopped" ? "중지됨" : state.status === "error" ? "오류" : "대기"}
          </div>
        </div>

        {isConfig && (
          <div className="batch-render-options">
            <label className="batch-render-option">
              <input
                type="checkbox"
                checked={state.forceRerender}
                onChange={(event) => onForceRerenderChange(event.target.checked)}
              />
              <span>
                <strong>모두 처음부터 다시 렌더링</strong>
                <em>기존 MP4가 있어도 선택한 곡은 새 `/render` 작업을 시작합니다.</em>
              </span>
            </label>
            <div className="batch-render-select-actions">
              <button className="pill-btn sm" onClick={onSelectAll}>전체 선택</button>
              <button className="pill-btn sm" onClick={onClearSelection}>전체 제외</button>
              <span className="mono">{selectedCount}/{state.items.length}곡 선택</span>
            </div>
          </div>
        )}

        <div className="batch-render-summary">
          <div>
            <strong>{Math.round(state.progress || 0)}%</strong>
            <span>전체 진행률</span>
          </div>
          <div>
            <strong>{isConfig ? selectedCount : `${doneCount}/${state.total}`}</strong>
            <span>{isConfig ? "선택됨" : "완료/건너뜀"}</span>
          </div>
          <div>
            <strong>{errorCount}</strong>
            <span>실패</span>
          </div>
        </div>

        <div className="progress-track batch-render-progress">
          <div className="bar" style={{width:`${Math.min(100, Math.max(0, state.progress || 0))}%`}}/>
        </div>
        <div className="batch-render-stage mono">
          {state.stopping ? "중지 요청됨 · 다음 곡은 시작하지 않습니다." : state.stage || "작업 준비 중…"}
        </div>

        <div className="batch-render-list">
          {state.items.map((item, index) => (
            <div key={item.songId} className={`batch-render-row ${item.status} ${state.currentSongId === item.songId ? "active" : ""}`}>
              <div className="batch-render-index mono">
                {isConfig ? (
                  <input
                    className="batch-render-check"
                    type="checkbox"
                    checked={!!item.selected}
                    onChange={() => onToggleSong(item.songId)}
                    onClick={(event) => event.stopPropagation()}
                  />
                ) : (
                  String(index + 1).padStart(2, "0")
                )}
              </div>
              <div className="batch-render-title">
                <span>{item.title}</span>
                <em>{item.message || statusLabel[item.status] || item.status}</em>
              </div>
              <div className="batch-render-mini">
                <div style={{width: `${Math.min(100, Math.max(0, item.progress || 0))}%`}}/>
              </div>
              <span className={`batch-render-badge ${item.status}`}>
                {statusLabel[item.status] || item.status}
              </span>
            </div>
          ))}
        </div>

        {state.error && (
          <div className="batch-render-error">
            {state.error}
          </div>
        )}

        <div className="modal-actions">
          {isConfig ? (
            <>
              <button className="pill-btn" onClick={onClose}>취소</button>
              <button className="pill-btn primary" onClick={onStart} disabled={selectedCount === 0}>
                <Icon.Film size={13}/> {state.forceRerender ? "선택한 곡 처음부터 다시 렌더링" : "선택한 곡 렌더링"}
              </button>
            </>
          ) : state.running ? (
            <button className="pill-btn danger" onClick={onStop} disabled={state.stopping}>
              {state.stopping ? <><Icon.Loader size={13}/> 중지 중…</> : <><Icon.X size={12}/> 중지</>}
            </button>
          ) : (
            <button className="pill-btn primary" onClick={onClose}>
              {isFinished ? "닫기" : "확인"}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

function NewAlbumModal({ onCreate, onClose }) {
  const { Icon } = window;
  const [title, setTitle] = useStateModal("");
  const [artist, setArtist] = useStateModal("");
  const [year, setYear] = useStateModal(new Date().getFullYear());

  useEffectModal(() => {
    const onKey = (e) => {
      if (e.key === "Escape") onClose();
      if (e.key === "Enter" && title.trim()) onCreate({ title, artist, year: +year });
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [title, artist, year]);

  const submit = () => {
    if (!title.trim()) { window.toast("앨범 제목을 입력해주세요"); return; }
    onCreate({ title, artist, year: +year });
  };

  const inputStyle = {
    width:"100%", background:"var(--bg-2)", border:"1px solid var(--line)",
    borderRadius: 8, padding:"10px 12px", fontSize: 14, outline:"none", marginBottom: 10,
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{width: 440}}>
        <h3>새 앨범 만들기</h3>
        <p>앨범 정보를 입력하세요. 나중에 앨범 홈에서 제목을 수정할 수 있습니다.</p>

        <label style={{fontSize: 11, color:"var(--ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, display:"block", marginBottom: 4}}>
          앨범 제목
        </label>
        <input
          autoFocus
          placeholder="예: 밤의 편지"
          value={title}
          onChange={e => setTitle(e.target.value)}
          style={inputStyle}
        />

        <label style={{fontSize: 11, color:"var(--ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, display:"block", marginBottom: 4}}>
          아티스트
        </label>
        <input
          placeholder="예: 윤하린"
          value={artist}
          onChange={e => setArtist(e.target.value)}
          style={inputStyle}
        />

        <label style={{fontSize: 11, color:"var(--ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, display:"block", marginBottom: 4}}>
          연도
        </label>
        <input
          type="number"
          value={year}
          onChange={e => setYear(e.target.value)}
          style={{...inputStyle, fontFamily:"var(--font-mono)"}}
        />

        <div className="modal-actions">
          <button className="pill-btn" onClick={onClose}>취소</button>
          <button className="pill-btn primary" onClick={submit}>
            <Icon.Plus size={12}/> 앨범 생성
          </button>
        </div>
      </div>
    </div>
  );
}

function DownloadModal({ album, setAlbum, onClose }) {
  const { Icon } = window;
  const ready = album.songs.filter(s => s.status === "ready");
  const [bundling, setBundling] = useStateModal(false);
  const [progress, setProgress] = useStateModal(0);
  const [stage, setStage] = useStateModal("");
  const [errorMsg, setErrorMsg] = useStateModal("");

  const patchSongExportState = (songId, patch) => {
    setAlbum((prevAlbum) => ({
      ...prevAlbum,
      songs: prevAlbum.songs.map((song) => (
        song.id !== songId
          ? song
          : {
              ...song,
              exportState: {
                ...(song.exportState || {}),
                ...patch,
              },
            }
      )),
    }));
  };

  const ensureRenderedVideo = async (song, songIndex) => {
    const existingVideoFilename = String(song?.exportState?.videoFilename || "").trim();
    const existingJobId = String(song?.exportState?.jobId || "").trim();
    const existingDone = song?.exportState?.status === "done" || !!existingVideoFilename;
    const segmentStart = 8 + Math.floor((songIndex / Math.max(ready.length, 1)) * 52);
    const segmentSize = Math.max(8, Math.floor(52 / Math.max(ready.length, 1)));

    if (existingDone && (existingVideoFilename || existingJobId)) {
      return {
        ...song,
        exportState: {
          ...(song.exportState || {}),
          status: "done",
          done: true,
          progress: 100,
        },
      };
    }

    let audioFilename = getAlbumStorageAudioFilename(song);
    if (!audioFilename) {
      setStage(`${song.title} 오디오 업로드 중…`);
      const audioBlob = await loadAlbumSongAudioBlob(song);
      const audioForm = new FormData();
      audioForm.append("file", audioBlob, song.filename || `${song.title}.mp3`);
      const audioResp = await fetch(`${ALBUM_STORAGE_BASE}/audio/save`, { method: "POST", body: audioForm });
      if (!audioResp.ok) {
        throw new Error(`${song.title} 오디오 업로드 실패 (${audioResp.status})`);
      }
      const audioData = await audioResp.json();
      audioFilename = String(audioData?.filename || "").trim();
      patchSongExportState(song.id, { audioFilename });
    }

    setStage("렌더링 서버 확인 중…");
    const healthResp = await fetch(`${ALBUM_RENDERER_BASE}/health`);
    if (!healthResp.ok) {
      throw new Error(`렌더링 서버 연결 실패 (${healthResp.status})`);
    }

    const songData = {
      title: song.title,
      filename: song.filename || "audio.mp3",
      duration: song.duration,
      paletteName: song.paletteName || "midnight-violet",
      previewSettings: song.previewSettings || {},
      scenes: song.scenes || [],
      sceneGlobal: song.sceneGlobal || {},
      lyricTimeline: song.lyricTimeline || null,
    };

    setStage(`${song.title} 비디오 렌더링 시작…`);
    const startResp = await fetch(`${ALBUM_RENDERER_BASE}/render`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ songData, audioFilename }),
    });
    if (!startResp.ok) {
      let errMsg = `${song.title} 렌더 시작 실패 (${startResp.status})`;
      try {
        const errBody = await startResp.json();
        errMsg = errBody?.error || errBody?.message || errMsg;
      } catch {}
      throw new Error(errMsg);
    }

    const { jobId } = await startResp.json();
    patchSongExportState(song.id, {
      status: "bundling",
      done: false,
      progress: 0,
      error: "",
      audioFilename,
      jobId,
    });

    for (;;) {
      await waitAlbumMs(1200);
      const statusResp = await fetch(`${ALBUM_RENDERER_BASE}/render/${jobId}/status`);
      if (!statusResp.ok) {
        throw new Error(`${song.title} 렌더 상태 조회 실패 (${statusResp.status})`);
      }
      const statusData = await statusResp.json();
      const status = String(statusData?.status || "pending");
      const renderProgress = Math.round(Number(statusData?.progress || 0) * 100);
      const overallProgress = segmentStart + Math.round((Math.min(100, renderProgress) / 100) * segmentSize);
      setProgress(Math.min(66, overallProgress));
      setStage(`${song.title} ${status === "bundling" ? "번들링" : status === "uploading" ? "업로드" : "렌더링"} 중… ${Math.min(100, renderProgress)}%`);
      patchSongExportState(song.id, {
        status,
        done: status === "done",
        progress: renderProgress,
        error: statusData?.error || "",
        audioFilename,
        jobId,
        videoFilename: statusData?.videoFilename || "",
      });

      if (status === "done") {
        return {
          ...song,
          exportState: {
            ...(song.exportState || {}),
            status: "done",
            done: true,
            progress: 100,
            audioFilename,
            jobId,
            videoFilename: statusData?.videoFilename || "",
          },
        };
      }
      if (status === "error") {
        throw new Error(statusData?.error || `${song.title} 렌더링 실패`);
      }
    }
  };

  const start = async () => {
    if (!ready.length) return;
    setBundling(true);
    setProgress(0);
    setStage("JSZip 로드 중…");
    setErrorMsg("");

    try {
      const JSZip = await loadAlbumJSZip();
      const zip = new JSZip();
      const albumBase = sanitizeAlbumEntryName(album.title || "album", "album");
      const audioFolder = zip.folder("Audio");
      const videoFolder = zip.folder("Video");
      const renderedSongs = [];

      setProgress(8);
      setStage("곡 비디오 준비 중…");

      for (let index = 0; index < ready.length; index += 1) {
        renderedSongs.push(await ensureRenderedVideo(ready[index], index));
      }

      setProgress(60);
      setStage("곡 파일 수집 중…");

      for (let index = 0; index < renderedSongs.length; index += 1) {
        const song = renderedSongs[index];
        const songBase = sanitizeAlbumEntryName(
          (song.filename || song.title || `track-${index + 1}`).replace(/\.[^.]+$/, ""),
          `track-${index + 1}`
        );
        const order = String(index + 1).padStart(2, "0");
        const audioName = `${order}_${song.filename || `${songBase}.mp3`}`;
        const videoName = `${order}_${songBase}.mp4`;
        const segmentStart = 60 + Math.floor((index / Math.max(renderedSongs.length, 1)) * 6);
        setProgress(segmentStart);
        setStage(`${song.title || songBase} 파일 수집 중…`);

        try {
          audioFolder.file(audioName, await loadAlbumSongAudioBlob(song));
        } catch (e) {
          console.warn("[Album ZIP] 오디오 수집 실패", song.title, e);
        }

        try {
          videoFolder.file(videoName, await loadAlbumSongVideoBlob(song));
        } catch (e) {
          console.warn("[Album ZIP] 비디오 수집 실패", song.title, e);
        }
      }

      setProgress(74);
      setStage("zip 압축 중…");
      const blob = await zip.generateAsync(
        {
          type: "blob",
          compression: "DEFLATE",
          compressionOptions: { level: 6 },
        },
        (metadata) => {
          const current = 74 + Math.round((metadata.percent || 0) * 0.25);
          setProgress(Math.min(99, current));
        }
      );

      const filename = `${albumBase}.zip`;
      triggerAlbumBlobDownload(blob, filename);
      setProgress(100);
      setStage("다운로드 시작…");
      window.toast("앨범 zip 다운로드 시작");
      onClose();
    } catch (e) {
      console.error("[Album ZIP] 생성 실패:", e);
      setErrorMsg(e.message || String(e));
      window.toast("앨범 zip 생성 실패: " + (e.message || e));
    } finally {
      setBundling(false);
    }
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{width: 520}}>
        <h3>앨범 다운로드</h3>
        <p>{album.title} · {ready.length}곡의 mp3와 Remotion 비디오를 바로 zip으로 묶어 다운로드합니다.</p>

        <div className="file-list">
          <div className="file-row"><Icon.Album size={14}/><span>{album.title}.zip — 루트</span><span className="sz">~{(ready.length*14).toFixed(0)} MB</span></div>
          <div className="file-row" style={{paddingLeft: 24}}>
            <Icon.Music size={12}/><span>Audio/</span><span className="sz">{ready.length} files</span>
          </div>
          <div className="file-row" style={{paddingLeft: 24}}>
            <Icon.Film size={12}/><span>Video/</span><span className="sz">{ready.length} files</span>
          </div>
          {ready.map((s, i) => (
            <React.Fragment key={s.id}>
              <div className="file-row" style={{paddingLeft: 40}}>
                <Icon.Music size={12}/><span>Audio/{(i+1).toString().padStart(2,"0")}_{s.filename || `${s.title}.mp3`}</span><span className="sz">{s.size}</span>
              </div>
              <div className="file-row" style={{paddingLeft: 40}}>
                <Icon.Film size={12}/><span>Video/{(i+1).toString().padStart(2,"0")}_{(s.filename || s.title).replace(/\.[^.]+$/, "")}.mp4</span><span className="sz">~12 MB</span>
              </div>
            </React.Fragment>
          ))}
        </div>

        {bundling && (
          <div style={{marginTop: 16}}>
            <div className="progress-track"><div className="bar" style={{width:`${progress}%`}}/></div>
            <div style={{fontSize: 11, color:"var(--ink-3)", fontFamily:"var(--font-mono)", marginTop: 6}}>
              {stage || "작업 준비 중…"}
            </div>
          </div>
        )}

        {errorMsg && (
          <div style={{marginTop: 16, color:"oklch(70% 0.17 24)", fontSize: 12}}>
            실패: {errorMsg}
          </div>
        )}

        <div className="modal-actions">
          <button className="pill-btn" onClick={onClose}>닫기</button>
          <button className="pill-btn primary" onClick={start} disabled={bundling}>
            {bundling ? <><Icon.Loader size={13}/> ZIP 준비 중…</> : <><Icon.Download/> 다운로드</>}
          </button>
        </div>
      </div>
    </div>
  );
}
