// Cliente Kie.ai — Ultimo Verso · Àltima
//
// IMPORTANTE: este cliente NO habla con Kie.ai directamente. Habla con
// NUESTRO backend (Vercel Functions en /api/*), que es quien custodia
// la clave de Kie.ai, sube los MP3 a R2 y escribe en Airtable.
//
// Flujo:
//   1) POST /api/generate     → crea taskId y registro Airtable (estado "Generando")
//   2) GET  /api/status?id=…  → polling hasta SUCCESS
//   3) POST /api/finalize     → descarga MP3 a R2, actualiza Airtable, genera QR
//
// El componente UvComposing llama a UV_KIE.compose(data, lyrics, onProgress)
// y recibe { audioUrl, streamUrl, songId, qrUrl, publicUrl }.

const UV_KIE = {
  // Endpoint base: por defecto el mismo origen que la app (Vercel).
  // Sobreescribible vía localStorage para desarrollo local apuntando a otro host.
  baseUrl() {
    try {
      const raw = localStorage.getItem("uv.kie.baseUrl");
      if (raw) return raw;
    } catch (e) {}
    return ""; // mismo origen
  },
  setBaseUrl(url) {
    if (url) localStorage.setItem("uv.kie.baseUrl", url);
    else localStorage.removeItem("uv.kie.baseUrl");
  },

  // En producción siempre intentamos generar (la clave vive en backend).
  // Permitimos al usuario forzar el modo "sintético" (sin Kie) si quiere
  // probar la UI sin gastar créditos.
  isConfigured() {
    try {
      const off = localStorage.getItem("uv.kie.disabled");
      if (off === "1") return false;
    } catch (e) {}
    return true;
  },
  setEnabled(on) {
    if (on) localStorage.removeItem("uv.kie.disabled");
    else localStorage.setItem("uv.kie.disabled", "1");
  },

  cfg() {
    return {
      baseUrl: this.baseUrl(),
      model: "V4_5",
      enabled: this.isConfigured(),
    };
  },

  // POST /api/generate
  async create(data, lyrics) {
    const res = await fetch(`${this.baseUrl()}/api/generate`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data, lyrics }),
    });
    if (!res.ok) {
      const txt = await res.text().catch(() => "");
      throw new Error(`Backend error ${res.status}: ${txt.slice(0,200)}`);
    }
    const json = await res.json();
    if (!json.ok) throw new Error(json.error || "Error en backend");
    // { ok:true, songId, taskId }
    return json;
  },

  // GET /api/status?id=…
  async status(songId) {
    const res = await fetch(`${this.baseUrl()}/api/status?id=${encodeURIComponent(songId)}`);
    if (!res.ok) throw new Error(`Status ${res.status}`);
    return await res.json();
  },

  // POST /api/finalize — descarga audios a R2, actualiza Airtable, devuelve URLs definitivas
  async finalize(songId) {
    const res = await fetch(`${this.baseUrl()}/api/finalize`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ songId }),
    });
    if (!res.ok) {
      const txt = await res.text().catch(() => "");
      throw new Error(`Finalize ${res.status}: ${txt.slice(0,200)}`);
    }
    return await res.json();
  },

  // POST /api/select — guarda qué versión (A o B) ha elegido el cliente
  async select(songId, choice) {
    const res = await fetch(`${this.baseUrl()}/api/select`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ songId, choice }),
    });
    if (!res.ok) {
      const txt = await res.text().catch(() => "");
      throw new Error(`Select ${res.status}: ${txt.slice(0,200)}`);
    }
    return await res.json();
  },

  // Flujo completo con polling. onProgress recibe (frase, %).
  // Devuelve { audioUrl, audioUrlAlt, streamUrl, imageUrl, songId, publicUrl, qrUrl, lyrics }
  async compose(data, lyrics, onProgress) {
    onProgress?.("Escribiendo la letra con IA…", 4);
    const create = await this.create(data, lyrics);
    const { songId, taskId, lyrics: refinedLyrics, lyricsSource } = create;
    if (lyricsSource === "claude") {
      onProgress?.("Letra refinada por Claude. Componiendo música…", 10);
    } else {
      onProgress?.("Enviando a Kie.ai…", 10);
    }

    const t0 = Date.now();
    const MAX_MS = 5 * 60 * 1000; // 5 min total
    const FALLBACK_AT_MS = 4 * 60 * 1000; // a los 4 min, acepta solo la A si no hay B
    let lastStatus = "";
    let lastSeen = null;

    while (Date.now() - t0 < MAX_MS) {
      await new Promise(r => setTimeout(r, 5000));
      let s;
      try { s = await this.status(songId); }
      catch (e) { onProgress?.("Consultando estado…", null); continue; }
      lastSeen = s;

      const elapsed = (Date.now() - t0) / 1000;
      const pseudoPct = Math.min(88, 12 + elapsed * 1.0);

      if (s.status !== lastStatus) {
        lastStatus = s.status;
        const phrase = {
          PENDING:       "Buscando una metáfora justa…",
          TEXT_SUCCESS:  "Letra lista. Dando forma al estribillo…",
          FIRST_SUCCESS: "Primera versión casi lista…",
          PROCESSING:    "Ajustando la métrica de la segunda versión…",
          SUCCESS:       "Casi lista.",
        }[s.status] || "Componiendo en silencio…";
        onProgress?.(phrase, pseudoPct);
      } else {
        onProgress?.(null, pseudoPct);
      }

      // CONDICIÓN IDEAL: ambas pistas listas (SUCCESS o ambas audioUrl presentes)
      const bothReady = !!(s.audioUrl && s.audioUrlAlt);
      const fallback  = !!s.audioUrl && (Date.now() - t0 > FALLBACK_AT_MS);
      const isSuccess = s.status === "SUCCESS";

      if (bothReady || isSuccess || fallback) {
        onProgress?.("Guardando tu canción…", 92);
        try {
          const fin = await this.finalize(songId);
          onProgress?.("Lista.", 100);
          return {
            audioUrl:    fin.audioUrl    || s.audioUrl,
            audioUrlAlt: fin.audioUrlAlt || s.audioUrlAlt || null,
            streamUrl:   s.streamUrl,
            streamUrlAlt:s.streamUrlAlt,
            imageUrl:    fin.imageUrl || s.imageUrl,
            songId,
            taskId,
            publicUrl:   fin.publicUrl,
            qrUrl:       fin.qrUrl,
            lyrics:      refinedLyrics,
          };
        } catch (e) {
          console.warn("[UV] finalize falló:", e);
          onProgress?.("Lista (sin guardar).", 100);
          return {
            audioUrl:    s.audioUrl,
            audioUrlAlt: s.audioUrlAlt || null,
            streamUrl:   s.streamUrl,
            streamUrlAlt:s.streamUrlAlt,
            imageUrl:    s.imageUrl,
            songId, taskId,
            publicUrl: null, qrUrl: null,
            lyrics:    refinedLyrics,
          };
        }
      }

      if (s.status === "FAILED" || s.status === "ERROR" ||
          s.status === "CREATE_TASK_FAILED" || s.status === "GENERATE_AUDIO_FAILED") {
        throw new Error(`Kie.ai: ${s.errorMessage || s.status}`);
      }
    }
    throw new Error("Tiempo de espera agotado.");
  },
};

// Compat: result.jsx histórico referenciaba UV_SUNO. Mantenemos alias para
// no romper la app si quedan referencias.
const UV_SUNO = UV_KIE;

Object.assign(window, { UV_KIE, UV_SUNO });
