import {
  ACAO_POR_CHAVE,
  CATALOGO,
  ORDEM_NIVEL,
  type NivelModulo,
} from "./catalogo-permissoes.ts";

/**
 * Valida o que vai para a coluna `permissions`.
 *
 * A rota PATCH gravava o corpo da requisição direto: `patch.permissions =
 * body.permissions`. Qualquer coisa entrava — uma chave com erro de digitação,
 * um nível escrito errado, um array, um objeto aninhado. Nada disso dá erro na
 * hora; dá erro meses depois, quando alguém percebe que a permissão que o admin
 * marcou nunca valeu, porque o nome estava errado por uma letra.
 *
 * O resolvedor já ignora o que não reconhece, então lixo aqui nunca virou
 * acesso indevido. O problema é o inverso: vira acesso que o admin acha que deu
 * e não deu. Por isso a validação recusa em vez de limpar em silêncio — quem
 * está configurando precisa saber que não pegou.
 */

export type AjustesValidos = Record<string, boolean | NivelModulo>;

export type ResultadoValidacao =
  | { ok: true; ajustes: AjustesValidos }
  | { ok: false; erro: string };

const MODULOS = new Set(CATALOGO.map((m) => m.chave));

const NIVEIS = Object.keys(ORDEM_NIVEL) as NivelModulo[];

export function validarAjustesDePermissao(bruto: unknown): ResultadoValidacao {
  if (bruto === null || bruto === undefined) return { ok: true, ajustes: {} };

  if (typeof bruto !== "object" || Array.isArray(bruto)) {
    return { ok: false, erro: "As permissões precisam vir como um objeto." };
  }

  const ajustes: AjustesValidos = {};

  for (const [chave, valor] of Object.entries(bruto as Record<string, unknown>)) {
    /* Nome de módulo: o valor é o degrau da escada. */
    if (MODULOS.has(chave)) {
      if (typeof valor !== "string") {
        return { ok: false, erro: `O nível do módulo "${chave}" precisa ser um texto.` };
      }
      const nivel = valor.toLowerCase() as NivelModulo;
      if (!NIVEIS.includes(nivel)) {
        return {
          ok: false,
          erro: `Nível "${valor}" não existe. Use: ${NIVEIS.join(", ")}.`,
        };
      }
      ajustes[chave] = nivel;
      continue;
    }

    /* Nome de ação: o valor é permitir ou negar. */
    if (ACAO_POR_CHAVE.has(chave)) {
      if (typeof valor !== "boolean") {
        return { ok: false, erro: `A permissão "${chave}" precisa ser verdadeiro ou falso.` };
      }
      ajustes[chave] = valor;
      continue;
    }

    return { ok: false, erro: `A permissão "${chave}" não existe.` };
  }

  return { ok: true, ajustes };
}

/**
 * Lê o que já está gravado, descartando o que não dá para interpretar.
 *
 * Estrito para gravar, tolerante para ler. São 165 empresas cujas permissões
 * foram gravadas antes de existir validação, e uma sobra inválida lá não pode
 * impedir o modal de abrir — o resolvedor ignora essa sobra de qualquer forma,
 * então mostrar o resto é a leitura honesta do que está valendo.
 */
export function lerAjustesArmazenados(bruto: unknown): AjustesValidos {
  if (!bruto || typeof bruto !== "object" || Array.isArray(bruto)) return {};

  const ajustes: AjustesValidos = {};
  for (const [chave, valor] of Object.entries(bruto as Record<string, unknown>)) {
    if (MODULOS.has(chave) && typeof valor === "string") {
      const nivel = valor.toLowerCase() as NivelModulo;
      if (NIVEIS.includes(nivel)) ajustes[chave] = nivel;
      continue;
    }
    if (ACAO_POR_CHAVE.has(chave) && typeof valor === "boolean") {
      ajustes[chave] = valor;
    }
  }
  return ajustes;
}
