/**
 * Para quem a área "Produtos digitais" já está aberta.
 *
 * Abre aos poucos, como os assistentes. A variável `DIGITAIS_LIBERADOS` diz
 * para quem:
 *
 *   - vazia ou ausente: fechada para todo mundo (a entrada nem aparece no menu);
 *   - uma lista de ids, separados por vírgula: só essas empresas. Vale o id da
 *     empresa ou o do dono;
 *   - `todas`: aberta para todas as empresas.
 *
 * Fechada é o padrão de propósito: subir o código sem mexer na variável não
 * muda nada para ninguém.
 */
export function digitaisLiberados(
  ids: Array<string | null | undefined>,
  configuracao: string | undefined = process.env.DIGITAIS_LIBERADOS
): boolean {
  const bruto = String(configuracao || "").trim().toLowerCase();
  if (!bruto) return false;
  if (bruto === "todas" || bruto === "todos" || bruto === "*") return true;
  const lista = new Set(bruto.split(/[\s,;]+/).filter(Boolean));
  return ids.some((id) => Boolean(id) && lista.has(String(id).toLowerCase()));
}

/* ------------------------------------------------ liberação pelo banco (193) */

export type LinhaDeLiberacao = { company_id: string; liberado: boolean | null };

/** Os ids válidos, sem repetir e sem vazio. */
export function idsDaLiberacao(ids: Array<string | null | undefined>): string[] {
  return Array.from(new Set(ids.filter((id): id is string => Boolean(id)).map((id) => String(id).trim().toLowerCase()).filter(Boolean)));
}

/** Alguma linha de `digitais_ajustes` (da empresa ou do dono) libera a área? */
export function liberadoNasLinhas(ids: Array<string | null | undefined>, linhas: LinhaDeLiberacao[] | null | undefined): boolean {
  const validos = new Set(idsDaLiberacao(ids));
  return (linhas ?? []).some((linha) => linha?.liberado === true && validos.has(String(linha.company_id || "").toLowerCase()));
}

const LEMBRAR_POR_MS = 60_000;

/**
 * A conta "variável OU banco", com a resposta do banco lembrada por um minuto.
 * Recebe quem lê o banco (para os testes não precisarem dele). Se a leitura
 * falhar, vale a última resposta lembrada ou só a variável: nunca lança erro.
 */
export function criarLiberacaoLembrada(opcoes: {
  ler: (ids: string[]) => Promise<LinhaDeLiberacao[]>;
  configuracao?: () => string | undefined;
  agora?: () => number;
  aoFalhar?: (erro: unknown) => void;
}) {
  const lembranca = new Map<string, { valor: boolean; ate: number }>();
  const configuracao = opcoes.configuracao ?? (() => process.env.DIGITAIS_LIBERADOS);
  const agora = opcoes.agora ?? (() => Date.now());

  async function consultar(ids: Array<string | null | undefined>): Promise<boolean> {
    if (digitaisLiberados(ids, configuracao())) return true;
    const validos = idsDaLiberacao(ids);
    if (!validos.length) return false;
    const chave = validos.join(",");
    const momento = agora();
    const guardado = lembranca.get(chave);
    if (guardado && guardado.ate > momento) return guardado.valor;
    try {
      const valor = liberadoNasLinhas(validos, await opcoes.ler(validos));
      lembranca.set(chave, { valor, ate: momento + LEMBRAR_POR_MS });
      return valor;
    } catch (erro) {
      opcoes.aoFalhar?.(erro);
      return guardado?.valor ?? false;
    }
  }

  /** Esquece a lembrança: de todos, ou só das chaves que tenham algum desses ids. */
  function esquecer(ids?: Array<string | null | undefined>) {
    if (!ids) {
      lembranca.clear();
      return;
    }
    const alvo = idsDaLiberacao(ids);
    for (const chave of Array.from(lembranca.keys())) if (chave.split(",").some((id) => alvo.includes(id))) lembranca.delete(chave);
  }

  return { consultar, esquecer };
}
