import { randomUUID } from "node:crypto";
import type { SigningConfig, SignatureHeader } from "./signing";
import { signStructuredPayload } from "./signing";
import { PacificaWebSocket } from "./wsClient";
import pino from "pino";
import { RateLimiter } from "./rateLimiter";

const DEFAULT_EXPIRY_WINDOW_MS = 5_000;
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
const RPC_SLOW_THRESHOLD_MS = 5_000;

export interface CreateOrderRequest {
  symbol: string;
  side: "bid" | "ask";
  price: string | number;
  amount: string | number;
  tif: "GTC" | "IOC" | "FOK";
  reduceOnly?: boolean;
  clientOrderId: string;
}

export interface CancelOrderRequest {
  orderId?: string;
  clientOrderId?: string;
}

export interface CancelAllRequest {
  allSymbols?: boolean;
  excludeReduceOnly?: boolean;
  symbol?: string;
}

export interface CreateOrderResult {
  orderId: string;
}

export class PacificaWsOrderGateway {
  private readonly logger: pino.Logger;
  private readonly rateLimiter?: RateLimiter;

  constructor(
    private readonly ws: PacificaWebSocket,
    private readonly signing: SigningConfig,
    private readonly expiryWindowMs: number = DEFAULT_EXPIRY_WINDOW_MS,
    private readonly requestTimeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,
    logger?: pino.Logger,
    rateLimiter?: RateLimiter
  ) {
    if (!signing.apiKey || !signing.secret) {
      throw new Error("Pacifica WS gateway requires apiKey and secret");
    }
    this.logger = (logger ?? pino()).child({ name: 'PacificaWsOrderGateway' });
    this.rateLimiter = rateLimiter;
  }

  async connect(): Promise<void> {
    this.ws.connect();
    await this.ws.waitForOpen();
  }

  async createOrder(request: CreateOrderRequest): Promise<CreateOrderResult> {
    await this.rateLimiter?.acquire();
    const header = this.buildHeader("create_order");
    const payload = {
      symbol: request.symbol,
      side: request.side,
      price: this.formatNumeric(request.price),
      amount: this.formatNumeric(request.amount),
      tif: request.tif,
      reduce_only: Boolean(request.reduceOnly),
      client_order_id: request.clientOrderId
    };

    const body = this.buildSignedBody(header, payload);

    this.logger.info({ symbol: request.symbol, side: request.side, price: request.price, amount: request.amount, clientOrderId: request.clientOrderId }, 'Sending create_order via WebSocket');

    // 记录 RPC 性能指标
    const startTs = Date.now();
    const response = await this.ws.sendRpc("create_order", body, this.requestTimeoutMs);
    const elapsedMs = Date.now() - startTs;

    if (elapsedMs > RPC_SLOW_THRESHOLD_MS) {
      this.logger.warn({
        method: 'create_order',
        symbol: request.symbol,
        clientOrderId: request.clientOrderId,
        elapsedMs,
        thresholdMs: RPC_SLOW_THRESHOLD_MS
      }, 'Slow WebSocket RPC detected');
    } else {
      this.logger.debug({
        method: 'create_order',
        symbol: request.symbol,
        clientOrderId: request.clientOrderId,
        elapsedMs
      }, 'create_order RPC completed');
    }

    const orderId = extractOrderId(response);

    if (!orderId) {
      throw new Error(`Pacifica WS create_order response missing order id: ${JSON.stringify(response)}`);
    }

    this.logger.info({ orderId, clientOrderId: request.clientOrderId, elapsedMs }, 'Order created successfully via WebSocket');

    return { orderId };
  }

  async cancelOrder(request: CancelOrderRequest): Promise<void> {
    await this.rateLimiter?.acquire();
    const header = this.buildHeader("cancel_order");

    if (!request.orderId && !request.clientOrderId) {
      throw new Error("Pacifica cancel request requires orderId or clientOrderId");
    }

    const payload: Record<string, unknown> = {};
    if (request.orderId) {
      payload.order_id = request.orderId;
    }
    if (request.clientOrderId) {
      payload.client_order_id = request.clientOrderId;
    }

    const body = this.buildSignedBody(header, payload);
    await this.ws.sendRpc("cancel_order", body, this.requestTimeoutMs);
  }

  async cancelAll(request: CancelAllRequest = {}): Promise<void> {
    await this.rateLimiter?.acquire();
    const header = this.buildHeader("cancel_all_orders");
    const payload: Record<string, unknown> = {
      all_symbols: request.allSymbols ?? true,
      exclude_reduce_only: request.excludeReduceOnly ?? false
    };
    if (request.symbol) {
      payload.symbol = request.symbol;
    }
    const body = this.buildSignedBody(header, payload);
    await this.ws.sendRpc("cancel_all_orders", body, this.requestTimeoutMs);
  }

  private buildHeader(type: string): SignatureHeader {
    return {
      timestamp: Date.now(),
      expiry_window: this.expiryWindowMs,
      type
    };
  }

  private buildSignedBody(header: SignatureHeader, payload: Record<string, unknown>) {
    const { signature } = signStructuredPayload(this.signing, header, payload);
    return {
      account: this.signing.apiKey,
      signature,
      timestamp: header.timestamp,
      expiry_window: header.expiry_window,
      ...payload
    };
  }
  private formatNumeric(value: string | number): string {
    if (typeof value === "string") return value;
    if (!Number.isFinite(value)) {
      throw new Error(`Pacifica WS numeric field must be finite, received ${value}`);
    }
    return value.toString();
  }
}

function extractOrderId(raw: any): string | undefined {
  if (!raw) return undefined;
  const candidates: Array<unknown> = [
    raw.order_id,
    raw.orderId,
    raw.id,
    raw.i,
    raw.result?.order_id,
    raw.result?.orderId,
    raw.result?.id,
    raw.result?.i,
    raw.data?.order_id,
    raw.data?.orderId,
    raw.data?.id,
    raw.data?.i,
    raw.I,
    raw.result?.I,
    raw.data?.I
  ];
  for (const candidate of candidates) {
    if (candidate === undefined || candidate === null) continue;
    if (typeof candidate === "string" && candidate.trim().length > 0) {
      return candidate;
    }
    if (typeof candidate === "number" && Number.isFinite(candidate)) {
      return candidate.toString();
    }
  }
  return undefined;
}
