<template>
  <!-- Render é feito no script via função render para suportar VNodeChild em células -->
  <div />
</template>

<script lang="ts">
import { defineComponent, h, onBeforeUnmount, onMounted, PropType, ref, watch } from "vue";
import type { ComponentPublicInstance } from "vue";
import { ArrowDown, ArrowUp, MoreVertical } from "lucide-vue-next";
import { codigosResposta } from "p-respostas";
import type { EliTabelaConsulta } from "./types-eli-tabela";

export default defineComponent({
  name: "EliTabela",
  inheritAttrs: false,
  props: {
    tabela: {
      // Observação: este componente é “generic-friendly”.
      // Usamos `any` aqui para permitir passar `EliTabelaConsulta<T>` de qualquer T
      // sem brigar com invariância do TS (por causa do callback `celula(linha: T)`).
      type: Object as PropType<EliTabelaConsulta<any>>,
      required: true,
    },
  },
  setup(props) {
    const carregando = ref(false);
    const erro = ref<string | null>(null);
    const linhas = ref<unknown[]>([]);
    const quantidade = ref<number>(0);
    const acoesVisiveis = ref<boolean[][]>([]);
    const menuAberto = ref<number | null>(null);
    const menuElementos = new Map<number, HTMLElement>();
    const colunaOrdenacao = ref<string | null>(null);
    const direcaoOrdenacao = ref<"asc" | "desc">("asc");
    let carregamentoSequencial = 0;

    function registrarMenuElemento(indice: number, elemento: HTMLElement | null) {
      if (elemento) {
        menuElementos.set(indice, elemento);
      } else {
        menuElementos.delete(indice);
      }
    }

    function criarRegistradorMenu(indice: number) {
      return (elemento: Element | ComponentPublicInstance | null) => {
        if (elemento instanceof HTMLElement) {
          registrarMenuElemento(indice, elemento);
        } else {
          registrarMenuElemento(indice, null);
        }
      };
    }

    function alternarOrdenacao(chave?: string) {
      if (!chave) {
        return;
      }

      if (colunaOrdenacao.value === chave) {
        direcaoOrdenacao.value =
          direcaoOrdenacao.value === "asc" ? "desc" : "asc";
      } else {
        colunaOrdenacao.value = chave;
        direcaoOrdenacao.value = "asc";
      }

      void carregar();
    }

    function handleClickFora(evento: MouseEvent) {
      if (menuAberto.value === null) {
        return;
      }

      const container = menuElementos.get(menuAberto.value);
      if (container && container.contains(evento.target as Node)) {
        return;
      }

      menuAberto.value = null;
    }

    function normalizarFilhos(filhos: unknown) {
      // `VNodeChild` pode ser null/undefined/boolean.
      // Para a assinatura de `h()`, normalizamos para string vazia.
      if (filhos === null || filhos === undefined || filhos === false) {
        return "";
      }
      return filhos as never;
    }

    function renderErro(mensagem: string) {
      return h(
        "div",
        {
          class: "eli-tabela eli-tabela--erro",
          role: "alert",
        },
        [
          h("div", { class: "eli-tabela__erro-titulo" }, "Erro"),
          h("div", { class: "eli-tabela__erro-mensagem" }, mensagem),
        ]
      );
    }

    function renderVazio(mensagem?: string) {
      return h(
        "div",
        {
          class: "eli-tabela eli-tabela--vazio",
        },
        mensagem ?? "Nenhum registro encontrado."
      );
    }

    function renderCarregando() {
      return h(
        "div",
        {
          class: "eli-tabela eli-tabela--carregando",
          "aria-busy": "true",
        },
        "Carregando..."
      );
    }

    async function carregar() {
      const idCarregamento = ++carregamentoSequencial;
      carregando.value = true;
      erro.value = null;
      acoesVisiveis.value = [];
      menuAberto.value = null;
      menuElementos.clear();

      const parametrosOrdenacao = colunaOrdenacao.value
        ? {
            coluna_ordem: colunaOrdenacao.value as never,
            direcao_ordem: direcaoOrdenacao.value,
          }
        : undefined;

      try {
        const tabelaConfig = props.tabela;
        const res = await tabelaConfig.consulta(parametrosOrdenacao);

        if (idCarregamento !== carregamentoSequencial) {
          return;
        }

        if (res.cod !== codigosResposta.sucesso) {
          linhas.value = [];
          quantidade.value = 0;
          erro.value = res.mensagem;
          return;
        }

        const valores = res.valor?.valores ?? [];
        const total = res.valor?.quantidade ?? valores.length;

        linhas.value = valores;
        quantidade.value = total;

        const acoes = tabelaConfig.acoes ?? [];

        if (!acoes.length) {
          acoesVisiveis.value = [];
          return;
        }

        const preResultado = valores.map(() =>
          acoes.map((acao) => {
            if (acao.exibir === undefined) {
              return true;
            }

            if (typeof acao.exibir === "boolean") {
              return acao.exibir;
            }

            return false;
          })
        );

        acoesVisiveis.value = preResultado;

        const visibilidade = await Promise.all(
          valores.map(async (linha) =>
            Promise.all(
              acoes.map(async (acao) => {
                if (acao.exibir === undefined) {
                  return true;
                }

                if (typeof acao.exibir === "boolean") {
                  return acao.exibir;
                }

                try {
                  const resultado = acao.exibir(linha as never);
                  return Boolean(await Promise.resolve(resultado));
                } catch {
                  return false;
                }
              })
            )
          )
        );

        if (idCarregamento === carregamentoSequencial) {
          acoesVisiveis.value = visibilidade;
        }
      } catch (e) {
        if (idCarregamento !== carregamentoSequencial) {
          return;
        }

        linhas.value = [];
        quantidade.value = 0;
        erro.value = e instanceof Error ? e.message : "Erro ao carregar dados.";
      } finally {
        if (idCarregamento === carregamentoSequencial) {
          carregando.value = false;
        }
      }
    }

    onMounted(() => {
      document.addEventListener("click", handleClickFora);
      void carregar();
    });

    onBeforeUnmount(() => {
      document.removeEventListener("click", handleClickFora);
      menuElementos.clear();
    });
    watch(
      () => props.tabela,
      () => {
        menuAberto.value = null;
        menuElementos.clear();
        colunaOrdenacao.value = null;
        direcaoOrdenacao.value = "asc";
        // Caso a definição de tabela/consulta mude
        void carregar();
      }
    );

    watch(linhas, () => {
      menuAberto.value = null;
      menuElementos.clear();
    });

    return () => {
      const tabela = props.tabela;

      if (carregando.value) {
        return renderCarregando();
      }

      if (erro.value) {
        return renderErro(erro.value);
      }

      const colunas = tabela.colunas;
      const acoes = tabela.acoes ?? [];
      const temAcoes = acoes.length > 0;

      if (!linhas.value.length) {
        return renderVazio(tabela.mensagemVazio);
      }

      const cabecalho = colunas.map((coluna) => {
        const chaveOrdenacao =
          coluna.coluna_ordem !== undefined
            ? (coluna.coluna_ordem as unknown as string)
            : undefined;
        const ordenavel = Boolean(chaveOrdenacao);
        const ativa = ordenavel && colunaOrdenacao.value === chaveOrdenacao;
        const iconeOrdenacao = ordenavel
          ? ativa
            ? h(direcaoOrdenacao.value === "asc" ? ArrowUp : ArrowDown, {
                class: "eli-tabela__th-icone",
                size: 16,
                strokeWidth: 2,
                "aria-hidden": "true",
              })
            : h(ArrowUp, {
                class: "eli-tabela__th-icone eli-tabela__th-icone--oculto",
                size: 16,
                strokeWidth: 2,
                "aria-hidden": "true",
              })
          : null;

        const conteudo = ordenavel
          ? h(
              "button",
              {
                type: "button",
                class: [
                  "eli-tabela__th-botao",
                  ativa ? "eli-tabela__th-botao--ativo" : undefined,
                ],
                onClick: () => alternarOrdenacao(chaveOrdenacao),
              },
              [
                h("span", { class: "eli-tabela__th-texto" }, coluna.rotulo),
                iconeOrdenacao,
              ]
            )
          : h("span", { class: "eli-tabela__th-label" }, coluna.rotulo);

        return h(
          "th",
          {
            class: [
              "eli-tabela__th",
              ordenavel ? "eli-tabela__th--ordenavel" : undefined,
            ],
            scope: "col",
          },
          conteudo
        );
      });

      if (temAcoes) {
        cabecalho.push(
          h(
            "th",
            { class: "eli-tabela__th eli-tabela__th--acoes", scope: "col" },
            "Ações"
          )
        );
      }

      return h(
        "div",
        {
          class: "eli-tabela",
        },
        [
          h("table", { class: "eli-tabela__table" }, [
            h(
              "thead",
              { class: "eli-tabela__thead" },
              h(
                "tr",
                { class: "eli-tabela__tr eli-tabela__tr--header" },
                cabecalho
              )
            ),
            h(
              "tbody",
              { class: "eli-tabela__tbody" },
              linhas.value.map((linha, i) => {
                const celulas = colunas.map((coluna, j) =>
                  h(
                    "td",
                    {
                      class: [
                        "eli-tabela__td",
                        coluna.acao ? "eli-tabela__td--clicavel" : undefined,
                      ],
                      key: `${i}-${j}`,
                      onClick: coluna.acao ? () => coluna.acao?.() : undefined,
                    },
                    normalizarFilhos(coluna.celula(linha as never))
                  )
                );

                if (temAcoes) {
                  const visibilidade = acoesVisiveis.value[i] ?? [];
                  const acoesDisponiveis = acoes
                    .map((acao, indice) => {
                      const fallbackVisivel =
                        acao.exibir === undefined
                          ? true
                          : typeof acao.exibir === "boolean"
                          ? acao.exibir
                          : false;

                      return {
                        acao,
                        indice,
                        visivel: visibilidade[indice] ?? fallbackVisivel,
                      };
                    })
                    .filter((item) => item.visivel);

                  const possuiAcoes = acoesDisponiveis.length > 0;

                  if (!possuiAcoes && menuAberto.value === i) {
                    menuAberto.value = null;
                  }

                  const estaAberto = menuAberto.value === i;
                  const toggleId = `eli-tabela-acoes-toggle-${i}`;
                  const menuId = `eli-tabela-acoes-menu-${i}`;

                  const botaoToggle = h(
                    "button",
                    {
                      id: toggleId,
                      class: "eli-tabela__acoes-toggle",
                      type: "button",
                      disabled: !possuiAcoes,
                      onClick: (evento: MouseEvent) => {
                        evento.stopPropagation();
                        if (!possuiAcoes) {
                          return;
                        }
                        menuAberto.value = estaAberto ? null : i;
                      },
                      "aria-haspopup": "menu",
                      "aria-expanded": estaAberto ? "true" : "false",
                      "aria-controls": possuiAcoes ? menuId : undefined,
                      "aria-label": possuiAcoes
                        ? "Ações da linha"
                        : "Nenhuma ação disponível",
                      title: possuiAcoes ? "Ações" : "Nenhuma ação disponível",
                    },
                    [
                      h(MoreVertical, {
                        class: "eli-tabela__acoes-toggle-icone",
                        size: 18,
                        strokeWidth: 2,
                      }),
                    ]
                  );

                  const menu =
                    estaAberto && possuiAcoes
                      ? h(
                          "ul",
                          {
                            id: menuId,
                            class: "eli-tabela__acoes-menu",
                            role: "menu",
                            "aria-labelledby": toggleId,
                          },
                          acoesDisponiveis.map(({ acao, indice }) =>
                            h(
                              "li",
                              {
                                key: `acao-${indice}`,
                                class: "eli-tabela__acoes-item",
                                role: "none",
                              },
                              h(
                                "button",
                                {
                                  type: "button",
                                  class: "eli-tabela__acoes-item-botao",
                                  style: {
                                    color: acao.cor,
                                  },
                                  onClick: (evento: MouseEvent) => {
                                    evento.stopPropagation();
                                    menuAberto.value = null;
                                    acao.acao(linha as never);
                                  },
                                  role: "menuitem",
                                  "aria-label": acao.rotulo,
                                  title: acao.rotulo,
                                },
                                [
                                  h(acao.icone, {
                                    class: "eli-tabela__acoes-item-icone",
                                    size: 16,
                                    strokeWidth: 2,
                                  }),
                                  h(
                                    "span",
                                    { class: "eli-tabela__acoes-item-texto" },
                                    acao.rotulo
                                  ),
                                ]
                              )
                            )
                          )
                        )
                      : null;

                  const classesContainer = [
                    "eli-tabela__acoes-container",
                  ];

                  if (estaAberto) {
                    classesContainer.push("eli-tabela__acoes-container--aberto");
                  }

                  celulas.push(
                    h(
                      "td",
                      {
                        class: ["eli-tabela__td", "eli-tabela__td--acoes"],
                        key: `${i}-acoes`,
                      },
                      h(
                        "div",
                        {
                          class: classesContainer,
                          ref: criarRegistradorMenu(i),
                        },
                        [botaoToggle, menu]
                      )
                    )
                  );
                }

                return h(
                  "tr",
                  { class: "eli-tabela__tr", key: i },
                  celulas
                );
              })
            ),
          ]),
        ]
      );
    };
  },
});
</script>

<style scoped>
.eli-tabela {
  width: 100%;
}

.eli-tabela__table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 12px;
  overflow: visible;
}

.eli-tabela__th,
.eli-tabela__td {
  padding: 10px 12px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  vertical-align: top;
}

.eli-tabela__th {
  text-align: left;
  font-weight: 600;
  background: rgba(0, 0, 0, 0.03);
}

.eli-tabela__th--ordenavel {
  padding: 0;
}

.eli-tabela__th--ordenavel .eli-tabela__th-botao {
  padding: 10px 12px;
}

.eli-tabela__th-botao {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  width: 100%;
  background: transparent;
  border: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
  transition: color 0.2s ease;
}

.eli-tabela__th-botao:hover,
.eli-tabela__th-botao:focus-visible {
  color: rgba(15, 23, 42, 0.85);
}

.eli-tabela__th-botao:focus-visible {
  outline: 2px solid rgba(37, 99, 235, 0.45);
  outline-offset: 2px;
}

.eli-tabela__th-botao--ativo {
  color: rgba(37, 99, 235, 0.95);
}

.eli-tabela__th-texto {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
}

.eli-tabela__th-icone {
  flex-shrink: 0;
}

.eli-tabela__th-icone--oculto {
  opacity: 0;
}

.eli-tabela__tr:last-child .eli-tabela__td {
  border-bottom: none;
}

.eli-tabela__td--clicavel {
  cursor: pointer;
}

.eli-tabela__td--clicavel:hover {
  background: rgba(0, 0, 0, 0.03);
}

.eli-tabela--erro {
  border: 1px solid rgba(220, 53, 69, 0.35);
  border-radius: 12px;
  padding: 12px;
}

.eli-tabela--carregando {
  border: 1px dashed rgba(0, 0, 0, 0.25);
  border-radius: 12px;
  padding: 12px;
  opacity: 0.8;
}

.eli-tabela__erro-titulo {
  font-weight: 700;
  margin-bottom: 4px;
}

.eli-tabela__erro-mensagem {
  opacity: 0.9;
}

.eli-tabela--vazio {
  border: 1px dashed rgba(0, 0, 0, 0.25);
  border-radius: 12px;
  padding: 12px;
  opacity: 0.8;
}

.eli-tabela__th--acoes {
  text-align: right;
  white-space: nowrap;
}

.eli-tabela__td--acoes {
  white-space: nowrap;
}

.eli-tabela__acoes-container {
  display: flex;
  justify-content: flex-end;
  position: relative;
  z-index: 1;
}

.eli-tabela__acoes-container--aberto {
  z-index: 200;
}

.eli-tabela__acoes-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 9999px;
  border: none;
  background: transparent;
  color: rgba(15, 23, 42, 0.72);
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.eli-tabela__acoes-toggle-icone {
  display: block;
}

.eli-tabela__acoes-toggle:hover,
.eli-tabela__acoes-toggle:focus-visible {
  background-color: rgba(15, 23, 42, 0.08);
  color: rgba(15, 23, 42, 0.95);
}

.eli-tabela__acoes-toggle:focus-visible {
  outline: 2px solid rgba(37, 99, 235, 0.45);
  outline-offset: 2px;
}

.eli-tabela__acoes-toggle:disabled {
  cursor: default;
  color: rgba(148, 163, 184, 0.8);
  background: transparent;
}

.eli-tabela__acoes-menu {
  position: absolute;
  top: 100%;
  margin-top: 8px;
  right: 0;
  min-width: 180px;
  padding: 6px 0;
  margin: 0;
  list-style: none;
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.08);
  border-radius: 10px;
  box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
  z-index: 1000;
}

.eli-tabela__acoes-item {
  margin: 0;
}

.eli-tabela__acoes-item-botao {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  border: none;
  background: transparent;
  font-size: 0.9rem;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.eli-tabela__acoes-item-botao:hover,
.eli-tabela__acoes-item-botao:focus-visible {
  background-color: rgba(15, 23, 42, 0.06);
}

.eli-tabela__acoes-item-botao:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: -2px;
}

.eli-tabela__acoes-item-icone {
  flex-shrink: 0;
}

.eli-tabela__acoes-item-texto {
  flex: 1;
  text-align: left;
}
</style>
