import { createClient } from "../../src/compat/supabase.js";
import * as kv from "./kv_store.tsx";
import * as kvAtomic from "./kv_atomic.tsx";
import * as usuarios from "./usuarios.tsx";
import * as mpesa from "./mpesa.tsx";

// Extraido de index.tsx (Fase 1 do plano de reducao do cold-start) para ser
// partilhado entre a funcao principal (make-server-7249dcd9) e a nova funcao
// admin-server-7249dcd9, sem duplicar logica que iria divergir com o tempo.

export function decodificarPayloadJwt(token?: string): any | null {
  if (!token) return null;
  try {
    const partes = token.split('.');
    if (partes.length < 2) return null;
    const base64 = partes[1].replace(/-/g, '+').replace(/_/g, '/');
    const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');
    return JSON.parse(atob(padded));
  } catch (_error) {
    return null;
  }
}

export function isEmpresaTonlineId(empresaId?: string | null) {
  const valor = String(empresaId || "").trim().toLowerCase();
  return ["tonline", "empresa_tonline", "tonline_ei", "tonline_01", "1120168"].includes(valor);
}

export function isUsuarioSuperAdminTonline(usuario: any) {
  return usuario?.nivel === "super_admin" || (usuario?.tipo === "admin" && isEmpresaTonlineId(usuario?.empresaId));
}

export async function obterUsuarioAutenticadoRequest(c: any) {
  const accessToken = c.req.header("X-User-Token");
  if (!accessToken) return null;

  const supabaseUrl = Deno.env.get("SUPABASE_URL") ?? "";
  const anonKey = Deno.env.get("SUPABASE_ANON_KEY") ?? "";
  const supabase = createClient(supabaseUrl, anonKey);
  const { data: { user }, error } = await supabase.auth.getUser(accessToken);

  if (error || !user?.email) {
    return null;
  }

  return await usuarios.buscarUsuarioPorEmail(user.email);
}

export async function validarSuperAdminTonlineRequest(c: any) {
  const usuario = await obterUsuarioAutenticadoRequest(c);

  if (!isUsuarioSuperAdminTonline(usuario)) {
    return {
      permitido: false,
      resposta: c.json({
        success: false,
        error: "Acesso reservado à administração da TONLINE",
      }, 403),
    };
  }

  return { permitido: true, usuario };
}

export async function validarAcessoEmpresaRequest(c: any, empresaId: string) {
  const usuario = await obterUsuarioAutenticadoRequest(c);

  if (!usuario) {
    return {
      permitido: false,
      resposta: c.json({
        success: false,
        error: "Sessao invalida ou expirada",
      }, 401),
    };
  }

  if (usuario.ativo === false) {
    return {
      permitido: false,
      resposta: c.json({
        success: false,
        error: "Utilizador inactivo",
      }, 403),
    };
  }

  if (isUsuarioSuperAdminTonline(usuario)) {
    return { permitido: true, usuario };
  }

  if (String(usuario.empresaId || "") !== String(empresaId || "")) {
    return {
      permitido: false,
      resposta: c.json({
        success: false,
        error: "Nao tem permissao para aceder a esta empresa",
      }, 403),
    };
  }

  return { permitido: true, usuario };
}

// As rotas de gestao de utilizadores nao passavam por nenhuma verificacao de
// sessao/permissao (achado critico da auditoria de Gestao e Administracao):
// qualquer chamada directa a API conseguia criar um super_admin, repor a
// password de qualquer utilizador, ou editar/eliminar utilizadores de
// qualquer empresa. Estes dois helpers fecham essa lacuna: exigem que o
// chamador tenha a permissao de gerir utilizadores, e impedem que alguem
// sem ser super_admin actue sobre utilizadores de outra empresa ou crie/
// promova alguem a super_admin.
export async function validarGestaoUsuariosRequest(c: any) {
  const usuario = await obterUsuarioAutenticadoRequest(c);

  if (!usuario) {
    return {
      permitido: false,
      resposta: c.json({ success: false, error: "Sessao invalida ou expirada" }, 401),
    };
  }

  if (usuario.ativo === false) {
    return {
      permitido: false,
      resposta: c.json({ success: false, error: "Utilizador inactivo" }, 403),
    };
  }

  if (isUsuarioSuperAdminTonline(usuario)) {
    return { permitido: true, usuario };
  }

  if (usuario.permissoes?.gerenciarUsuarios !== true) {
    return {
      permitido: false,
      resposta: c.json({ success: false, error: "Nao tem permissao para gerir utilizadores" }, 403),
    };
  }

  return { permitido: true, usuario };
}

export async function validarAcessoUsuarioAlvoRequest(c: any, usuarioAlvoId: string) {
  const gestao = await validarGestaoUsuariosRequest(c);
  if (!gestao.permitido) return gestao;

  const usuarioAlvo = await usuarios.buscarUsuarioPorId(usuarioAlvoId);
  if (!usuarioAlvo) {
    return {
      permitido: false,
      resposta: c.json({ success: false, error: "Utilizador nao encontrado" }, 404),
    };
  }

  if (
    !isUsuarioSuperAdminTonline(gestao.usuario) &&
    String(usuarioAlvo.empresaId || "") !== String(gestao.usuario.empresaId || "")
  ) {
    return {
      permitido: false,
      resposta: c.json({ success: false, error: "Nao tem permissao para gerir utilizadores de outra empresa" }, 403),
    };
  }

  return { permitido: true, usuario: gestao.usuario, usuarioAlvo };
}

export function bloquearEscaladaPrivilegioUsuario(c: any, chamador: any, dados: any) {
  if (dados?.nivel === "super_admin" && !isUsuarioSuperAdminTonline(chamador)) {
    return c.json({ success: false, error: "Apenas um super administrador pode atribuir o nivel super_admin" }, 403);
  }
  if (dados?.permissoes?.gerenciarUsuarios === true && chamador?.permissoes?.gerenciarUsuarios !== true && !isUsuarioSuperAdminTonline(chamador)) {
    return c.json({ success: false, error: "Nao pode conceder uma permissao que voce proprio nao possui" }, 403);
  }
  return null;
}

export async function lerBodyJsonSeguro(c: any, fallback: any = {}) {
  // ✅ O Hono já cacheia o resultado de c.req.json() por pedido — não
  // manipular c.req.bodyCache manualmente (ver nota em index.tsx sobre o
  // erro "bodyCache[anyCachedKey].then is not a function").
  try {
    return await c.req.json();
  } catch (_error) {
    return fallback;
  }
}

export function gerarSenhaTemporaria(tamanho = 10): string {
  const alfabeto = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789@#$";
  const bytes = new Uint8Array(tamanho);
  crypto.getRandomValues(bytes);
  return Array.from(bytes, (byte) => alfabeto[byte % alfabeto.length]).join("");
}

export const RESERVA_CADASTRO_TTL_MS = 20 * 60 * 1000;
export const RESERVA_RENOVACAO_TTL_MS = 60 * 60 * 1000;

export type ReservaPagamento = {
  status: "PROCESSING" | "COMPLETED" | "REVIEW_REQUIRED";
  tipo: "cadastro" | "renovacao" | "levantamento_revendedor";
  criadoEm: string;
  expiraEm?: string;
  [key: string]: unknown;
};

export function reservaEstaExpirada(reserva: any): boolean {
  if (!reserva?.expiraEm || reserva.status === "COMPLETED" || reserva.status === "REVIEW_REQUIRED") {
    return false;
  }

  return Date.parse(String(reserva.expiraEm)) < Date.now();
}

export async function reservarChavesPagamento(
  chaves: string[],
  dados: Omit<ReservaPagamento, "status" | "criadoEm" | "expiraEm">,
  ttlMs: number,
): Promise<{ success: boolean; error?: string; chave?: string }> {
  const chavesOrdenadas = [...new Set(chaves)].sort();
  const reservadas: string[] = [];
  const agora = new Date();
  const reserva: ReservaPagamento = {
    ...dados,
    status: "PROCESSING",
    criadoEm: agora.toISOString(),
    expiraEm: new Date(agora.getTime() + ttlMs).toISOString(),
  };

  try {
    for (const chave of chavesOrdenadas) {
      let resultado = await kvAtomic.createIfAbsent(chave, reserva);

      if (!resultado.created) {
        const existente = await kv.get(chave);
        if (reservaEstaExpirada(existente)) {
          await kvAtomic.deleteKey(chave).catch(() => {});
          resultado = await kvAtomic.createIfAbsent(chave, {
            ...reserva,
            substituiReservaExpiradaEm: new Date().toISOString(),
          });
        }
      }

      if (!resultado.created) {
        await Promise.all(reservadas.map((key) => kvAtomic.deleteKey(key).catch(() => {})));
        return { success: false, error: "Ja existe um pagamento em processamento para estes dados.", chave };
      }

      reservadas.push(chave);
    }

    return { success: true };
  } catch (error: any) {
    await Promise.all(reservadas.map((key) => kvAtomic.deleteKey(key).catch(() => {})));
    return { success: false, error: error?.message || "Nao foi possivel reservar o pagamento" };
  }
}

export async function libertarReservasPagamento(chaves: string[]): Promise<void> {
  await Promise.all([...new Set(chaves)].map((chave) => kvAtomic.deleteKey(chave).catch(() => {})));
}

export async function marcarReservasPagamento(
  chaves: string[],
  status: ReservaPagamento["status"],
  dados: Record<string, unknown>,
): Promise<void> {
  await Promise.all(
    [...new Set(chaves)].map(async (chave) => {
      const existente = (await kv.get(chave)) || {};
      await kv.set(chave, {
        ...existente,
        ...dados,
        status,
        dataUltimaAtualizacao: new Date().toISOString(),
        expiraEm: undefined,
      });
    }),
  );
}

export function criarSnapshotPagamento(input: {
  tipo: "primeiro_pagamento" | "renovacao";
  plano: any;
  valorRecorrente: number;
  taxaLicenca: number;
  cicloMeses: number;
  checkout?: mpesa.MpesaCheckout;
  dataVencimentoAnterior?: string;
  dataProximaRenovacao?: string;
}) {
  return {
    tipo: input.tipo,
    planoId: input.plano?.id,
    planoNome: input.plano?.nome,
    cicloMeses: input.cicloMeses,
    valorRecorrente: input.valorRecorrente,
    taxaLicenca: input.taxaLicenca,
    valorTotal: input.valorRecorrente + input.taxaLicenca,
    dataVencimentoAnterior: input.dataVencimentoAnterior,
    dataProximaRenovacao: input.dataProximaRenovacao,
    mpesa: input.checkout
      ? {
          checkoutId: input.checkout.id,
          conversationId: input.checkout.conversationId,
          transactionId: input.checkout.transactionId,
          transactionReference: input.checkout.transactionReference,
          thirdPartyReference: input.checkout.thirdPartyReference,
          serviceProviderCode: input.checkout.serviceProviderCode,
          responseCode: input.checkout.responseCode,
        }
      : undefined,
    capturadoEm: new Date().toISOString(),
  };
}
