import * as kv from "./kv_store.tsx";
import * as mpesa from "./mpesa.tsx";
import * as revendedores from "./revendedores.tsx";
import {
  reservarChavesPagamento,
  libertarReservasPagamento,
  marcarReservasPagamento,
} from "./admin-shared-helpers.tsx";

// Levantamento = pedido de um revendedor para transferir o saldo disponível
// de comissões para o seu número M-Pesa, via B2C (ver mpesa.tsx). Sempre o
// saldo total disponível de uma vez - não há campo de valor parcial, para
// manter o fluxo de um único clique descrito pelo utilizador.

export interface LevantamentoRevendedor {
  id: string;
  revendedorId: string;
  valor: number;
  numeroMpesa: string;
  status: "processando" | "pago" | "falhado";
  conversationId?: string;
  transactionId?: string;
  responseCode?: string;
  erro?: string;
  dataSolicitacao: string;
  dataConclusao?: string;
}

const LEVANTAMENTO_PREFIX = "levantamento_revendedor:";
const RESERVA_LEVANTAMENTO_TTL_MS = 15 * 60 * 1000;
const COOLDOWN_NUMERO_MPESA_MS = 24 * 60 * 60 * 1000;

export async function listarLevantamentos(revendedorId: string): Promise<LevantamentoRevendedor[]> {
  const todos = await kv.getByPrefix<LevantamentoRevendedor>(LEVANTAMENTO_PREFIX);
  return todos
    .filter((levantamento) => levantamento && levantamento.revendedorId === revendedorId)
    .sort((a, b) => new Date(b.dataSolicitacao).getTime() - new Date(a.dataSolicitacao).getTime());
}

export async function solicitarLevantamento(
  revendedorId: string,
): Promise<{ success: boolean; data?: LevantamentoRevendedor; error?: string }> {
  const revendedor = await revendedores.buscarRevendedorPorId(revendedorId);
  if (!revendedor || !revendedor.ativo) {
    return { success: false, error: "Revendedor não encontrado ou inactivo" };
  }

  if (revendedor.numeroMpesaAlteradoEm) {
    const alteradoEm = new Date(revendedor.numeroMpesaAlteradoEm).getTime();
    const decorrido = Date.now() - alteradoEm;
    if (Number.isFinite(alteradoEm) && decorrido < COOLDOWN_NUMERO_MPESA_MS) {
      const horasRestantes = Math.ceil((COOLDOWN_NUMERO_MPESA_MS - decorrido) / (60 * 60 * 1000));
      return {
        success: false,
        error: `O número M-Pesa foi alterado recentemente. Por segurança, aguarde mais ${horasRestantes}h antes de levantar.`,
      };
    }
  }

  const { saldoDisponivel } = await revendedores.obterSaldoRevendedor(revendedorId);
  if (saldoDisponivel <= 0) {
    return { success: false, error: "Não há saldo disponível para levantar" };
  }

  const chaveReserva = `levantamento_em_curso:${revendedorId}`;
  const reserva = await reservarChavesPagamento(
    [chaveReserva],
    { tipo: "levantamento_revendedor", revendedorId },
    RESERVA_LEVANTAMENTO_TTL_MS,
  );
  if (!reserva.success) {
    return { success: false, error: "Já existe um levantamento em processamento. Aguarde a conclusão." };
  }

  const id = `lev_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
  const agora = new Date().toISOString();

  let levantamento: LevantamentoRevendedor = {
    id,
    revendedorId,
    valor: saldoDisponivel,
    numeroMpesa: revendedor.numeroMpesa,
    status: "processando",
    dataSolicitacao: agora,
  };
  await kv.set(`${LEVANTAMENTO_PREFIX}${id}`, levantamento);

  try {
    const resultado = await mpesa.criarPagamentoB2C({
      numeroMpesa: revendedor.numeroMpesa,
      valor: saldoDisponivel,
    });

    if (resultado.success) {
      levantamento = {
        ...levantamento,
        status: "pago",
        conversationId: resultado.conversationId,
        transactionId: resultado.transactionId,
        responseCode: resultado.responseCode,
        dataConclusao: new Date().toISOString(),
      };
      await kv.set(`${LEVANTAMENTO_PREFIX}${id}`, levantamento);
      await revendedores.registrarLevantamentoConcluido(revendedorId, saldoDisponivel);
      // Ao contrário de "cadastro"/"renovacao" (reservas por checkout/ciclo,
      // que nunca devem ser reprocessadas), a chave aqui é só por revendedorId
      // e cobre todos os levantamentos futuros dele - por isso é preciso
      // libertar em vez de marcar "COMPLETED", ou nenhum levantamento
      // seguinte seria sequer tentado (reservaEstaExpirada nunca expira
      // COMPLETED). O histórico real já fica em LEVANTAMENTO_PREFIX acima.
      await libertarReservasPagamento([chaveReserva]);
      return { success: true, data: levantamento };
    }

    levantamento = {
      ...levantamento,
      status: "falhado",
      responseCode: resultado.responseCode,
      erro: resultado.error,
      dataConclusao: new Date().toISOString(),
    };
    await kv.set(`${LEVANTAMENTO_PREFIX}${id}`, levantamento);
    await libertarReservasPagamento([chaveReserva]);
    return { success: false, error: resultado.error, data: levantamento };
  } catch (error: any) {
    levantamento = {
      ...levantamento,
      status: "falhado",
      erro: error?.message || String(error),
      dataConclusao: new Date().toISOString(),
    };
    await kv.set(`${LEVANTAMENTO_PREFIX}${id}`, levantamento);
    await libertarReservasPagamento([chaveReserva]);
    return { success: false, error: levantamento.erro, data: levantamento };
  }
}
