import { BinanceC2CClient } from "./client.js";

/* ===================== Tipos de domínio ===================== */

/** tradeType: "BUY" (você compra) | "SELL" (você vende) */
export type TradeType = "BUY" | "SELL";

/** Método de pagamento associado a um anúncio */
export interface TradeMethod {
  identifier: string;
  payId: number;
  payType: string;
}

/** Corpo para criar anúncio — POST /sapi/v1/c2c/ads/post */
export interface AdPublishReq {
  asset: string;
  fiatUnit: string;
  tradeType: TradeType;
  /** 0 = preço fixo, 1 = preço flutuante (verifique na sua conta) */
  priceType: number;
  price?: number;
  /** % de flutuação quando priceType = flutuante */
  priceFloatingRatio?: number;
  rateFloatingRatio?: number;
  initAmount: number;
  minSingleTransAmount: number;
  maxSingleTransAmount: number;
  payTimeLimit?: number;
  tradeMethods: TradeMethod[];
  autoReplyMsg?: string;
  remarks?: string;
  onlineNow?: boolean;
  onlineDelayTime?: number;
  saveAsTemplate?: number;
  templateName?: string;
  classify?: string;
  [extra: string]: unknown; // demais filtros de comprador (KYC, volume, etc.)
}

/** Corpo para atualizar anúncio — POST /sapi/v1/c2c/ads/update */
export interface AdUpdateReq extends Partial<AdPublishReq> {
  advNo: string;
  advStatus?: number;
  updateMode?: string;
}

/** Detalhe de um anúncio (resposta resumida — expanda conforme necessidade) */
export interface AdDetail {
  advNo: string;
  asset: string;
  fiatUnit: string;
  tradeType: TradeType;
  price: string;
  priceType: number;
  priceFloatingRatio?: string;
  initAmount: string;
  surplusAmount: string;
  minSingleTransAmount: string;
  maxSingleTransAmount: string;
  advStatus: number;
  tradeMethods: TradeMethod[];
  [extra: string]: unknown;
}

/**
 * Anúncio de concorrente vindo do mercado (`ads/search`). A Binance costuma
 * aninhar o anunciante em `advertiser`. Parcial — confirmar com a resposta real.
 */
export interface MarketAdRaw {
  advNo: string;
  price: string;
  asset: string;
  fiatUnit: string;
  tradeType: TradeType;
  minSingleTransAmount?: string;
  maxSingleTransAmount?: string;
  surplusAmount?: string;
  tradeMethods?: TradeMethod[];
  advertiser?: {
    userNo?: string;
    nickName?: string;
    monthOrderCount?: number;
    monthFinishRate?: number;
    [extra: string]: unknown;
  };
  [extra: string]: unknown;
}

export interface AdListReq {
  page: number;
  rows: number;
  asset?: string;
  fiatUnit?: string;
  tradeType?: TradeType;
  advStatus?: number;
  advNo?: string;
  startDate?: string;
  endDate?: string;
}

export interface PageResult<T> {
  total: number;
  data: T[];
}

/** Ordem ativa/histórica */
export interface OrderListReq {
  page: number;
  rows: number;
  asset?: string;
  tradeType?: TradeType;
  orderStatus?: number;
  orderStatusList?: number[];
  startDate?: number;
  endDate?: number;
}

export interface OrderDetail {
  orderNumber: string;
  advNo: string;
  tradeType: TradeType;
  asset: string;
  fiat: string;
  amount: string;
  totalPrice: string;
  orderStatus: number;
  [extra: string]: unknown;
}

/** Credencial do chat — GET /sapi/v1/c2c/chat/retrieveChatCredential */
export interface ChatCredential {
  chatWssUrl: string;
  listenKey: string;
  listenToken: string;
}

export interface ChatMessage {
  id: number;
  orderNo: string;
  type: string;
  content: string;
  self: boolean;
  createTime: number;
  [extra: string]: unknown;
}

/* ===================== API tipada ===================== */

export class C2CApi {
  constructor(private readonly c: BinanceC2CClient) {}

  /* ---- Anúncios ---- */

  listAds(req: AdListReq) {
    return this.c.request<PageResult<AdDetail>>({
      method: "POST",
      path: "/sapi/v1/c2c/ads/listWithPagination",
      body: req,
    });
  }

  getAdByNo(adsNo: string) {
    return this.c.request<AdDetail>({
      method: "POST",
      path: "/sapi/v1/c2c/ads/getDetailByNo",
      query: { adsNo },
    });
  }

  postAd(req: AdPublishReq) {
    return this.c.request<string>({
      method: "POST",
      path: "/sapi/v1/c2c/ads/post",
      body: req,
    });
  }

  updateAd(req: AdUpdateReq) {
    return this.c.request<boolean>({
      method: "POST",
      path: "/sapi/v1/c2c/ads/update",
      body: req,
    });
  }

  /** advStatus: ligar/desligar anúncios em lote */
  updateAdStatus(advNos: string[], advStatus: number) {
    return this.c.request<unknown>({
      method: "POST",
      path: "/sapi/v1/c2c/ads/updateStatus",
      body: { advNos, advStatus },
    });
  }

  getReferencePrice(req: {
    assets: string[];
    fiatCurrency: string;
    tradeType: TradeType;
    payType?: string;
    fromUserRole?: string;
  }) {
    return this.c.request<unknown[]>({
      method: "POST",
      path: "/sapi/v1/c2c/ads/getReferencePrice",
      body: req,
    });
  }

  /**
   * Busca anúncios de concorrentes no mercado (entrada do auto-pricing).
   * Endpoint conforme CLAUDE.md ("Binance: ads/search"). Tipos parciais —
   * confirmar a forma exata da resposta contra a conta real.
   */
  searchAds(req: {
    asset: string;
    fiat: string;
    tradeType: TradeType;
    page?: number;
    rows?: number;
    payTypes?: string[];
    transAmount?: string;
  }) {
    return this.c.request<PageResult<MarketAdRaw>>({
      method: "POST",
      path: "/sapi/v1/c2c/ads/search",
      body: req,
    });
  }

  /* ---- Ordens ---- */

  listOrders(req: OrderListReq) {
    return this.c.request<PageResult<OrderDetail>>({
      method: "POST",
      path: "/sapi/v1/c2c/orderMatch/listOrders",
      body: req,
    });
  }

  getOrderDetail(adOrderNo: string) {
    return this.c.request<OrderDetail>({
      method: "POST",
      path: "/sapi/v1/c2c/orderMatch/getUserOrderDetail",
      body: { adOrderNo },
    });
  }

  markOrderAsPaid(body: { orderNumber: string; payId?: number }) {
    return this.c.request<unknown>({
      method: "POST",
      path: "/sapi/v1/c2c/orderMatch/markOrderAsPaid",
      body,
    });
  }

  /** Libera a cripto (requer verificação 2FA conforme a conta) */
  releaseCoin(body: {
    orderNumber: string;
    confirmPaidType?: string;
    payId?: number;
    authType?: string;
    googleVerifyCode?: string;
    mobileVerifyCode?: string;
    emailVerifyCode?: string;
  }) {
    return this.c.request<unknown>({
      method: "POST",
      path: "/sapi/v1/c2c/orderMatch/releaseCoin",
      body,
    });
  }

  /* ---- Chat ---- */

  getChatCredential() {
    return this.c.request<ChatCredential>({
      method: "GET",
      path: "/sapi/v1/c2c/chat/retrieveChatCredential",
    });
  }

  getChatMessages(req: {
    orderNo: string;
    page: number;
    rows: number;
    chatMessageType?: string;
    id?: number;
    sort?: "asc" | "desc";
  }) {
    return this.c.request<PageResult<ChatMessage>>({
      method: "GET",
      path: "/sapi/v1/c2c/chat/retrieveChatMessagesWithPagination",
      query: req,
    });
  }

  markOrderMessagesAsRead(orderNo: string, userId: number) {
    return this.c.request<void>({
      method: "POST",
      path: "/sapi/v1/c2c/chat/markOrderMessagesAsRead",
      body: { orderNo, userId },
    });
  }

  /**
   * Envia uma mensagem de texto no chat de uma ordem.
   * NB: na Binance C2C o envio também pode ser feito pela conexão WSS. Este
   * endpoint REST precisa ser confirmado contra a doc/conta real (SAPI v7.4)
   * — se for só-WSS, o envio migra para o worker de chat.
   */
  sendTextMessage(body: { orderNo: string; message: string; uuid?: string }) {
    return this.c.request<unknown>({
      method: "POST",
      path: "/sapi/v1/c2c/chat/sendMessage",
      body,
    });
  }

  /* ---- Merchant status ---- */

  startBusiness() {
    return this.c.request<unknown>({ method: "POST", path: "/sapi/v1/c2c/merchant/startBusiness" });
  }
  closeBusiness() {
    return this.c.request<unknown>({ method: "POST", path: "/sapi/v1/c2c/merchant/closeBusiness" });
  }
  goOnline() {
    return this.c.request<unknown>({ method: "POST", path: "/sapi/v1/c2c/merchant/getOnline" });
  }
  goOffline() {
    return this.c.request<unknown>({ method: "POST", path: "/sapi/v1/c2c/merchant/getOffline" });
  }
}
