import { exchangeClientForAccount } from "../exchange/index.js";
import { findRowById, list as listAccounts } from "../accounts/repository.js";
import { upsertOrder, getById as getOrderById, type OrderRow } from "../repos/orders.js";
import { upsertCounterparty, getById as getCounterparty } from "../repos/counterparties.js";
import { insertMessage } from "../repos/chat.js";
import { postIncomingToTelegram } from "../telegram/bridge.js";
import { runOrderAutomations } from "./automations.js";
import type { Exchange } from "../exchange/types.js";

/**
 * Núcleo do worker de chat (modo polling): para cada conta ativa, espelha as
 * ordens ativas, dispara as automações e reflete o chat. Mensagens novas de
 * cliente (direction='in') são postadas no Telegram.
 *
 * NB: a Binance também oferece chat via WSS (menor latência) — o polling aqui
 * é a base confiável; um source WSS pode ser plugado depois sem mudar o resto.
 */

export interface ChatRunStats {
  accounts: number;
  orders: number;
  newMessages: number;
  automationsFired: number;
  errors: string[];
}

export async function runChatOnce(): Promise<ChatRunStats> {
  const stats: ChatRunStats = {
    accounts: 0,
    orders: 0,
    newMessages: 0,
    automationsFired: 0,
    errors: [],
  };

  const accounts = (await listAccounts()).filter((a) => a.active);
  for (const acc of accounts) {
    stats.accounts++;
    try {
      await processAccount(acc.id, stats);
    } catch (err) {
      stats.errors.push(`conta ${acc.id}: ${errMsg(err)}`);
    }
  }
  return stats;
}

async function processAccount(accountId: number, stats: ChatRunStats): Promise<void> {
  const row = await findRowById(accountId);
  if (!row) return;
  const exchange: Exchange = row.exchange;
  const client = await exchangeClientForAccount(accountId);

  // Só ordens ativas/pendentes (filtra ruído de ordens fechadas).
  const orders = await client.listOrders({ pendingOnly: true, rows: 50 });

  for (const record of orders) {
    stats.orders++;

    // Espelha contraparte + ordem.
    let counterpartyId: number | null = null;
    if (record.counterpartyRef) {
      const cp = await upsertCounterparty(exchange, record.counterpartyRef, record.counterpartyName);
      counterpartyId = cp.id;
    }
    const up = await upsertOrder(accountId, record, counterpartyId);
    const orderRow = await getOrderById(up.id);
    if (!orderRow) continue;

    // Automações (idempotentes por ordem).
    const counterparty = counterpartyId ? await getCounterparty(counterpartyId) : null;
    try {
      const res = await runOrderAutomations({ client, orderRow, record, counterparty });
      stats.automationsFired += res.fired.length;
    } catch (err) {
      stats.errors.push(`automação ordem ${record.orderNo}: ${errMsg(err)}`);
    }

    // Espelha o chat e posta mensagens novas de cliente no Telegram.
    try {
      await mirrorChat(client, orderRow, record.counterpartyName, stats);
    } catch (err) {
      stats.errors.push(`chat ordem ${record.orderNo}: ${errMsg(err)}`);
    }
  }
}

async function mirrorChat(
  client: Awaited<ReturnType<typeof exchangeClientForAccount>>,
  orderRow: OrderRow,
  customerName: string | null,
  stats: ChatRunStats,
): Promise<void> {
  const messages = await client.getChatMessages(orderRow.order_no);
  for (const msg of messages) {
    const { id, isNew } = await insertMessage(msg, orderRow.id);
    if (!isNew) continue;
    stats.newMessages++;
    // Só mensagens de cliente vão pro Telegram (filtra ruído/eco).
    if (msg.direction === "in") {
      await postIncomingToTelegram(id, orderRow, customerName, msg.content);
    }
  }
}

function errMsg(err: unknown): string {
  return err instanceof Error ? err.message : String(err);
}
