"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Toaster } from "sonner";
import { getSupabaseClient } from "@/lib/supabase";
import { PageHeader } from "@/components/ui/page-header";
import Link from "next/link";
import { CalendarDays, ChevronLeft, ChevronRight, FileBarChart, Loader2, Plus, RefreshCw, Tags } from "lucide-react";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { carregarClientes } from "./painel-compromisso";
import { useArrastarEvento, useDeslizar, useRolagemHorizontal, type EventoArrastavel } from "./gestos";
import Alertas from "./alertas";
import LinhaDoDia from "./linha-do-dia";
import GradeDoMes from "./grade-do-mes";
import GradeHoraria from "./grade-horaria";
import PainelDetalhe from "./painel-detalhe";
import PainelCategorias from "./categorias";
import FiltroCategorias from "./filtro-categorias";
import SeletorData from "./seletor-data";
import { DicaEvento, useDica } from "./dica";
import Indicadores from "./indicadores";
import Avisos from "./avisos";
import { enfileirar, guardar, lerFila, recuperar, subirFila, useEstaOnline } from "./sem-sinal";
import { aviso } from "./aviso";
import {
  chaveDoDia,
  doTexto,
  inicioDaSemana,
  somaDias,
  type CategoriaAgenda,
  type EventoAgenda,
} from "./tipos";

type Visao = "dia" | "semana" | "mes" | "grade";

const DIAS_CURTOS = ["seg", "ter", "qua", "qui", "sex", "sáb", "dom"];
const CHAVE_OCULTAS = "ktalogu:agenda:categorias-ocultas";

function tituloDoMes(d: Date) {
  return d.toLocaleDateString("pt-BR", { month: "long", year: "numeric" });
}

/* ---------------- pecas ---------------- */

function Vazio({ texto }: { texto: string }) {
  return (
    <div className="rounded-[14px] border border-[var(--ag-border)] bg-[var(--ag-surface)] px-4 py-14 text-center shadow-[var(--ag-sombra-1)]">
      <CalendarDays className="mx-auto mb-2 h-6 w-6 text-[var(--ag-text-5)]" />
      <p className="text-[13px] text-[var(--ag-text-4)]">{texto}</p>
    </div>
  );
}

/* ---------------- pagina ---------------- */

export default function AgendaPage() {
  const [visao, setVisao] = useState<Visao>("dia");
  const [ancora, setAncora] = useState(() => new Date());
  const [eventos, setEventos] = useState<EventoAgenda[]>([]);
  const [carregando, setCarregando] = useState(true);
  const [erro, setErro] = useState<string | null>(null);
  /* Data e hora de onde a pessoa clicou: clicar as 14h e o formulario abrir
     sem horario obrigaria a redigitar o que o clique ja disse. */
  const [criarEm, setCriarEm] = useState<{ data: string; hora: number | null } | null>(null);
  const [clientes, setClientes] = useState<{ id: string; nome: string }[]>([]);
  const [categorias, setCategorias] = useState<CategoriaAgenda[]>([]);
  const [painelCategorias, setPainelCategorias] = useState(false);
  const [ocultas, setOcultas] = useState<Set<string>>(new Set());
  const [podeEscrever, setPodeEscrever] = useState(false);
  const [guardadoEm, setGuardadoEm] = useState<number | null>(null);
  const [pendentes, setPendentes] = useState(0);
  const [detalhe, setDetalhe] = useState<EventoAgenda | null>(null);
  const online = useEstaOnline();

  const hojeChave = chaveDoDia(new Date());

  /* Categorias escondidas ficam por navegador, nao por sessao: e um ajuste de
     leitura da propria pessoa, nao algo que devesse mudar a visao de mais
     ninguem na empresa. Le depois de montar para nao brigar com a hidratacao
     do servidor, que nunca viu o localStorage. */
  useEffect(() => {
    try {
      const bruto = window.localStorage.getItem(CHAVE_OCULTAS);
      if (bruto) setOcultas(new Set(JSON.parse(bruto)));
    } catch {
      /* sem preferencia guardada, comeca mostrando tudo */
    }
  }, []);

  useEffect(() => {
    try {
      window.localStorage.setItem(CHAVE_OCULTAS, JSON.stringify(Array.from(ocultas)));
    } catch {
      /* preferencia de filtro nao e critica o bastante para travar a tela */
    }
  }, [ocultas]);

  /* A janela buscada e sempre maior que a exibida: assim trocar de semana ou
     mes nao dispara consulta nova a cada clique. */
  const janela = useMemo(() => {
    if (visao === "mes" || visao === "grade") {
      const primeiro = new Date(ancora.getFullYear(), ancora.getMonth(), 1);
      return { de: somaDias(primeiro, -10), ate: somaDias(primeiro, 48) };
    }
    if (visao === "semana") {
      const seg = inicioDaSemana(ancora);
      return { de: somaDias(seg, -7), ate: somaDias(seg, 20) };
    }
    return { de: somaDias(ancora, -2), ate: somaDias(ancora, 9) };
  }, [visao, ancora]);

  const chaveJanela = `${chaveDoDia(janela.de)}|${chaveDoDia(janela.ate)}`;
  const ultimaBusca = useRef<string>("");

  const buscar = useCallback(async (forcar = false) => {
    if (!forcar && ultimaBusca.current === chaveJanela) return;
    ultimaBusca.current = chaveJanela;
    setCarregando(true);
    setErro(null);
    try {
      const supabase = getSupabaseClient();
      const { data, error } = await supabase.rpc("get_agenda_range", {
        p_inicio: chaveDoDia(janela.de),
        p_fim: chaveDoDia(janela.ate),
      });
      if (error) throw new Error(error.message);
      const lista = (data as EventoAgenda[]) ?? [];
      setEventos(lista);
      setGuardadoEm(null);
      guardar(chaveJanela, lista);
    } catch (e) {
      /* Rede caiu: mostra a ultima janela guardada em vez da tela vazia, e
         diz de quando ela e. */
      const cache = recuperar(chaveJanela);
      if (cache) {
        setEventos(cache.eventos);
        setGuardadoEm(cache.em);
        setErro(null);
      } else {
        setErro(e instanceof Error ? e.message : "Não foi possível carregar a agenda.");
        setEventos([]);
      }
    } finally {
      setCarregando(false);
    }
  }, [chaveJanela, janela.de, janela.ate]);

  useEffect(() => {
    buscar();
  }, [buscar]);

  /* Quando o sinal volta, o que ficou pendente sobe sozinho. A pessoa nao
     precisa lembrar de nada — foi justamente por estar sem rede que ela nao
     conseguiu na hora. */
  useEffect(() => {
    setPendentes(lerFila().length);
    if (!online) return;
    (async () => {
      const subiram = await subirFila();
      setPendentes(lerFila().length);
      if (subiram > 0) buscar(true);
    })();
  }, [online, buscar]);

  const carregarCategorias = useCallback(async () => {
    try {
      const r = await fetch("/api/agenda/categories");
      const j = (await r.json().catch(() => null)) as { categorias?: CategoriaAgenda[] } | null;
      setCategorias(j?.categorias ?? []);
    } catch {
      setCategorias([]);
    }
  }, []);

  useEffect(() => {
    carregarCategorias();
  }, [carregarCategorias]);

  /* Clientes e permissao sao pedidos uma vez: nao mudam ao navegar entre
     semanas, e recarregar a cada troca de data deixaria a agenda lenta. */
  useEffect(() => {
    (async () => {
      try {
        const supabase = getSupabaseClient();
        const { data: { user } } = await supabase.auth.getUser();
        if (!user) return;
        const ctx = await getCompanyContext(supabase, user);
        const ids = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
        setPodeEscrever(canCompany(ctx, user.id, "agenda.board.write"));
        setClientes(await carregarClientes(ids));
      } catch {
        /* sem clientes o painel ainda abre, so nao oferece vinculo */
      }
    })();
  }, []);

  /* O que esta escondido pelo filtro sai antes de qualquer visao agrupar por
     dia — assim nenhuma tela precisa saber que o filtro existe. */
  const eventosVisiveis = useMemo(
    () => eventos.filter((e) => !ocultas.has(e.categoria_id ?? "")),
    [eventos, ocultas]
  );

  const contagensPorCategoria = useMemo(() => {
    const m: Record<string, number> = {};
    eventos.forEach((e) => {
      const id = e.categoria_id ?? "";
      m[id] = (m[id] ?? 0) + 1;
    });
    return m;
  }, [eventos]);

  /* Agrupa uma vez por busca: refazer isso a cada render deixaria a troca de
     dia perceptivelmente lenta em meses cheios. */
  const porDia = useMemo(() => {
    const mapa = new Map<string, EventoAgenda[]>();
    eventosVisiveis.forEach((e) => {
      const lista = mapa.get(e.data);
      if (lista) lista.push(e);
      else mapa.set(e.data, [e]);
    });
    return mapa;
  }, [eventosVisiveis]);

  const diasDaSemana = useMemo(() => {
    const seg = inicioDaSemana(ancora);
    return Array.from({ length: 7 }, (_, i) => somaDias(seg, i));
  }, [ancora]);

  const diasDoMes = useMemo(() => {
    const primeiro = new Date(ancora.getFullYear(), ancora.getMonth(), 1);
    const inicio = inicioDaSemana(primeiro);
    return Array.from({ length: 42 }, (_, i) => somaDias(inicio, i));
  }, [ancora]);

  /* Os dias que os indicadores medem — sempre os mesmos que a aba mostra.
     `diasDoMes` nao serve aqui: sao 42 casas de grade, com sobra do mes
     anterior e do seguinte, e isso faria a ocupacao do mes contar dias que
     nem sao dele. */
  const diasDoPeriodo = useMemo(() => {
    if (visao === "dia") return [ancora];
    if (visao === "semana") return diasDaSemana;
    const total = new Date(ancora.getFullYear(), ancora.getMonth() + 1, 0).getDate();
    return Array.from(
      { length: total },
      (_, i) => new Date(ancora.getFullYear(), ancora.getMonth(), i + 1)
    );
  }, [visao, ancora, diasDaSemana]);

  const rotuloPeriodo =
    visao === "dia" ? "Neste dia" : visao === "semana" ? "Nesta semana" : "Neste mês";

  const { ref: scrollMesRef, temMaisPraDireita: mesTemMaisPraDireita, podeRolar: mesPodeRolar } =
    useRolagemHorizontal<HTMLDivElement>();
  const dicaMes = useDica();

  /* Clicar no vazio da grade abre o formulario ja com o dia e a hora daquele
     ponto. Sem permissao de escrita nao ha o que abrir — e a celula continua
     so um espaco em branco, sem hover nem cursor de clique. */
  const criar = useMemo(
    () =>
      podeEscrever
        ? (data: string, hora: number | null) => {
            setDetalhe(null);
            setCriarEm({ data, hora });
          }
        : undefined,
    [podeEscrever]
  );

  const eventosDoDia = porDia.get(chaveDoDia(ancora)) ?? [];

  /* Remarcar grava na origem: compromisso na propria tabela, entrega no
     pedido. E o que prova que a agenda opera o sistema em vez de so exibi-lo. */
  const remarcar = useCallback(
    async (evento: EventoArrastavel, novaData: string, novaHora: number | null) => {
      const antes = eventos;
      /* Move na tela antes da resposta: esperar o servidor para o item pular
         de dia faz o gesto parecer quebrado. Se falhar, volta. */
      setEventos((lista) =>
        lista.map((e) =>
          e.tipo === evento.tipo && e.origem_id === evento.origem_id && e.data === evento.data
            ? { ...e, data: novaData }
            : e
        )
      );
      try {
        const resposta =
          evento.tipo === "compromisso"
            ? await fetch(`/api/agenda/events/${evento.origem_id}/remarcar`, {
                method: "POST",
                headers: { "content-type": "application/json" },
                body: JSON.stringify({ event_date: novaData, start_hour: novaHora }),
              })
            : await fetch(`/api/orders/${evento.origem_id}/delivery-date`, {
                method: "POST",
                headers: { "content-type": "application/json" },
                body: JSON.stringify({ delivery_date: novaData }),
              });
        const json = (await resposta.json().catch(() => null)) as { error?: string } | null;
        if (!resposta.ok) throw new Error(json?.error || "Não consegui remarcar.");
        buscar(true);

        /* Desfazer aqui e nao no apagar: remarcar e um gesto de arrasto, e
           arrastar sem querer acontece. Voltar e so gravar a data antiga,
           entao o botao cumpre o que promete — diferente do apagar, onde nao
           haveria o que restaurar. */
        const voltarPara = evento.data;
        const horaOriginal = evento.hora ?? null;
        aviso.desfazer("Compromisso remarcado.", {
          descricao: `Movido para ${doTexto(novaData).toLocaleDateString("pt-BR")}.`,
          acao: {
            rotulo: "Desfazer",
            aoClicar: () => remarcarRef.current?.(evento, voltarPara, horaOriginal),
          },
        });
      } catch (e) {
        setEventos(antes);
        const mensagem = e instanceof Error ? e.message : "Não consegui remarcar.";
        setErro(mensagem);
        aviso.erro(mensagem);
      }
    },
    [eventos, buscar]
  );

  /* O desfazer precisa chamar o proprio remarcar. Uma referencia quebra o
     ciclo — a funcao nao pode se citar dentro do useCallback que a define. */
  const remarcarRef = useRef(remarcar);
  useEffect(() => {
    remarcarRef.current = remarcar;
  }, [remarcar]);

  const { iniciar, arrastando, posicao, diaAlvo, horaAlvo } = useArrastarEvento({ aoSoltar: remarcar });

  const deslizar = useDeslizar({
    aoAnterior: () => navegar(-1),
    aoProximo: () => navegar(1),
    bloqueado: Boolean(arrastando) || detalhe !== null || criarEm !== null,
  });

  /* Concluir some da tela na hora e sobe depois. Sem sinal a acao vai para a
     fila — quem esta na rua marca a visita como feita ali mesmo. */
  async function concluir(evento: EventoAgenda) {
    /* Checado antes de mexer em qualquer coisa: nao da pra concluir o que
       ainda nao aconteceu, e a pessoa sabe na hora, sem a tela piscar o
       compromisso sumindo e voltando por causa de uma acao que ia falhar. */
    if (evento.data > hojeChave) {
      aviso.erro("Ainda não aconteceu", {
        descricao: "Só dá para concluir a partir da data marcada.",
      });
      return;
    }
    setEventos((lista) =>
      lista.filter((e) => !(e.tipo === "compromisso" && e.origem_id === evento.origem_id))
    );
    if (!navigator.onLine) {
      enfileirar({ tipo: "concluir", eventoId: evento.origem_id, titulo: evento.titulo });
      setPendentes(lerFila().length);
      return;
    }
    try {
      const r = await fetch(`/api/agenda/events/${evento.origem_id}/concluir`, { method: "POST" });
      const json = (await r.json().catch(() => null)) as { error?: string } | null;
      if (!r.ok) {
        if (r.status === 404) return;
        throw new Error(json?.error || "Não foi possível concluir.");
      }
      aviso.sucesso("Compromisso concluído", { descricao: "Saiu da lista de pendentes." });
      buscar(true);
    } catch (e) {
      /* So enfileira quando o motivo foi rede — um 400 de regra de negocio
         (data no futuro, por exemplo) so ia falhar de novo quando o sinal
         voltasse, e a pessoa nunca saberia por que. */
      if (!navigator.onLine) {
        enfileirar({ tipo: "concluir", eventoId: evento.origem_id, titulo: evento.titulo });
        setPendentes(lerFila().length);
      } else {
        buscar(true);
        aviso.erro(e instanceof Error ? e.message : "Não foi possível concluir.");
      }
    }
  }

  function navegar(passo: number) {
    if (visao === "dia") setAncora((d) => somaDias(d, passo));
    else if (visao === "semana") setAncora((d) => somaDias(d, passo * 7));
    else setAncora((d) => new Date(d.getFullYear(), d.getMonth() + passo, 1));
  }

  function abrirEvento(id: string) {
    const achado = eventos.find((x) => x.origem_id === id);
    if (achado) abrirDetalhe(achado);
  }

  function abrirDetalhe(evento: EventoAgenda) {
    setCriarEm(null);
    setDetalhe(evento);
  }

  function alternarCategoria(id: string) {
    setOcultas((prev) => {
      const novo = new Set(prev);
      if (novo.has(id)) novo.delete(id);
      else novo.add(id);
      return novo;
    });
  }

  const rotulo =
    visao === "dia"
      ? ancora.toLocaleDateString("pt-BR", { weekday: "long", day: "2-digit", month: "long" })
      : visao === "semana"
        ? `${diasDaSemana[0].toLocaleDateString("pt-BR", { day: "2-digit", month: "short" })} — ${diasDaSemana[6].toLocaleDateString("pt-BR", { day: "2-digit", month: "short" })}`
        : tituloDoMes(ancora);

  return (
    <div className="min-h-screen bg-[var(--ks-color-bg-body)] pb-24 lg:pb-10">
      {/* Sem padding lateral proprio: `.ks-dashboard-content` do layout ja
          aplica o dele, e os dois somados comiam 64px de cada lado — largura
          que na agenda vira coluna mais larga e titulo inteiro. */}
      <div className="w-full space-y-4 py-6">
        <PageHeader
          eyebrow="Operação"
          title="Agenda"
          subtitle="Todos os prazos do sistema numa tela só"
          actions={
            <>
              {/* Fora do `podeEscrever`: ler relatorio nao e escrever na
                  agenda — quem so consulta continua podendo analisar. */}
              <Link
                href="/dashboard/agenda/relatorio"
                className="flex h-11 items-center gap-2 rounded-[9px] border border-[var(--ag-border-strong)] bg-[var(--ag-surface)] px-[14px] text-[13px] font-semibold text-[var(--ag-text-2)] transition-colors hover:border-[var(--ag-border-strong)] hover:bg-[var(--ag-surface-2)] sm:h-9"
              >
                <FileBarChart className="h-4 w-4" />
                Relatório
              </Link>
              {podeEscrever ? (
              <button
                type="button"
                onClick={() => {
                  setDetalhe(null);
                  setCriarEm({ data: chaveDoDia(ancora), hora: null });
                }}
                className="flex h-11 items-center gap-2 rounded-[9px] bg-[#6C3CF0] px-[14px] text-[13px] font-semibold text-white transition-colors hover:bg-[#5C31D8] sm:h-9"
              >
                <Plus className="h-4 w-4" />
                Novo compromisso
              </button>
              ) : null}
            </>
          }
        />

        <Indicadores eventos={eventosVisiveis} dias={diasDoPeriodo} periodo={rotuloPeriodo} />

        {(!online || guardadoEm || pendentes > 0) && (
          <div className="flex flex-wrap items-center gap-2 rounded-xl bg-amber-50 px-3 py-2.5 text-xs font-semibold text-amber-800 dark:bg-amber-500/10 dark:text-amber-300">
            {!online && <span>Sem conexão.</span>}
            {guardadoEm && (
              <span>
                Mostrando o que foi guardado às{" "}
                {new Date(guardadoEm).toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" })}.
              </span>
            )}
            {pendentes > 0 && (
              <span>
                {pendentes} {pendentes === 1 ? "ação aguarda" : "ações aguardam"} o sinal voltar.
              </span>
            )}
          </div>
        )}

        <Avisos />

        {/* Controles. No celular a barra fica com alvos grandes e o seletor de
            visao ocupa a largura inteira, para acerto com o polegar. */}
        <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between sm:gap-6">
          <div className="flex items-center gap-2">
            <button
              type="button"
              onClick={() => navegar(-1)}
              aria-label="Período anterior"
              className="flex h-11 w-11 items-center justify-center rounded-[9px] border border-[var(--ag-border-strong)] bg-[var(--ag-surface)] text-[var(--ag-text-2)] transition-colors hover:border-[var(--ag-border-strong)] hover:bg-[var(--ag-surface-2)] sm:h-9 sm:w-9"
            >
              <ChevronLeft className="h-4 w-4" />
            </button>
            <button
              type="button"
              onClick={() => setAncora(new Date())}
              title="Voltar para a data de hoje"
              className="flex h-11 items-center rounded-[9px] border border-[var(--ag-border-strong)] bg-[var(--ag-surface)] px-[14px] text-[12px] font-semibold text-[var(--ag-text-2)] transition-colors hover:border-[var(--ag-border-strong)] hover:bg-[var(--ag-surface-2)] sm:h-9"
            >
              Hoje
            </button>
            <button
              type="button"
              onClick={() => navegar(1)}
              aria-label="Próximo período"
              className="flex h-11 w-11 items-center justify-center rounded-[9px] border border-[var(--ag-border-strong)] bg-[var(--ag-surface)] text-[var(--ag-text-2)] transition-colors hover:border-[var(--ag-border-strong)] hover:bg-[var(--ag-surface-2)] sm:h-9 sm:w-9"
            >
              <ChevronRight className="h-4 w-4" />
            </button>
            {/* O rotulo e o proprio seletor de data: passar mes a mes para
                chegar numa data distante era o unico caminho ate aqui. */}
            <SeletorData valor={ancora} aoEscolher={setAncora} rotulo={rotulo} />
            {carregando && <Loader2 className="h-3.5 w-3.5 shrink-0 animate-spin text-[var(--ag-text-5)]" />}
          </div>

          {/* Duas linhas no celular: o seletor de visao e os icones de acao
              disputando a mesma linha apertava tudo — o rotulo das visoes
              chegava a se sobrepor. */}
          <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-4">
            <div className="grid grid-cols-4 gap-[3px] rounded-[10px] bg-[var(--ag-surface-2)] p-[3px] sm:flex sm:flex-none">
              {([
                { chave: "dia" as Visao, rotulo: "Dia" },
                { chave: "semana" as Visao, rotulo: "Semana" },
                { chave: "mes" as Visao, rotulo: "Mês" },
                { chave: "grade" as Visao, rotulo: "Grade" },
              ]).map((v) => (
                <button
                  key={v.chave}
                  type="button"
                  onClick={() => setVisao(v.chave)}
                  aria-pressed={visao === v.chave}
                  className={`flex h-9 items-center justify-center rounded-[7px] px-[14px] text-[12px] transition-colors sm:h-[30px] ${
                    visao === v.chave
                      ? "bg-[var(--ag-surface)] font-semibold text-[var(--ag-accent-text)] shadow-[var(--ag-sombra-2)]"
                      : "font-medium text-[var(--ag-text-3)] hover:text-[var(--ag-text-2)]"
                  }`}
                >
                  {v.rotulo}
                </button>
              ))}
            </div>
            <div className="flex items-center justify-end gap-2 sm:justify-start">
              <FiltroCategorias
                categorias={categorias}
                ocultas={ocultas}
                aoAlternar={alternarCategoria}
                aoMostrarTodas={() => setOcultas(new Set())}
                contagens={contagensPorCategoria}
              />
              {podeEscrever && (
                <button
                  type="button"
                  onClick={() => setPainelCategorias(true)}
                  aria-label="Categorias"
                  title="Categorias"
                  className="flex h-11 w-11 items-center justify-center rounded-[9px] border border-[var(--ag-border-strong)] bg-[var(--ag-surface)] text-[var(--ag-text-2)] transition-colors hover:border-[var(--ag-border-strong)] hover:bg-[var(--ag-surface-2)] sm:h-9 sm:w-9"
                >
                  <Tags className="h-4 w-4" />
                </button>
              )}
              <Alertas />
              <button
                type="button"
                onClick={() => buscar(true)}
                aria-label="Atualizar"
                className="flex h-11 w-11 items-center justify-center rounded-[9px] border border-[var(--ag-border-strong)] bg-[var(--ag-surface)] text-[var(--ag-text-2)] transition-colors hover:border-[var(--ag-border-strong)] hover:bg-[var(--ag-surface-2)] sm:h-9 sm:w-9"
              >
                <RefreshCw className="h-4 w-4" />
              </button>
            </div>
          </div>
        </div>

        {/* Deslizar troca de periodo. Fica no container das visoes, e nao na
            pagina toda, para nao disputar com a rolagem do cabecalho. */}
        <div {...deslizar}>
        {erro && (
          <p className="rounded-xl bg-rose-50 px-4 py-3 text-sm font-semibold text-rose-700 dark:bg-rose-500/10 dark:text-rose-300">
            {erro}
          </p>
        )}

        {/* ---------- DIA ---------- */}
        {visao === "dia" && (
          <div className="space-y-2">
            {eventosDoDia.length === 0 && !carregando ? (
              <Vazio texto="Nenhum prazo para este dia." />
            ) : (
              <LinhaDoDia
                eventos={eventosDoDia}
                ehHoje={chaveDoDia(ancora) === hojeChave}
                aoAbrirCompromisso={abrirEvento}
                aoArrastar={iniciar}
                aoConcluir={podeEscrever ? concluir : () => {}}
                arrastandoId={arrastando?.origem_id ?? null}
              />
            )}
          </div>
        )}

        {/* ---------- SEMANA ---------- */}
        {visao === "semana" && (
          <GradeHoraria
            dias={diasDaSemana}
            porDia={porDia}
            hojeChave={hojeChave}
            aoAbrir={abrirDetalhe}
            aoArrastar={iniciar}
            aoCriar={criar}
            diaAlvo={diaAlvo}
            horaAlvo={horaAlvo}
            arrastandoId={arrastando?.origem_id ?? null}
          />
        )}

        {visao === "grade" && (
          <GradeDoMes
            ancora={ancora}
            porDia={porDia}
            hojeChave={hojeChave}
            aoAbrir={abrirDetalhe}
            aoArrastar={iniciar}
            aoCriar={criar}
            diaAlvo={diaAlvo}
            horaAlvo={horaAlvo}
            arrastandoId={arrastando?.origem_id ?? null}
          />
        )}

        {/* ---------- MES ---------- */}
        {visao === "mes" && (
          <div className="relative overflow-hidden rounded-[14px] border border-[var(--ag-border)] bg-[var(--ag-surface)] shadow-[var(--ag-sombra-1)]">
            <div
              ref={scrollMesRef}
              className={`overflow-x-auto [&::-webkit-scrollbar]:h-[6px] [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-[var(--ag-border-strong)] [&::-webkit-scrollbar-track]:bg-transparent ${
                mesPodeRolar ? "cursor-[ew-resize]" : ""
              }`}
            >
              <div className="min-w-[760px]">
                <div className="grid grid-cols-7 border-b border-[var(--ag-line-v)]">
                  {DIAS_CURTOS.map((d) => (
                    <div
                      key={d}
                      className="border-l border-[var(--ag-line-v)] px-3 py-2.5 text-center text-[11px] font-semibold uppercase tracking-[0.03em] text-[var(--ag-text-3)] first:border-l-0"
                    >
                      {d}
                    </div>
                  ))}
                </div>
                <div className="grid grid-cols-7">
                  {diasDoMes.map((dia) => {
                    const chave = chaveDoDia(dia);
                    const doDia = porDia.get(chave) ?? [];
                    const ehHoje = chave === hojeChave;
                    const doMes = dia.getMonth() === ancora.getMonth();
                    const alvo = diaAlvo === chave;
                    return (
                      /* A celula do dia faz duas coisas conforme onde se
                         clica: o numero leva para a visao Dia, o vazio abre um
                         compromisso novo naquela data. Antes o quadrado
                         inteiro so navegava, e criar exigia voltar ao botao do
                         topo. */
                      <div
                        key={chave}
                        data-dia={chave}
                        onClick={
                          criar
                            ? (ev) => {
                                if ((ev.target as HTMLElement).closest("button")) return;
                                criar(chave, null);
                              }
                            : undefined
                        }
                        className={`min-h-[104px] border-b border-l border-[var(--ag-line-v)] p-2 text-left transition-colors first-of-type:border-l-0 hover:bg-[var(--ag-surface-2)] ${
                          criar ? "cursor-pointer" : ""
                        } ${!doMes ? "opacity-40" : ""} ${alvo ? "ring-1 ring-inset ring-[#6C3CF0]" : ""}`}
                        style={ehHoje || alvo ? { background: "rgba(108,60,240,0.045)" } : undefined}
                      >
                        <button
                          type="button"
                          onClick={() => {
                            setAncora(doTexto(chave));
                            setVisao("dia");
                          }}
                          title="Abrir este dia"
                          className="flex h-[22px] min-w-[22px] items-center justify-center rounded-[7px] px-1 text-[13px] tabular-nums transition-colors hover:bg-[rgba(108,60,240,0.1)]"
                          style={{ color: ehHoje ? "#6C3CF0" : "var(--ag-text)", fontWeight: ehHoje ? 700 : 600 }}
                        >
                          {dia.getDate()}
                        </button>
                        <span className="mt-1.5 flex flex-col gap-1">
                          {doDia.slice(0, 3).map((e) => (
                            <button
                              key={`${e.tipo}-${e.origem_id}`}
                              type="button"
                              onClick={() => abrirDetalhe(e)}
                              {...dicaMes.dicaProps(<DicaEvento evento={e} />)}
                              className="truncate rounded-[6px] px-1.5 py-[3px] text-left text-[10px] font-medium leading-tight transition-opacity hover:opacity-80"
                              style={{ background: `${e.categoria_cor}17`, color: e.categoria_cor }}
                            >
                              {e.titulo}
                            </button>
                          ))}
                          {doDia.length > 3 && (
                            <span className="px-1 text-[10px] font-semibold text-[var(--ag-text-5)]">
                              +{doDia.length - 3}
                            </span>
                          )}
                        </span>
                      </div>
                    );
                  })}
                </div>
              </div>
            </div>

            {mesTemMaisPraDireita && (
              <div
                aria-hidden
                className="pointer-events-none absolute bottom-0 right-0 top-0 z-20 w-10 bg-gradient-to-l from-[var(--ag-surface)] to-transparent"
              />
            )}

            {dicaMes.camada}
          </div>
        )}

        </div>
      </div>

      {/* Fantasma sob o dedo: sem ele o arrasto e invisivel e a pessoa nao
          sabe se pegou o item. */}
      {arrastando && posicao && (
        <div
          aria-hidden
          className="pointer-events-none fixed z-[60] max-w-[240px] -translate-x-1/2 -translate-y-1/2 rounded-lg border border-[var(--ks-color-primary)] bg-[var(--ks-color-bg-elevated)] px-3 py-2 text-[13px] font-bold text-[var(--ks-color-text-primary)] shadow-2xl"
          style={{ left: posicao.x, top: posicao.y }}
        >
          <span className="block truncate">{arrastando.titulo}</span>
          <span className="block text-[11px] font-semibold text-[var(--ks-color-primary)]">
            {diaAlvo ? `Soltar em ${doTexto(diaAlvo).toLocaleDateString("pt-BR")}` : "Arraste até um dia"}
          </span>
        </div>
      )}

      <PainelDetalhe
        evento={detalhe}
        criarEm={criarEm}
        clientes={clientes}
        categorias={categorias}
        podeEditar={podeEscrever}
        aoFechar={() => {
          setDetalhe(null);
          setCriarEm(null);
        }}
        aoConcluir={(e) => {
          setDetalhe(null);
          concluir(e);
        }}
        aoApagar={async (id) => {
          setDetalhe(null);
          try {
            const r = await fetch(`/api/agenda/events/${id}`, { method: "DELETE" });
            const j = (await r.json().catch(() => null)) as { error?: string } | null;
            if (!r.ok) throw new Error(j?.error || "Não foi possível apagar.");
            aviso.sucesso("Compromisso apagado", { descricao: "O registro foi removido da agenda." });
          } catch (e) {
            aviso.erro(e instanceof Error ? e.message : "Não foi possível apagar.");
          }
          buscar(true);
        }}
        aoAtualizou={() => {
          setDetalhe(null);
          setCriarEm(null);
          buscar(true);
        }}
      />

      <PainelCategorias
        aberto={painelCategorias}
        categorias={categorias}
        aoFechar={() => setPainelCategorias(false)}
        aoMudou={() => {
          carregarCategorias();
          buscar(true);
        }}
      />

      <Toaster richColors position="top-center" />
    </div>
  );
}
