import { ProxyAgent, type Dispatcher } from "undici";
import { BinanceC2CClient } from "../binance/client.js";
import { decrypt } from "../crypto/secretbox.js";
import { findRowById } from "./repository.js";
import type { ExchangeAccountRow } from "./types.js";

/**
 * Monta clientes de exchange a partir de uma conta do cofre, decifrando as
 * credenciais e plugando o proxy daquela conta. Nada fora daqui deve mexer
 * com api_secret em claro nem instanciar o cliente diretamente.
 */

/**
 * Constrói um dispatcher de proxy (undici) a partir das colunas de proxy.
 * Retorna `undefined` quando o proxy está desligado ou incompleto.
 *
 * Observação: o `ProxyAgent` do undici cobre proxies HTTP/HTTPS. SOCKS5 ainda
 * não é suportado aqui — se configurado, lançamos um erro claro em vez de
 * silenciosamente ignorar o proxy (vazaria o IP real).
 */
export function buildProxyDispatcher(row: ExchangeAccountRow): Dispatcher | undefined {
  if (!row.proxy_enabled) return undefined;
  if (!row.proxy_protocol || !row.proxy_host || !row.proxy_port) {
    throw new Error(
      `Conta ${row.id}: proxy habilitado mas incompleto (protocolo/host/porta).`,
    );
  }
  if (row.proxy_protocol === "socks5") {
    throw new Error(
      `Conta ${row.id}: proxy SOCKS5 ainda não é suportado (use http/https).`,
    );
  }

  const uri = `${row.proxy_protocol}://${row.proxy_host}:${row.proxy_port}`;
  const opts: ProxyAgent.Options = { uri };
  if (row.proxy_user) {
    const pass = row.proxy_pass_enc ? decrypt(row.proxy_pass_enc) : "";
    const basic = Buffer.from(`${row.proxy_user}:${pass}`).toString("base64");
    opts.token = `Basic ${basic}`;
  }
  return new ProxyAgent(opts);
}

/** Instancia o cliente Binance C2C de uma conta (decifra secret + proxy). */
export function buildBinanceClient(row: ExchangeAccountRow): BinanceC2CClient {
  if (row.exchange !== "binance") {
    throw new Error(`Conta ${row.id} não é Binance (exchange=${row.exchange}).`);
  }
  const dispatcher = buildProxyDispatcher(row);
  return new BinanceC2CClient({
    apiKey: row.api_key,
    secretKey: decrypt(row.api_secret_enc),
    userId: row.binance_user_id ?? "",
    dispatcher,
  });
}

/** Conveniência: carrega a conta por id e já devolve o cliente Binance. */
export async function binanceClientForAccount(id: number): Promise<BinanceC2CClient> {
  const row = await findRowById(id);
  if (!row) throw new Error(`Conta ${id} não encontrada.`);
  return buildBinanceClient(row);
}
