"use client";

import { useEffect, useRef, useState } from "react";
import { Check, FileText, Link2, ListChecks, Loader2, Paperclip, Play, Plus, Radio, Trash2, Upload, X } from "lucide-react";

import { EscolherDaBiblioteca, type ArquivoDaBiblioteca } from "@/components/digitais/biblioteca";
import ImageUpload from "@/components/image-upload";
import { pedirDigitais } from "@/components/digitais/comum";
import { SeloDoArquivo } from "@/components/digitais/selo-do-arquivo";
import { AreaDeTexto, ChaveDeAjuste, EscolhaDeAjuste } from "@/components/ui/folha-de-ajustes";
import { Input } from "@/components/ui/input";
import { duracaoLegivel, lerLinkDeVideo, relogio, textoDosCapitulos, type TipoDeAula } from "@/lib/digitais/curso";

import { dataEHoraDoCampo, diaDoCampo, inicioDoDia, isoDaDataEHora, type Aula, type Modulo } from "./tipos";

/*
 * O lado direito do conteúdo (prévia 8): a aula escolhida. Cada mudança é
 * salva sozinha (os textos, um instante depois de parar de digitar; as
 * escolhas, na hora), então trocar de aula nunca perde nada.
 */

export type EnvioDaAula = { enviados: number; total: number; fase: "enviando" | "conferindo"; nome: string };

const TIPOS: Array<{ valor: TipoDeAula; rotulo: string; Icone: typeof Play }> = [
  { valor: "video", rotulo: "Vídeo", Icone: Play },
  { valor: "texto", rotulo: "Texto", Icone: FileText },
  { valor: "materiais", rotulo: "Só materiais", Icone: Paperclip },
  { valor: "ao_vivo", rotulo: "Ao vivo", Icone: Radio },
];

type Rascunho = {
  titulo: string;
  descricao: string;
  texto: string;
  link: string;
  duracaoMin: string;
  aoVivoEm: string;
  aoVivoLink: string;
  capitulos: string;
};

function rascunhoDe(aula: Aula): Rascunho {
  return {
    titulo: aula.titulo,
    descricao: aula.descricao ?? "",
    texto: aula.texto ?? "",
    link: aula.link ? aula.link.url : "",
    duracaoMin: aula.duracaoS ? String(Math.round(aula.duracaoS / 60)) : "",
    aoVivoEm: dataEHoraDoCampo(aula.aoVivoEm),
    aoVivoLink: aula.aoVivoLink ?? "",
    capitulos: textoDosCapitulos(aula.capitulos ?? []),
  };
}

export function EditorDaAula({
  cursoId,
  aula,
  modulo,
  posicao,
  podeEditar,
  envio,
  aoEnviarVideo,
  aoMudar,
  aoAvisar,
  aoApagar,
}: {
  cursoId: string;
  aula: Aula;
  modulo: { titulo: string; numero: number; total: number };
  posicao: number;
  podeEditar: boolean;
  envio: EnvioDaAula | null;
  aoEnviarVideo: (arquivo: File) => void;
  aoMudar: () => void;
  aoAvisar: (tom: "bom" | "ruim" | "atencao", texto: string) => void;
  aoApagar: () => void;
}) {
  const [rascunho, setRascunho] = useState<Rascunho>(() => rascunhoDe(aula));
  const [estado, setEstado] = useState<"salvo" | "salvando" | "erro" | null>(null);
  const [escolhendo, setEscolhendo] = useState<"video" | "material" | null>(null);
  const [novoLink, setNovoLink] = useState<{ titulo: string; url: string } | null>(null);
  const [enviandoMaterial, setEnviandoMaterial] = useState<string | null>(null);
  const pendenteRef = useRef<Record<string, unknown> | null>(null);
  const relogioRef = useRef<number | undefined>(undefined);
  const entradaDoVideo = useRef<HTMLInputElement | null>(null);
  const entradaDoMaterial = useRef<HTMLInputElement | null>(null);
  const entradaDaLegenda = useRef<HTMLInputElement | null>(null);
  const [enviandoLegenda, setEnviandoLegenda] = useState<string | null>(null);

  const enviarMudancas = (mudancas: Record<string, unknown>, recarregar = true, manterVivo = false) => {
    setEstado("salvando");
    return fetch(`/api/digitais/cursos/${cursoId}/aulas/${aula.id}`, {
      method: "PUT",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(mudancas),
      keepalive: manterVivo,
    })
      .then(async (resposta) => {
        const corpo = (await resposta.json().catch(() => null)) as { error?: string } | null;
        if (!resposta.ok) throw new Error(corpo?.error || "Não foi possível salvar o vídeo.");
        setEstado("salvo");
        if (recarregar) aoMudar();
      })
      .catch((e: unknown) => {
        setEstado("erro");
        aoAvisar("ruim", e instanceof Error ? e.message : "Não foi possível salvar o vídeo.");
      });
  };

  /* Textos: salva um instante depois de parar de digitar. */
  const agendar = (mudancas: Record<string, unknown>) => {
    pendenteRef.current = { ...(pendenteRef.current ?? {}), ...mudancas };
    if (relogioRef.current) window.clearTimeout(relogioRef.current);
    relogioRef.current = window.setTimeout(() => {
      const enviar = pendenteRef.current;
      pendenteRef.current = null;
      if (enviar) void enviarMudancas(enviar);
    }, 800);
  };

  /* Saiu da aula com algo por salvar: salva na saída. */
  useEffect(
    () => () => {
      if (relogioRef.current) window.clearTimeout(relogioRef.current);
      const enviar = pendenteRef.current;
      if (enviar) {
        pendenteRef.current = null;
        void fetch(`/api/digitais/cursos/${cursoId}/aulas/${aula.id}`, {
          method: "PUT",
          headers: { "content-type": "application/json" },
          body: JSON.stringify(enviar),
          keepalive: true,
        });
      }
    },
    // A aula deste editor não muda: o pai troca o componente inteiro (key).
    // eslint-disable-next-line react-hooks/exhaustive-deps
    []
  );

  const mudarTexto = (campo: keyof Rascunho, valor: string, paraOBanco: () => Record<string, unknown> | null) => {
    setRascunho((atual) => ({ ...atual, [campo]: valor }));
    const mudancas = paraOBanco();
    if (mudancas) agendar(mudancas);
  };

  const escolher = (mudancas: Record<string, unknown>) => {
    if (!podeEditar) return;
    void enviarMudancas(mudancas);
  };

  const escolherVideo = (arquivo: ArquivoDaBiblioteca) => {
    setEscolhendo(null);
    void enviarMudancas({ videoArquivoId: arquivo.id, ...(arquivo.duracao ? { duracaoS: Math.round(arquivo.duracao) } : {}) });
  };

  const colocarMaterial = async (corpo: Record<string, unknown>) => {
    try {
      await pedirDigitais(`/api/digitais/cursos/${cursoId}/aulas/${aula.id}/materiais`, { method: "POST", body: JSON.stringify(corpo) });
      aoMudar();
      return true;
    } catch (e) {
      aoAvisar("ruim", e instanceof Error ? e.message : "Não foi possível pôr o material.");
      return false;
    }
  };

  const enviarMaterial = async (arquivo: File) => {
    setEnviandoMaterial(arquivo.name);
    try {
      const { enviarArquivo } = await import("@/components/digitais/enviar-arquivo");
      const id = await enviarArquivo(arquivo, () => undefined, new AbortController().signal);
      await colocarMaterial({ arquivoId: id, titulo: arquivo.name.replace(/\.[^.]+$/, "") });
    } catch (e) {
      aoAvisar("ruim", e instanceof Error ? e.message : "O envio do material não deu certo.");
    } finally {
      setEnviandoMaterial(null);
    }
  };

  /* A legenda sobe para a biblioteca como qualquer arquivo e entra na aula. */
  const enviarLegenda = async (arquivo: File) => {
    if (!/\.(vtt|srt)$/i.test(arquivo.name)) {
      aoAvisar("ruim", "A legenda precisa ser um arquivo .vtt ou .srt.");
      return;
    }
    setEnviandoLegenda(arquivo.name);
    try {
      const { enviarArquivo } = await import("@/components/digitais/enviar-arquivo");
      const id = await enviarArquivo(arquivo, () => undefined, new AbortController().signal);
      await enviarMudancas({ legendaArquivoId: id });
    } catch (e) {
      aoAvisar("ruim", e instanceof Error ? e.message : "O envio da legenda não deu certo.");
    } finally {
      setEnviandoLegenda(null);
    }
  };

  const tirarMaterial = async (materialId: string) => {
    try {
      await pedirDigitais(`/api/digitais/cursos/${cursoId}/aulas/${aula.id}/materiais?material=${materialId}`, { method: "DELETE" });
      aoMudar();
    } catch (e) {
      aoAvisar("ruim", e instanceof Error ? e.message : "Não foi possível tirar o material.");
    }
  };

  const liberacao = aula.liberacao;
  const percentual = envio ? Math.round((envio.enviados / Math.max(1, envio.total)) * 100) : 0;
  const linkLido = rascunho.link ? lerLinkDeVideo(rascunho.link) : null;

  return (
    <section className="rounded-2xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-elevated)] p-5 shadow-[var(--ks-shadow-subtle)] sm:p-6">
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0 flex-1">
          <Input
            aria-label="Nome do vídeo"
            value={rascunho.titulo}
            maxLength={160}
            disabled={!podeEditar}
            className="text-[17px] font-extrabold"
            onChange={(e) =>
              mudarTexto("titulo", e.target.value, () => (e.target.value.trim() ? { titulo: e.target.value } : null))
            }
          />
          <p className="mt-1.5 text-xs text-[var(--ks-color-text-muted)]">
            Módulo {modulo.numero} · Vídeo {posicao} de {modulo.total}
          </p>
        </div>
        <span className="flex shrink-0 items-center gap-1 pt-2 text-xs text-[var(--ks-color-text-muted)]">
          {estado === "salvando" ? (
            <>
              <Loader2 size={13} className="animate-spin" />
              Salvando…
            </>
          ) : estado === "salvo" ? (
            <>
              <Check size={13} className="text-[var(--ks-emerald)]" />
              Salvo
            </>
          ) : null}
        </span>
      </div>

      <p className="mt-5 text-xs font-bold text-[var(--ks-color-text-secondary)]">Tipo de conteúdo</p>
      <div role="radiogroup" aria-label="Tipo de conteúdo" className="mt-1.5 inline-flex flex-wrap gap-1 rounded-xl bg-[var(--ks-color-bg-inset)] p-1">
        {TIPOS.map(({ valor, rotulo, Icone }) => {
          const ativo = aula.tipo === valor;
          return (
            <button
              key={valor}
              type="button"
              role="radio"
              aria-checked={ativo}
              disabled={!podeEditar}
              onClick={() => !ativo && escolher({ tipo: valor })}
              className={`inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-[13px] font-semibold transition-colors ${
                ativo ? "bg-white text-[var(--ks-brand-600)] shadow-sm" : "text-[var(--ks-color-text-secondary)] hover:text-[var(--ks-color-text-primary)]"
              }`}
            >
              <Icone size={14} />
              {rotulo}
            </button>
          );
        })}
      </div>

      {aula.tipo === "video" ? (
        <div className="mt-5">
          <p className="text-xs font-bold text-[var(--ks-color-text-secondary)]">Vídeo</p>
          {envio ? (
            <div className="mt-1.5 rounded-2xl border border-[var(--ks-color-border-subtle)] p-3">
              <p className="truncate text-sm font-bold text-[var(--ks-color-text-primary)]">{envio.nome}</p>
              <p className="mt-0.5 text-xs text-[var(--ks-color-text-muted)]">
                {envio.fase === "conferindo" ? "Conferindo o vídeo…" : `Enviando… ${percentual}%`} Pode continuar montando o conteúdo.
              </p>
              <div className="mt-2 h-1.5 overflow-hidden rounded-full bg-[var(--ks-color-bg-inset)]">
                <div className="h-full rounded-full bg-[var(--ks-brand-600)] transition-all" style={{ width: `${envio.fase === "conferindo" ? 100 : percentual}%` }} />
              </div>
            </div>
          ) : aula.video ? (
            <div className="mt-1.5 flex flex-wrap items-center gap-3 rounded-2xl border border-[var(--ks-color-border-subtle)] p-3">
              <span className="flex h-14 w-24 shrink-0 items-center justify-center rounded-xl text-white" style={{ background: "linear-gradient(135deg, #3b1c7a 0%, #e2626b 100%)" }}>
                <Play size={20} />
              </span>
              <span className="min-w-0 flex-1">
                <span className="block truncate text-sm font-bold text-[var(--ks-color-text-primary)]">{aula.video.nome}</span>
                <span className="block text-xs text-[var(--ks-color-text-muted)]">
                  {aula.video.tamanhoLegivel}
                  {aula.duracaoS ? ` · ${relogio(aula.duracaoS)}` : ""} ·{" "}
                  {aula.video.status === "pronto" ? "guardado na sua área (sem custo por visualização)" : "ainda enviando"}
                </span>
              </span>
              {podeEditar ? (
                <span className="flex gap-1.5">
                  <button type="button" className="ks-button ks-button-soft ks-button-sm" onClick={() => entradaDoVideo.current?.click()}>
                    Trocar
                  </button>
                  <button type="button" className="ks-button ks-button-ghost ks-button-sm" onClick={() => escolher({ videoArquivoId: null, duracaoS: null })} aria-label="Tirar o vídeo">
                    <X size={14} />
                  </button>
                </span>
              ) : null}
            </div>
          ) : podeEditar && !aula.link ? (
            <div className="mt-1.5 flex flex-wrap gap-2 rounded-2xl border border-dashed border-[var(--ks-color-border-strong)] p-4">
              <button type="button" className="ks-button ks-button-primary ks-button-sm" onClick={() => entradaDoVideo.current?.click()}>
                <Upload size={14} />
                Enviar vídeo
              </button>
              <button type="button" className="ks-button ks-button-soft ks-button-sm" onClick={() => setEscolhendo("video")}>
                Da biblioteca
              </button>
              <p className="w-full text-xs text-[var(--ks-color-text-muted)]">MP4 (H.264) em 720p ou 1080p é o que toca em qualquer aparelho. Até 2 GB por vídeo.</p>
            </div>
          ) : null}
          <input
            ref={entradaDoVideo}
            type="file"
            accept="video/mp4,video/webm,video/quicktime,.mp4,.m4v,.webm,.mov"
            className="hidden"
            onChange={(e) => {
              const arquivo = e.target.files?.[0];
              e.target.value = "";
              if (arquivo) aoEnviarVideo(arquivo);
            }}
          />

          {!aula.video && !envio ? (
            <div className="mt-3">
              <label className="text-xs text-[var(--ks-color-text-muted)]" htmlFor={`link-${aula.id}`}>
                ou cole um link do <b>YouTube</b>, <b>Vimeo</b> ou <b>Panda Video</b> se o vídeo já está lá
              </label>
              <div className="mt-1.5 grid gap-2 sm:grid-cols-[minmax(0,1fr)_140px]">
                <Input
                  id={`link-${aula.id}`}
                  value={rascunho.link}
                  disabled={!podeEditar}
                  placeholder="https://youtu.be/..."
                  onChange={(e) =>
                    mudarTexto("link", e.target.value, () => {
                      const valor = e.target.value.trim();
                      if (!valor) return { videoLink: null };
                      return lerLinkDeVideo(valor) ? { videoLink: valor } : null;
                    })
                  }
                />
                <Input
                  aria-label="Duração em minutos"
                  value={rascunho.duracaoMin}
                  disabled={!podeEditar}
                  inputMode="numeric"
                  placeholder="Duração (min)"
                  onChange={(e) => {
                    const valor = e.target.value.replace(/\D/g, "").slice(0, 4);
                    mudarTexto("duracaoMin", valor, () => ({ duracaoS: valor ? Number(valor) * 60 : null }));
                  }}
                />
              </div>
              {rascunho.link && !linkLido ? (
                <p className="mt-1 text-xs text-[#c2263f]">Esse link não é de um vídeo do YouTube, Vimeo ou Panda Video.</p>
              ) : linkLido ? (
                <p className="mt-1 text-xs text-[var(--ks-emerald)]">
                  Vídeo do {linkLido.provedor === "youtube" ? "YouTube" : linkLido.provedor === "vimeo" ? "Vimeo" : "Panda Video"} reconhecido. O cliente assiste dentro da sua área.
                </p>
              ) : null}
            </div>
          ) : null}

          {aula.video ? (
            <div className="mt-5 grid gap-5">
              <div>
                <p className="text-xs font-bold text-[var(--ks-color-text-secondary)]">Legenda (opcional)</p>
                {enviandoLegenda ? (
                  <p className="mt-1.5 flex items-center gap-2 text-sm text-[var(--ks-color-text-muted)]">
                    <Loader2 size={14} className="animate-spin" />
                    Enviando {enviandoLegenda}…
                  </p>
                ) : aula.legenda ? (
                  <div className="mt-1.5 flex items-center gap-3 rounded-2xl border border-[var(--ks-color-border-subtle)] p-3">
                    <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--ks-color-bg-inset)] text-[11px] font-extrabold text-[var(--ks-color-text-secondary)]">CC</span>
                    <span className="min-w-0 flex-1">
                      <span className="block truncate text-sm font-semibold text-[var(--ks-color-text-primary)]">{aula.legenda.nome}</span>
                      <span className="block text-xs text-[var(--ks-color-text-muted)]">{aula.legenda.status === "pronto" ? "O cliente liga pelo botão CC do player." : "Conferindo o arquivo…"}</span>
                    </span>
                    {podeEditar ? (
                      <span className="flex gap-1.5">
                        <button type="button" className="ks-button ks-button-soft ks-button-sm" onClick={() => entradaDaLegenda.current?.click()}>
                          Trocar
                        </button>
                        <button type="button" className="ks-button ks-button-ghost ks-button-sm" aria-label="Tirar a legenda" onClick={() => escolher({ legendaArquivoId: null })}>
                          <X size={14} />
                        </button>
                      </span>
                    ) : null}
                  </div>
                ) : podeEditar ? (
                  <button type="button" className="ks-button ks-button-soft ks-button-sm mt-1.5" onClick={() => entradaDaLegenda.current?.click()}>
                    <Upload size={14} />
                    Enviar legenda (.vtt ou .srt)
                  </button>
                ) : (
                  <p className="mt-1.5 text-sm text-[var(--ks-color-text-muted)]">Sem legenda.</p>
                )}
                <input
                  ref={entradaDaLegenda}
                  type="file"
                  accept=".vtt,.srt,text/vtt"
                  className="hidden"
                  onChange={(e) => {
                    const arquivo = e.target.files?.[0];
                    e.target.value = "";
                    if (arquivo) void enviarLegenda(arquivo);
                  }}
                />
              </div>
              <div>
                <label className="text-xs font-bold text-[var(--ks-color-text-secondary)]" htmlFor={`capitulos-${aula.id}`}>
                  Capítulos (opcional)
                </label>
                <AreaDeTexto
                  id={`capitulos-${aula.id}`}
                  className="mt-1.5 font-mono text-[13px]"
                  rows={4}
                  maxLength={6000}
                  value={rascunho.capitulos}
                  disabled={!podeEditar}
                  placeholder={"0:00 Abertura\n12:40 Prensa 5 em 1"}
                  onChange={(e) => mudarTexto("capitulos", e.target.value, () => ({ capitulos: e.target.value }))}
                />
                <p className="mt-1 text-xs text-[var(--ks-color-text-muted)]">Um por linha: o tempo e o nome. Aparecem na linha do tempo do vídeo.</p>
              </div>
            </div>
          ) : null}
        </div>
      ) : null}

      {aula.tipo === "texto" ? (
        <div className="mt-5">
          <label className="text-xs font-bold text-[var(--ks-color-text-secondary)]" htmlFor={`texto-${aula.id}`}>
            Texto do conteúdo
          </label>
          <AreaDeTexto
            id={`texto-${aula.id}`}
            className="mt-1.5"
            rows={10}
            maxLength={20000}
            disabled={!podeEditar}
            value={rascunho.texto}
            placeholder="Escreva o conteúdo. Deixe uma linha em branco entre os parágrafos."
            onChange={(e) => mudarTexto("texto", e.target.value, () => ({ texto: e.target.value }))}
          />
        </div>
      ) : null}

      {aula.tipo === "ao_vivo" ? (
        <div className="mt-5 grid gap-3 sm:grid-cols-2">
          <div>
            <label className="text-xs font-bold text-[var(--ks-color-text-secondary)]" htmlFor={`aovivo-${aula.id}`}>
              Dia e hora (Brasília)
            </label>
            <input
              id={`aovivo-${aula.id}`}
              type="datetime-local"
              className="ks-field mt-1.5 w-full"
              value={rascunho.aoVivoEm}
              disabled={!podeEditar}
              onChange={(e) => mudarTexto("aoVivoEm", e.target.value, () => ({ aoVivoEm: isoDaDataEHora(e.target.value) }))}
            />
          </div>
          <div>
            <label className="text-xs font-bold text-[var(--ks-color-text-secondary)]" htmlFor={`aovivo-link-${aula.id}`}>
              Link da transmissão
            </label>
            <Input
              id={`aovivo-link-${aula.id}`}
              className="mt-1.5"
              value={rascunho.aoVivoLink}
              disabled={!podeEditar}
              placeholder="https://meet.google.com/..."
              onChange={(e) =>
                mudarTexto("aoVivoLink", e.target.value, () => {
                  const valor = e.target.value.trim();
                  return !valor || /^https:\/\/\S+$/i.test(valor) ? { aoVivoLink: valor || null } : null;
                })
              }
            />
          </div>
          <p className="text-xs text-[var(--ks-color-text-muted)] sm:col-span-2">
            O link aparece para o cliente 15 minutos antes. Depois, troque o tipo para vídeo com a gravação.
          </p>
        </div>
      ) : null}

      <div className="mt-5">
        <label className="text-xs font-bold text-[var(--ks-color-text-secondary)]" htmlFor={`descricao-${aula.id}`}>
          Descrição do vídeo
        </label>
        <AreaDeTexto
          id={`descricao-${aula.id}`}
          className="mt-1.5"
          rows={3}
          maxLength={4000}
          disabled={!podeEditar}
          value={rascunho.descricao}
          placeholder="Do que o vídeo trata, em poucas linhas."
          onChange={(e) => mudarTexto("descricao", e.target.value, () => ({ descricao: e.target.value }))}
        />
      </div>

      <div className="mt-5">
        <p className="text-xs font-bold text-[var(--ks-color-text-secondary)]">Materiais para baixar</p>
        {aula.materiais.length ? (
          <ul className="mt-1.5">
            {aula.materiais.map((material) => (
              <li key={material.id} className="flex items-center gap-3 border-b border-[var(--ks-n-100)] py-2.5 last:border-b-0">
                {material.arquivo ? (
                  <SeloDoArquivo categoria={material.arquivo.categoria} texto={material.arquivo.extensao.toUpperCase().slice(0, 4)} />
                ) : (
                  <SeloDoArquivo categoria="outro" texto="LINK" />
                )}
                <span className="min-w-0 flex-1">
                  <span className="block truncate text-sm font-semibold text-[var(--ks-color-text-primary)]">{material.titulo}</span>
                  <span className="block truncate text-xs text-[var(--ks-color-text-muted)]">
                    {material.arquivo ? `${material.arquivo.tamanhoLegivel}${material.arquivo.status !== "pronto" ? " · ainda enviando" : ""}` : material.url}
                  </span>
                </span>
                {podeEditar ? (
                  <button type="button" aria-label={`Tirar ${material.titulo}`} onClick={() => void tirarMaterial(material.id)} className="rounded-lg p-1.5 text-[var(--ks-color-text-soft)] hover:bg-[var(--ks-color-bg-hover)]">
                    <Trash2 size={15} />
                  </button>
                ) : null}
              </li>
            ))}
          </ul>
        ) : (
          <p className="mt-1 text-xs text-[var(--ks-color-text-muted)]">PDF, planilha, moldes: o cliente baixa embaixo do vídeo.</p>
        )}
        {enviandoMaterial ? (
          <p className="mt-2 flex items-center gap-1.5 text-xs text-[var(--ks-color-text-muted)]">
            <Loader2 size={13} className="animate-spin" />
            Enviando {enviandoMaterial}…
          </p>
        ) : null}
        {novoLink ? (
          <div className="mt-2 grid gap-2 rounded-xl bg-[var(--ks-color-bg-inset)] p-3 sm:grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)_auto]">
            <Input placeholder="Nome" value={novoLink.titulo} maxLength={160} onChange={(e) => setNovoLink({ ...novoLink, titulo: e.target.value })} />
            <Input placeholder="https://..." value={novoLink.url} onChange={(e) => setNovoLink({ ...novoLink, url: e.target.value })} />
            <span className="flex gap-1.5">
              <button
                type="button"
                className="ks-button ks-button-primary ks-button-sm"
                onClick={async () => {
                  if (await colocarMaterial({ url: novoLink.url, titulo: novoLink.titulo })) setNovoLink(null);
                }}
              >
                Pôr
              </button>
              <button type="button" className="ks-button ks-button-ghost ks-button-sm" onClick={() => setNovoLink(null)} aria-label="Cancelar">
                <X size={14} />
              </button>
            </span>
          </div>
        ) : podeEditar ? (
          <div className="mt-2 flex flex-wrap gap-1.5">
            <button type="button" className="ks-button ks-button-soft ks-button-sm" onClick={() => entradaDoMaterial.current?.click()} disabled={Boolean(enviandoMaterial)}>
              <Plus size={14} />
              Enviar material
            </button>
            <button type="button" className="ks-button ks-button-soft ks-button-sm" onClick={() => setEscolhendo("material")}>
              Da biblioteca
            </button>
            <button type="button" className="ks-button ks-button-soft ks-button-sm" onClick={() => setNovoLink({ titulo: "", url: "" })}>
              <Link2 size={14} />
              Link
            </button>
          </div>
        ) : null}
        <input
          ref={entradaDoMaterial}
          type="file"
          className="hidden"
          onChange={(e) => {
            const arquivo = e.target.files?.[0];
            e.target.value = "";
            if (arquivo) void enviarMaterial(arquivo);
          }}
        />
      </div>

      <div className="mt-5">
        <p className="text-xs font-bold text-[var(--ks-color-text-secondary)]">Quando este vídeo abre</p>
        <div className="mt-1.5">
          <EscolhaDeAjuste
            rotulo="Quando este vídeo abre"
            valor={liberacao}
            aoEscolher={(valor) => {
              if (!podeEditar || valor === liberacao) return;
              if (valor === "modulo") escolher({ liberacao: "modulo" });
              else if (valor === "dias") escolher({ liberacao: "dias", liberarDias: aula.liberarDias ?? 3 });
              else {
                const amanha = new Date(Date.now() + 86400000);
                escolher({ liberacao: "data", liberarEm: aula.liberarEm ?? inicioDoDia(diaDoCampo(amanha.toISOString())) });
              }
            }}
            opcoes={[
              { valor: "modulo", titulo: "Junto com o módulo", texto: "Abre assim que o módulo abrir para o cliente." },
              { valor: "dias", titulo: "Alguns dias depois da compra", texto: "Ex.: 3 dias depois, para o cliente ir no ritmo." },
              { valor: "data", titulo: "Numa data", texto: "Ex.: grupo que começa junto." },
            ]}
          />
        </div>
        {liberacao === "dias" ? (
          <div className="mt-2 flex items-center gap-2 text-sm text-[var(--ks-color-text-primary)]">
            Abre
            <input
              className="ks-field w-20"
              inputMode="numeric"
              defaultValue={aula.liberarDias ?? 3}
              disabled={!podeEditar}
              onBlur={(e) => {
                const dias = Math.round(Number(e.target.value));
                if (dias >= 1 && dias <= 3650 && dias !== aula.liberarDias) escolher({ liberacao: "dias", liberarDias: dias });
              }}
            />
            dias depois da compra
          </div>
        ) : null}
        {liberacao === "data" ? (
          <div className="mt-2 flex items-center gap-2 text-sm text-[var(--ks-color-text-primary)]">
            Abre em
            <input
              type="date"
              className="ks-field"
              defaultValue={diaDoCampo(aula.liberarEm)}
              disabled={!podeEditar}
              onChange={(e) => {
                const iso = inicioDoDia(e.target.value);
                if (iso) escolher({ liberacao: "data", liberarEm: iso });
              }}
            />
          </div>
        ) : null}
      </div>

      <div className="mt-4">
        <ChaveDeAjuste
          titulo="Prévia grátis na página de venda"
          texto="Quem ainda não comprou pode assistir este vídeo."
          ligado={aula.previaGratis}
          desabilitado={!podeEditar}
          aoMudar={(ligado) => escolher({ previaGratis: ligado })}
        />
        <ChaveDeAjuste
          titulo="Comentários neste vídeo"
          texto="Os clientes tiram dúvidas embaixo do vídeo."
          ligado={aula.comentarios}
          desabilitado={!podeEditar}
          aoMudar={(ligado) => escolher({ comentarios: ligado })}
        />
        <ChaveDeAjuste
          titulo="Aparece para os clientes"
          texto="Desligado, o vídeo fica como rascunho: só você vê."
          ligado={aula.publicada}
          desabilitado={!podeEditar}
          aoMudar={(ligado) => escolher({ publicada: ligado })}
        />
      </div>

      {podeEditar ? (
        <div className="mt-4 flex justify-end">
          <button type="button" className="ks-button ks-button-ghost ks-button-sm text-[#c2263f]" onClick={aoApagar}>
            <Trash2 size={14} />
            Apagar vídeo
          </button>
        </div>
      ) : null}

      {aula.tipo === "materiais" && !aula.materiais.length ? (
        <p className="mt-2 flex items-center gap-1.5 text-xs text-[#a16207]">
          <ListChecks size={13} />
          Ponha pelo menos um material: é ele que o cliente recebe aqui.
        </p>
      ) : null}

      {aula.tipo === "video" && aula.duracaoS ? (
        <p className="mt-2 text-xs text-[var(--ks-color-text-muted)]">Duração: {duracaoLegivel(aula.duracaoS)}</p>
      ) : null}

      {escolhendo ? (
        <EscolherDaBiblioteca
          titulo={escolhendo === "video" ? "Escolher um vídeo da biblioteca" : "Escolher um material da biblioteca"}
          so={escolhendo === "video" ? "video" : undefined}
          aoFechar={() => setEscolhendo(null)}
          aoEscolher={(arquivo) => {
            if (escolhendo === "video") escolherVideo(arquivo);
            else {
              setEscolhendo(null);
              void colocarMaterial({ arquivoId: arquivo.id, titulo: arquivo.nome.replace(/\.[^.]+$/, "") });
            }
          }}
        />
      ) : null}
    </section>
  );
}

/* O lado direito quando o escolhido é um módulo. */
export function EditorDoModulo({
  cursoId,
  modulo,
  numero,
  podeEditar,
  aoMudar,
  aoAvisar,
  aoApagar,
}: {
  cursoId: string;
  modulo: Modulo;
  numero: number;
  podeEditar: boolean;
  aoMudar: () => void;
  aoAvisar: (tom: "bom" | "ruim" | "atencao", texto: string) => void;
  aoApagar: () => void;
}) {
  const [titulo, setTitulo] = useState(modulo.titulo);
  const [descricao, setDescricao] = useState(modulo.descricao ?? "");
  const [estado, setEstado] = useState<"salvo" | "salvando" | null>(null);
  const relogioRef = useRef<number | undefined>(undefined);
  const pendenteRef = useRef<Record<string, unknown> | null>(null);

  const enviar = (mudancas: Record<string, unknown>, manterVivo = false) => {
    setEstado("salvando");
    return fetch(`/api/digitais/cursos/${cursoId}/modulos/${modulo.id}`, {
      method: "PUT",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(mudancas),
      keepalive: manterVivo,
    })
      .then(async (resposta) => {
        const corpo = (await resposta.json().catch(() => null)) as { error?: string } | null;
        if (!resposta.ok) throw new Error(corpo?.error || "Não foi possível salvar o módulo.");
        setEstado("salvo");
        aoMudar();
      })
      .catch((e: unknown) => {
        setEstado(null);
        aoAvisar("ruim", e instanceof Error ? e.message : "Não foi possível salvar o módulo.");
      });
  };

  const agendar = (mudancas: Record<string, unknown>) => {
    pendenteRef.current = { ...(pendenteRef.current ?? {}), ...mudancas };
    if (relogioRef.current) window.clearTimeout(relogioRef.current);
    relogioRef.current = window.setTimeout(() => {
      const pendente = pendenteRef.current;
      pendenteRef.current = null;
      if (pendente) void enviar(pendente);
    }, 800);
  };

  useEffect(
    () => () => {
      if (relogioRef.current) window.clearTimeout(relogioRef.current);
      const pendente = pendenteRef.current;
      if (pendente) {
        pendenteRef.current = null;
        void fetch(`/api/digitais/cursos/${cursoId}/modulos/${modulo.id}`, {
          method: "PUT",
          headers: { "content-type": "application/json" },
          body: JSON.stringify(pendente),
          keepalive: true,
        });
      }
    },
    // O pai troca o componente inteiro quando muda de módulo (key).
    // eslint-disable-next-line react-hooks/exhaustive-deps
    []
  );

  return (
    <section className="rounded-2xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-elevated)] p-5 shadow-[var(--ks-shadow-subtle)] sm:p-6">
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0 flex-1">
          <p className="text-xs font-bold uppercase tracking-[0.12em] text-[var(--ks-color-text-soft)]">Módulo {numero}</p>
          <Input
            aria-label="Nome do módulo"
            className="mt-1.5 text-[17px] font-extrabold"
            value={titulo}
            maxLength={160}
            disabled={!podeEditar}
            onChange={(e) => {
              setTitulo(e.target.value);
              if (e.target.value.trim()) agendar({ titulo: e.target.value });
            }}
          />
        </div>
        <span className="flex shrink-0 items-center gap-1 pt-6 text-xs text-[var(--ks-color-text-muted)]">
          {estado === "salvando" ? <Loader2 size={13} className="animate-spin" /> : estado === "salvo" ? <Check size={13} className="text-[var(--ks-emerald)]" /> : null}
          {estado === "salvando" ? "Salvando…" : estado === "salvo" ? "Salvo" : ""}
        </span>
      </div>
      <label className="mt-4 block text-xs font-bold text-[var(--ks-color-text-secondary)]" htmlFor={`modulo-${modulo.id}`}>
        Uma linha sobre o módulo
      </label>
      <AreaDeTexto
        id={`modulo-${modulo.id}`}
        className="mt-1.5"
        rows={2}
        maxLength={1000}
        disabled={!podeEditar}
        value={descricao}
        placeholder="Ex.: Impressora, tinta, prensa, papel e fornecedores"
        onChange={(e) => {
          setDescricao(e.target.value);
          agendar({ descricao: e.target.value });
        }}
      />
      <p className="mt-5 text-xs font-bold text-[var(--ks-color-text-secondary)]">Capa do módulo</p>
      <p className="mt-0.5 text-xs text-[var(--ks-color-text-muted)]">
        Foto em pé (como pôster). Aparece na área de membros com o nome do módulo por cima. Sem foto, a capa sai com a cor da área e o título.
      </p>
      <div className="mt-2 max-w-sm">
        <ImageUpload
          bucket="products"
          folder="digitais/modulos"
          value={modulo.capa ?? ""}
          onChange={(url) => void enviar({ capa: url || null })}
          disabled={!podeEditar}
          enableCrop
          cropAspect={2 / 3}
        />
      </div>
      <p className="mt-5 text-xs font-bold text-[var(--ks-color-text-secondary)]">Quando o módulo abre</p>
      <div className="mt-1.5">
        <EscolhaDeAjuste
          rotulo="Quando o módulo abre"
          valor={modulo.liberacao}
          aoEscolher={(valor) => {
            if (!podeEditar || valor === modulo.liberacao) return;
            if (valor === "imediata") void enviar({ liberacao: "imediata" });
            else if (valor === "dias") void enviar({ liberacao: "dias", liberarDias: modulo.liberarDias ?? 7 });
            else void enviar({ liberacao: "data", liberarEm: modulo.liberarEm ?? inicioDoDia(diaDoCampo(new Date(Date.now() + 7 * 86400000).toISOString())) });
          }}
          opcoes={[
            { valor: "imediata", titulo: "Logo depois da compra", texto: "O cliente vê o módulo assim que entra." },
            { valor: "dias", titulo: "Alguns dias depois da compra", texto: "Ex.: 7 dias depois, para o cliente não pular etapas." },
            { valor: "data", titulo: "Numa data", texto: "Ex.: grupo que começa junto." },
          ]}
        />
      </div>
      {modulo.liberacao === "dias" ? (
        <div className="mt-2 flex items-center gap-2 text-sm text-[var(--ks-color-text-primary)]">
          Abre
          <input
            className="ks-field w-20"
            inputMode="numeric"
            defaultValue={modulo.liberarDias ?? 7}
            disabled={!podeEditar}
            onBlur={(e) => {
              const dias = Math.round(Number(e.target.value));
              if (dias >= 1 && dias <= 3650 && dias !== modulo.liberarDias) void enviar({ liberacao: "dias", liberarDias: dias });
            }}
          />
          dias depois da compra
        </div>
      ) : null}
      {modulo.liberacao === "data" ? (
        <div className="mt-2 flex items-center gap-2 text-sm text-[var(--ks-color-text-primary)]">
          Abre em
          <input
            type="date"
            className="ks-field"
            defaultValue={diaDoCampo(modulo.liberarEm)}
            disabled={!podeEditar}
            onChange={(e) => {
              const iso = inicioDoDia(e.target.value);
              if (iso) void enviar({ liberacao: "data", liberarEm: iso });
            }}
          />
        </div>
      ) : null}
      {podeEditar ? (
        <div className="mt-5 flex justify-end">
          <button type="button" className="ks-button ks-button-ghost ks-button-sm text-[#c2263f]" onClick={aoApagar}>
            <Trash2 size={14} />
            Apagar módulo
          </button>
        </div>
      ) : null}
    </section>
  );
}
