// Image with shimmer skeleton + small→full fallback. Image fades in once decoded.
const SkeletonImg = ({ src, fullSrc, alt = "", style = {} }) => {
  const [actualSrc, setActualSrc] = React.useState(src || null);
  const [loaded, setLoaded] = React.useState(false);
  React.useEffect(() => { setActualSrc(src || null); setLoaded(false); }, [src]);
  const onError = () => {
    if (fullSrc && actualSrc !== fullSrc) setActualSrc(fullSrc);
  };
  return (
    <div style={{position: "relative", width: "100%", height: "100%", overflow: "hidden", ...style}}>
      {!loaded && <div className="thumb-skeleton" style={{position: "absolute", inset: 0}} />}
      {actualSrc && (
        <img src={actualSrc} alt={alt}
             onLoad={() => setLoaded(true)}
             onError={onError}
             style={{
               position: "absolute", inset: 0,
               width: "100%", height: "100%", objectFit: "cover",
               opacity: loaded ? 1 : 0,
               transition: "opacity 0.28s ease-out",
             }} />
      )}
    </div>
  );
};

const AvatarLibrary = ({ onCreate, onOpen }) => {
  const [items, setItems] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [busyId, setBusyId] = React.useState(null);

  const load = React.useCallback(async () => {
    try {
      const res = await window.gtAPI.fetch("/avatars");
      setItems(res.items || []);
    } catch (e) {
      setError(e.message || "falha ao carregar avatares");
    }
  }, []);

  React.useEffect(() => { load(); }, [load]);

  const handleDelete = async (id) => {
    if (!confirm("Apagar esse avatar? Thumbnails antigas continuam intactas.")) return;
    setBusyId(id);
    try {
      await window.gtAPI.fetch(`/avatars/${id}`, { method: "DELETE" });
      setItems(curr => curr.filter(a => a.id !== id));
    } catch (e) {
      alert(`Falha ao apagar: ${e.message}`);
    } finally {
      setBusyId(null);
    }
  };

  return (
    <div className="fade-in">
      <div className="row-gap" style={{marginBottom: 22}}>
        <div>
          <div className="eyebrow"><span className="dot" />Biblioteca</div>
          <h1 className="display" style={{fontSize: 36, margin: "4px 0 0"}}>Meus Avatares</h1>
          <div className="muted-2" style={{marginTop: 6}}>Use o mesmo rosto em todas as thumbnails. Consistência = reconhecimento = inscritos.</div>
        </div>
        <div className="spacer" />
        <button className="btn btn-primary" onClick={onCreate}><Icon name="plus" size={14} /> Criar novo avatar</button>
      </div>

      {error && (
        <div className="card" style={{padding: 14, borderColor: "var(--brand)", color: "var(--brand)", marginBottom: 16}}>
          {error}
        </div>
      )}

      {items === null && !error && (
        <div className="muted" style={{padding: 40, textAlign: "center"}}>Carregando…</div>
      )}

      {items && (
        <div className="avatar-library-grid">
          {items.map(av => (
            <div key={av.id} className="avatar-lib-card"
                 onClick={() => onOpen && onOpen(av.id)}
                 style={{cursor: onOpen ? "pointer" : "default"}}>
              <div className="lib-avatar" style={{overflow: "hidden", background: "var(--bg-2)"}}>
                {av.preview_url
                  ? <SkeletonImg src={av.preview_url} fullSrc={av.preview_url_full} alt={av.name} />
                  : <FacePlaceholder tone={0} emotion="happy" size={96} />}
              </div>
              <div className="name">{av.name}</div>
              <div className="row-gap" style={{gap: 6}}>
                <button className="btn btn-sm btn-ghost" disabled={busyId === av.id}
                        onClick={(e) => { e.stopPropagation(); handleDelete(av.id); }} title="Apagar">
                  <Icon name="trash" size={12} />
                </button>
              </div>
            </div>
          ))}
          <div className="avatar-lib-card add" onClick={onCreate}>
            <div className="lib-avatar" style={{background: "var(--surface-2)", display: "grid", placeItems: "center"}}>
              <Icon name="plus" size={28} />
            </div>
            <div className="name">Criar avatar</div>
            <div className="meta">descreva e a IA gera</div>
          </div>
        </div>
      )}

      {items && items.length === 0 && !error && (
        <div className="card" style={{padding: 22, marginTop: 28, display: "flex", gap: 18, alignItems: "center"}}>
          <div style={{width: 56, height: 56, borderRadius: 12, background: "var(--brand-soft)", color: "var(--brand)", display: "grid", placeItems: "center"}}>
            <Icon name="zap" size={24} />
          </div>
          <div style={{flex: 1}}>
            <div style={{fontWeight: 600, fontSize: 15}}>Por que avatares são o segredo</div>
            <div className="muted-2" style={{fontSize: 13, marginTop: 2}}>Canais que mantêm o mesmo rosto em todas as capas têm 2.4× mais retenção de inscritos. Crie o seu agora.</div>
          </div>
          <button className="btn btn-primary btn-sm" onClick={onCreate}><Icon name="plus" size={12} /> Criar primeiro</button>
        </div>
      )}
    </div>
  );
};

const MAX_REFS = 9;
const MIN_REFS = 1;
const MAX_BYTES_PER_FILE = 10 * 1024 * 1024;
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp"];

const AvatarCreator = ({ onSave, onCancel }) => {
  const [name, setName] = React.useState("");
  // refs: [{ file, previewUrl }] — previewUrl is a blob: URL from URL.createObjectURL
  const [refs, setRefs] = React.useState([]);
  const [submitting, setSubmitting] = React.useState(false);
  const [submitPhase, setSubmitPhase] = React.useState(null); // "uploading" | "cropping"
  const [error, setError] = React.useState(null);
  const [dragOver, setDragOver] = React.useState(false);
  const fileInputRef = React.useRef(null);

  // Revoke object URLs on unmount to avoid leaks.
  React.useEffect(() => {
    return () => { refs.forEach(r => URL.revokeObjectURL(r.previewUrl)); };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const addFiles = (fileList) => {
    setError(null);
    const incoming = Array.from(fileList);
    const remaining = MAX_REFS - refs.length;
    if (remaining <= 0) {
      setError(`máximo ${MAX_REFS} fotos`);
      return;
    }
    const accepted = [];
    for (const f of incoming) {
      if (accepted.length >= remaining) break;
      if (!ALLOWED_TYPES.includes(f.type)) {
        setError(`formato não suportado: ${f.name}`);
        continue;
      }
      if (f.size > MAX_BYTES_PER_FILE) {
        setError(`arquivo muito grande (max 10 MB): ${f.name}`);
        continue;
      }
      accepted.push({ file: f, previewUrl: URL.createObjectURL(f), id: `${f.name}-${f.size}-${Math.random()}` });
    }
    if (accepted.length > 0) setRefs(curr => [...curr, ...accepted]);
  };

  const removeRef = (id) => {
    setRefs(curr => {
      const target = curr.find(r => r.id === id);
      if (target) URL.revokeObjectURL(target.previewUrl);
      return curr.filter(r => r.id !== id);
    });
  };

  const onDrop = (e) => {
    e.preventDefault();
    setDragOver(false);
    if (e.dataTransfer?.files?.length) addFiles(e.dataTransfer.files);
  };

  const submit = async () => {
    if (!name.trim()) { setError("dá um nome pro avatar"); return; }
    if (refs.length < MIN_REFS) { setError(`mande pelo menos ${MIN_REFS} foto`); return; }
    if (submitting) return;
    setSubmitting(true);
    setSubmitPhase("uploading");
    setError(null);
    try {
      const files = refs.map(r => r.file);

      // 1. Ask the backend for signed upload URLs (tiny JSON request — no bytes).
      const { avatarId, bucket, uploads } = await window.gtAPI.fetch("/avatars/upload-urls", {
        method: "POST",
        body: JSON.stringify({
          files: files.map(f => ({ name: f.name, type: f.type, size: f.size })),
        }),
      });

      // 2. Upload each photo straight to Supabase Storage, bypassing the
      //    serverless function (and its ~4.5 MB request limit) entirely.
      await Promise.all(uploads.map(async (u, i) => {
        const { error } = await window.gtSupabase.storage
          .from(bucket)
          .uploadToSignedUrl(u.path, u.token, files[i], { contentType: files[i].type });
        if (error) throw new Error(error.message || `falha ao enviar ${files[i].name}`);
      }));

      // 3. Finalize: the backend verifies the objects and builds preview variants.
      setSubmitPhase("cropping");
      const res = await window.gtAPI.fetch("/avatars", {
        method: "POST",
        body: JSON.stringify({ name: name.trim(), avatarId, paths: uploads.map(u => u.path) }),
      });
      onSave(res);
    } catch (e) {
      setError(e.message || "falha ao salvar avatar");
      setSubmitting(false);
      setSubmitPhase(null);
    }
  };

  return (
    <div className="fade-in">
      <div className="row-gap" style={{marginBottom: 22}}>
        <button className="btn btn-sm btn-ghost" onClick={onCancel} disabled={submitting}>← Voltar</button>
        <div>
          <div className="eyebrow">Novo avatar</div>
          <h1 className="display" style={{fontSize: 32, margin: 0}}>Criar avatar</h1>
        </div>
      </div>

      <div className="card" style={{padding: 28, maxWidth: 720}}>
        <div className="eyebrow">Como funciona</div>
        <p className="muted-2" style={{fontSize: 14, marginTop: 6, marginBottom: 22}}>
          Mande de 1 a 9 fotos do seu rosto — frente, perfil esquerdo, perfil direito, ângulos diferentes, expressões variadas.
          Quanto mais variedade, melhor a IA preserva sua identidade nas thumbs. <strong>Salvar avatar é grátis</strong>; cada thumb gerada com ele usa 1 crédito.
        </p>

        <div className="col-gap" style={{gap: 16}}>
          <div className="field">
            <label>Nome</label>
            <input
              type="text"
              autoFocus
              maxLength={80}
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="ex: Marcos Pro"
              disabled={submitting}
            />
          </div>
          <div className="field">
            <label>Fotos do rosto <span className="muted" style={{fontWeight: 400}}>({refs.length}/{MAX_REFS})</span></label>

            <div
              onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
              onDragLeave={() => setDragOver(false)}
              onDrop={onDrop}
              onClick={() => !submitting && refs.length < MAX_REFS && fileInputRef.current?.click()}
              style={{
                border: `2px dashed ${dragOver ? "var(--brand)" : "var(--border-strong)"}`,
                borderRadius: 12,
                padding: 22,
                textAlign: "center",
                background: dragOver ? "var(--brand-soft)" : "var(--bg-2)",
                cursor: submitting || refs.length >= MAX_REFS ? "default" : "pointer",
                transition: "all .15s",
                opacity: refs.length >= MAX_REFS ? 0.5 : 1,
              }}
            >
              <Icon name="image" size={26} />
              <div style={{marginTop: 8, fontSize: 14, fontWeight: 500}}>
                {refs.length >= MAX_REFS
                  ? "Limite de fotos atingido"
                  : "Arraste fotos aqui ou clique pra escolher"}
              </div>
              <div className="muted" style={{fontSize: 12, marginTop: 4}}>
                JPG · PNG · WebP · até 10 MB cada · até {MAX_REFS} no total
              </div>
              <input
                ref={fileInputRef}
                type="file"
                accept={ALLOWED_TYPES.join(",")}
                multiple
                style={{display: "none"}}
                onChange={(e) => { addFiles(e.target.files); e.target.value = ""; }}
                disabled={submitting || refs.length >= MAX_REFS}
              />
            </div>

            {refs.length > 0 && (
              <div style={{
                display: "grid",
                gridTemplateColumns: "repeat(auto-fill, minmax(96px, 1fr))",
                gap: 10,
                marginTop: 12,
              }}>
                {refs.map((r, i) => (
                  <div key={r.id} style={{
                    position: "relative",
                    aspectRatio: "1",
                    borderRadius: 10,
                    overflow: "hidden",
                    border: "1px solid var(--border)",
                    background: "var(--bg-2)",
                  }}>
                    <img src={r.previewUrl} alt="" style={{width: "100%", height: "100%", objectFit: "cover"}} />
                    <button
                      type="button"
                      onClick={(e) => { e.stopPropagation(); removeRef(r.id); }}
                      disabled={submitting}
                      title="Remover"
                      style={{
                        position: "absolute", top: 4, right: 4, width: 22, height: 22,
                        borderRadius: "50%", border: 0, background: "rgba(0,0,0,0.7)",
                        color: "#fff", cursor: "pointer", display: "grid", placeItems: "center",
                        padding: 0,
                      }}
                    >
                      <Icon name="close" size={12} />
                    </button>
                    <div style={{
                      position: "absolute", bottom: 4, left: 4,
                      background: "rgba(0,0,0,0.7)", color: "#fff",
                      fontSize: 10, padding: "2px 6px", borderRadius: 4,
                      fontFamily: "var(--font-mono)",
                    }}>{String(i + 1).padStart(2, "0")}</div>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        {error && (
          <div style={{marginTop: 16, padding: 12, borderRadius: 8, background: "var(--brand-soft, rgba(225,29,72,0.12))", color: "var(--brand)", fontSize: 13}}>
            {error}
          </div>
        )}

        {submitting && submitPhase === "cropping" && (
          <div style={{
            marginTop: 16, padding: 14, borderRadius: 10,
            background: "var(--bg-2)", border: "1px solid var(--border)",
            display: "flex", alignItems: "center", gap: 12, fontSize: 13,
          }}>
            <span className="ring" style={{width: 18, height: 18, color: "var(--brand)"}} />
            <div>
              <div style={{fontWeight: 600}}>Recortando o rosto…</div>
              <div className="muted" style={{fontSize: 11, marginTop: 2}}>
                Estamos extraindo o sujeito da primeira foto pra deixar pronto pra compor as thumbs. ~10s.
              </div>
            </div>
          </div>
        )}

        <div className="row-gap" style={{gap: 10, marginTop: 22, justifyContent: "flex-end"}}>
          <button className="btn" onClick={onCancel} disabled={submitting}>Cancelar</button>
          <button className="btn btn-primary btn-lg" onClick={submit}
                  disabled={submitting || !name.trim() || refs.length < MIN_REFS}>
            {submitting
              ? (submitPhase === "cropping"
                  ? <><span className="ring" /> Recortando rosto…</>
                  : <><span className="ring" /> Subindo fotos…</>)
              : <><Icon name="check" size={14} /> Salvar avatar</>}
          </button>
        </div>
      </div>
    </div>
  );
};

const AvatarDetail = ({ avatarId, onBack, onDeleted, onUseInThumb }) => {
  const [avatar, setAvatar] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [confirmDelete, setConfirmDelete] = React.useState(false);

  React.useEffect(() => {
    if (!avatarId) return;
    let active = true;
    (async () => {
      try {
        const res = await window.gtAPI.fetch(`/avatars/${avatarId}`);
        if (active) setAvatar(res);
      } catch (e) {
        if (active) setError(e.message || "falha ao carregar");
      }
    })();
    return () => { active = false; };
  }, [avatarId]);

  const handleDelete = async () => {
    if (busy) return;
    setBusy(true);
    try {
      await window.gtAPI.fetch(`/avatars/${avatarId}`, { method: "DELETE" });
      onDeleted && onDeleted();
    } catch (e) {
      alert(`Falha ao apagar: ${e.message}`);
      setBusy(false);
    }
  };

  if (error) {
    return (
      <div className="fade-in">
        <button className="btn btn-sm btn-ghost" onClick={onBack}>← Voltar</button>
        <div className="card" style={{padding: 20, marginTop: 16, borderColor: "var(--brand)", color: "var(--brand)"}}>
          {error}
        </div>
      </div>
    );
  }
  if (!avatar) {
    return (
      <div className="fade-in" style={{padding: 40, textAlign: "center"}}>
        <span className="ring" style={{width: 28, height: 28, borderWidth: 2, color: "var(--brand)"}} />
        <div className="muted" style={{marginTop: 12}}>Carregando…</div>
      </div>
    );
  }

  const refs = avatar.references || [];

  return (
    <div className="fade-in">
      <div className="row-gap" style={{marginBottom: 22}}>
        <button className="btn btn-sm btn-ghost" onClick={onBack}>← Voltar</button>
        <div>
          <div className="eyebrow">Avatar</div>
          <h1 className="display" style={{fontSize: 32, margin: 0}}>{avatar.name}</h1>
        </div>
        <div className="spacer" />
        {onUseInThumb && (
          <button className="btn btn-primary" onClick={() => onUseInThumb(avatar)}>
            <Icon name="sparkles" size={14} /> Usar em thumbnail
          </button>
        )}
      </div>

      <div className="row-gap" style={{gap: 18, marginBottom: 22, alignItems: "flex-start"}}>
        <div className="card" style={{padding: 18, width: 220, flexShrink: 0, textAlign: "center"}}>
          <div style={{
            width: 160, height: 160, borderRadius: "50%",
            margin: "0 auto", overflow: "hidden", background: "var(--bg-2)",
          }}>
            {avatar.preview_url
              ? <SkeletonImg src={avatar.preview_url} fullSrc={avatar.preview_url_full} />
              : <FacePlaceholder tone={0} emotion="happy" size={160} />}
          </div>
          <div style={{marginTop: 12, fontWeight: 600, fontSize: 15}}>{avatar.name}</div>
          <div className="muted-2" style={{fontSize: 12, marginTop: 4}}>
            {refs.length} foto{refs.length === 1 ? "" : "s"} de referência
          </div>
          <div className="muted" style={{fontSize: 11, marginTop: 4, fontFamily: "var(--font-mono)"}}>
            {new Date(avatar.created_at).toLocaleDateString("pt-BR")}
          </div>
        </div>

        <div className="card" style={{padding: 22, flex: 1}}>
          <div className="eyebrow">Recorte</div>
          <div className="muted-2" style={{fontSize: 12, marginTop: 4}}>
            {avatar.has_subject
              ? "Pronto pra compor o avatar atrás de textos."
              : "Não foi gerado ainda."}
          </div>
        </div>
      </div>

      <div style={{marginBottom: 12}}>
        <div className="eyebrow">Fotos de referência</div>
        <div className="muted-2" style={{fontSize: 12, marginTop: 4}}>
          São essas {refs.length} foto{refs.length === 1 ? "" : "s"} que a IA usa pra preservar o rosto nas thumbnails.
        </div>
      </div>

      {refs.length === 0 ? (
        <div className="card" style={{padding: 30, textAlign: "center"}}>
          <div className="muted">Nenhuma foto de referência.</div>
        </div>
      ) : (
        <div style={{
          display: "grid",
          gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
          gap: 12,
          marginBottom: 24,
        }}>
          {refs.map((url, i) => (
            <div key={i} style={{
              position: "relative",
              aspectRatio: "1",
              borderRadius: 12,
              overflow: "hidden",
              border: "1px solid var(--border)",
              background: "var(--bg-2)",
            }}>
              <SkeletonImg src={url} />
              <div style={{
                position: "absolute", bottom: 6, left: 6,
                background: "rgba(0,0,0,0.7)", color: "#fff",
                fontSize: 10, padding: "2px 6px", borderRadius: 4,
                fontFamily: "var(--font-mono)",
              }}>
                {String(i + 1).padStart(2, "0")}
              </div>
            </div>
          ))}
        </div>
      )}

      {/* Danger zone */}
      <div className="card" style={{padding: 18, borderColor: "var(--border)", marginTop: 16}}>
        <div className="row-gap" style={{alignItems: "center"}}>
          <div>
            <div style={{fontWeight: 600, fontSize: 14}}>Apagar avatar</div>
            <div className="muted-2" style={{fontSize: 12, marginTop: 2}}>
              Thumbnails antigas geradas com esse avatar continuam intactas.
            </div>
          </div>
          <div className="spacer" />
          {confirmDelete ? (
            <div className="row-gap" style={{gap: 8}}>
              <button className="btn btn-sm" onClick={() => setConfirmDelete(false)} disabled={busy}>
                Cancelar
              </button>
              <button className="btn btn-sm" onClick={handleDelete} disabled={busy}
                      style={{background: "var(--brand)", borderColor: "var(--brand)", color: "#fff"}}>
                <Icon name="trash" size={12} /> {busy ? "Apagando…" : "Confirmar"}
              </button>
            </div>
          ) : (
            <button className="btn btn-sm btn-ghost" onClick={() => setConfirmDelete(true)}
                    style={{color: "var(--text-3)"}}>
              <Icon name="trash" size={12} /> Apagar
            </button>
          )}
        </div>
      </div>
    </div>
  );
};

window.AvatarLibrary = AvatarLibrary;
window.AvatarCreator = AvatarCreator;
window.AvatarDetail = AvatarDetail;
