"""Python version of demo/xlt_trade_api_demo.

This module intentionally mirrors the C++ demo structure:
- XLTTradeDemo class (like C++ XLTTradeDemo)
- encapsulated initialize/login/subscribe/query/order methods

It targets the *latest* py_xlt_api binding, which exposes:
- XLTTradeApi / XLTTradeConfig / XLTTradeSpi
- trade-message subscription via subscribe_trade_message(...)

"""

from __future__ import annotations

import time
from dataclasses import dataclass

import py_xlt_api as xlt

import config as _demo_config
from trade_spi import _TradeSpi

# Public re-export (so `from trade_demo import default_config` works).
default_config = _demo_config.default_config

__all__ = [
    "TradeAccount",
    "XLTTradeDemo",
    "default_config",
]


@dataclass
class TradeAccount:
    client_id: int
    account_name: str
    password: str
    pub_key_base64: str = "XXXXXXXXXXXXXX"


class XLTTradeDemo:
    """Python demo class mirroring the C++ XLTTradeDemo."""

    def __init__(self, config: xlt.XLTTradeConfig | None = None):
        self._api: xlt.XLTTradeApi = xlt.XLTTradeApi.create()
        self._spi = _TradeSpi(self)
        self._session_id = 0
        self._login_success = False
        self._login_finished = False
        self._request_id = 0
        self._config = config

        ## 以下仅算法厂商、PB或代理委托场景需要关注
        self._trade_token = 0
        self._auth_success = False
        self._auth_finished = False

    # ---- C++-like simple accessors ----
    def api_version(self) -> str:
        return self._api.get_api_version()

    def trading_day(self) -> int:
        return int(self._api.get_trading_day())

    def next_request_id(self) -> int:
        self._request_id += 1
        return self._request_id

    def initialize(self) -> bool:
        if self._config is None:
            raise RuntimeError("XLTTradeDemo requires a configured XLTTradeConfig")

        ok = self._api.initialize(self._config, self._spi)
        if not ok:
            err = self._api.get_last_error()
            print("initialize failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
        else:
            print("initialize success")
        return bool(ok)

    def login(self, account: TradeAccount) -> bool:
        if self._session_id != 0:
            self._api.logout(self._session_id)

        self._session_id = 0
        self._login_success = False
        self._login_finished = False
        self._spi.login_event.clear()

        req_id = self.next_request_id()
        ok = self._api.login(req_id, account.client_id, account.account_name, account.password, account.pub_key_base64)
        if not ok:
            err = self._api.get_last_error()
            print("login failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False

        # wait for callback
        self._spi.login_event.wait(timeout=10)
        return bool(self._login_success and self._session_id != 0)

    @property
    def session_id(self) -> int:
        return int(self._session_id)

    def subscribe_trade_message(self) -> bool:
        trade_msg_type = xlt.REPORT | xlt.RAW_REPORT | xlt.ORDER_BOOK
        ok = self._api.subscribe_trade_message(
            self._session_id,
            self.next_request_id(),
            trade_msg_type,
            xlt.TradeResumeType_QUICK,
            0,
        )
        if not ok:
            err = self._api.get_last_error()
            print("subscribe_trade_message failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
        else:
            print("subscribe_trade_message request send success")
        return bool(ok)

    def query_assets(self, account_index: int = 0) -> bool:
        ok = self._api.query_assets(self._session_id, self.next_request_id(), account_index)
        if not ok:
            err = self._api.get_last_error()
            print("query_assets failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("query_assets request send success")
        return True

    def query_positions(self, account_index: int = 0) -> bool:
        ok = self._api.query_positions(self._session_id, self.next_request_id(), "", xlt.ExchangeIndex_INIT, account_index)
        if not ok:
            err = self._api.get_last_error()
            print("query_positions failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("query_positions request send success")
        return True

    def query_orders(self, account_index: int = 0) -> bool:
        p = xlt.xlt_order_query_param_t()
        p.xid = 0
        p.security_code = ""
        ok = self._api.query_orders(self._session_id, self.next_request_id(), p, account_index)
        if not ok:
            err = self._api.get_last_error()
            print("query_orders failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("query_orders request send success")
        return True

    def query_orders_by_page(self, request_count: int = 2, account_index: int = 0) -> bool:
        empty_ref = xlt.xlt_page_query_reference_t.create()
        ok = self._api.query_orders_by_page(self._session_id, self.next_request_id(), request_count, empty_ref, account_index)
        if not ok:
            err = self._api.get_last_error()
            print("query_orders_by_page failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("query_orders_by_page request send success")
        return True

    def query_trades(self, account_index: int = 0) -> bool:
        ok = self._api.query_trades(self._session_id, self.next_request_id(), 0, account_index)
        if not ok:
            err = self._api.get_last_error()
            print("query_trades failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("query_trades request send success")
        return True

    def insert_order(self) -> int:
        info = xlt.xlt_order_insert_info_t()
        info.security_code = "600010"
        info.exchange_index = xlt.ExchangeIndex_SH
        info.price = 111600
        info.quantity = 1500
        info.order_type = xlt.OrderType_LIMIT_PRICE
        info.side = xlt.Side_BUY
        info.business_type = xlt.BusinessType_CASH

        xid = int(self._api.insert_order(self._session_id, info, self._trade_token))
        if xid == 0:
            err = self._api.get_last_error()
            print("insert_order failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
        print("insert_order request send success: { xid = %d }" % xid)
        return xid

    def cancel_order(self, xid: int) -> int:
        req = xlt.xlt_cancel_order_info_t()
        req.order_sequence = 0
        req.origin_xid = int(xid)
        cxid = int(self._api.cancel_order(self._session_id, req, self._trade_token))
        if cxid == 0:
            err = self._api.get_last_error()
            print("cancel_order failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
        print("cancel_order request send success: { cancel_xid = %d }" % cxid)
        return cxid

    def submit_counter_fund_transfer_request(self, side: int, amount: int, trade_token: int = 0) -> bool:
        ok, biz_no = self._api.submit_counter_fund_transfer_request(
            self._session_id,
            self.next_request_id(),
            int(side),
            int(amount),
            int(trade_token),
        )
        if not ok:
            err = self._api.get_last_error()
            print("submit_counter_fund_transfer_request failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("submit_counter_fund_transfer_request send success: { biz_no = %s }" % biz_no)
        return True

    def submit_cluster_fund_transfer_request(self, amount: int, trade_token: int = 0) -> bool:
        ok, biz_no = self._api.submit_cluster_fund_transfer_request(
            self._session_id,
            self.next_request_id(),
            int(amount),
            int(trade_token),
        )
        if not ok:
            err = self._api.get_last_error()
            print("submit_cluster_fund_transfer_request failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("submit_cluster_fund_transfer_request send success: { biz_no = %s } " % biz_no)
        return True

    def rebuild_trade_message(self, start_sequence: int, end_sequence: int, account_index: int = 0) -> bool:
        trade_msg_type = xlt.REPORT | xlt.RAW_REPORT | xlt.ORDER_BOOK
        ok = self._api.rebuild_trade_message(self._session_id, self.next_request_id(), trade_msg_type, int(start_sequence), int(end_sequence), account_index)
        if not ok:
            err = self._api.get_last_error()
            print("rebuild_trade_message failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("rebuild_trade_message request send success: { start_sequence = %d, end_sequence = %d }"
              % (int(start_sequence), int(end_sequence)))
        return True

    def query_init_assets(self, account_index: int = 0) -> bool:
        ok = self._api.query_init_assets(self._session_id, self.next_request_id(), int(account_index))
        if not ok:
            err = self._api.get_last_error()
            print("query_init_assets failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("query_init_assets request send success")
        return True

    def query_init_positions(self, account_index: int = 0) -> bool:
        ok = self._api.query_init_positions(self._session_id, self.next_request_id(), "", xlt.ExchangeIndex_INIT, account_index)
        if not ok:
            err = self._api.get_last_error()
            print("query_init_positions failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("query_init_positions request send success")
        return True

    def query_trade_account_info(self, account_index: int = 0) -> bool:
        ok = self._api.query_trade_account_info(self._session_id, self.next_request_id(), account_index)
        if not ok:
            err = self._api.get_last_error()
            print("query_trade_account_info failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("query_trade_account_info request send success")
        return True

    def print_init_assets(self) -> None:
        asset = self._api.get_init_assets(self._session_id)
        if not asset:
            print("get_init_assets: null")
            return
        print("get_init_assets: { account_index = %d, initial_amount = %d, fund_balance = %d }"
              % (int(asset.account_index), int(asset.initial_amount), int(asset.fund_balance)))

    def print_init_positions(self) -> None:
        init_positions = self._api.get_init_positions(self._session_id)
        if not init_positions:
            print("get_init_positions: null")
            return
        data_count = init_positions.data_count()
        print("get_init_positions: { data_count = %d }" % int(data_count))
        for i in range(int(data_count)):
            p = init_positions.get(i)
            print("  [%d]: { account_index = %d, security_code = %s, exchange_index = %d, initial_quantity = %d, total_quantity = %d, frozen_quantity = %d }"
                  % (i, int(p.account_index), p.security_code, int(p.exchange_index),
                     int(p.initial_quantity), int(p.total_quantity), int(p.frozen_quantity)))

    def get_account_index(self) -> int:
        account_index = int(self._api.get_account_index(self._session_id, self._trade_token))
        print("get_account_index: { account_index = %d }" % account_index)
        return account_index

    def auth_trade(self, account_name: str, password: str, pub_key_base64: str = "XXXXXXXXXXXXXX") -> bool:
        if self._trade_token != 0:
            self._api.deauth_trade(self._session_id, self.next_request_id(), int(self._trade_token))

        self._trade_token = 0
        self._auth_success = False
        self._auth_finished = False
        self._spi.auth_event.clear()

        info = xlt.xlt_auth_trade_request_info_t() if hasattr(xlt, 'xlt_auth_trade_request_info_t') else None
        ok = self._api.auth_trade(self._session_id, self.next_request_id(), account_name, password, pub_key_base64, info)
        if not ok:
            err = self._api.get_last_error()
            print("auth_trade failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("auth_trade request send success")

        self._spi.auth_event.wait(timeout=10)
        return bool(self._auth_success and self._trade_token != 0)

    def deauth_trade(self, trade_token: int) -> bool:
        ok = self._api.deauth_trade(self._session_id, self.next_request_id(), int(trade_token))
        if not ok:
            err = self._api.get_last_error()
            print("deauth_trade failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("deauth_trade request send success")
        return True

    def resend_order_request(self, xid: int) -> bool:
        ok = self._api.resend_order_request(self._session_id, int(xid))
        if not ok:
            err = self._api.get_last_error()
            print("resend_order_request failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("resend_order_request send success: { xid = %d }" % int(xid))
        return True

    def dump_api_performance(self) -> bool:
        ok = self._api.dump_api_performance_info()
        if not ok:
            err = self._api.get_last_error()
            print("dump_api_performance failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(err.error_code_type), int(err.error_code), err.error_msg))
            return False
        print("dump_api_performance success")
        return True

    def shutdown(self):
        """Gracefully stop the API so C++ background threads exit
        *before* the Python interpreter starts finalizing.

        Calls ``os._exit(0)`` at the end to skip Python finalization
        entirely – prevents the ``take_gil`` → ``pthread_exit`` →
        ``_Unwind_ForcedUnwind`` → ``abort`` crash in CPython 3.13.
        """
        import os, sys

        self._spi._shutting_down = True

        if self._session_id != 0:
            print("shutdown: logout ...")
            self._api.logout(self._session_id)
            self._session_id = 0
            time.sleep(0.5)

        print("shutdown: done")
        sys.stdout.flush()
        sys.stderr.flush()
        os._exit(0)

    def run_forever(self):
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("\nCtrl+C received, shutting down ...")
            self.shutdown()

