"""Python version of demo/xlt_privilege_api_demo.

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

It targets the `py_xlt_api` binding which exposes:
- XLTPrivilege / XLTPrivilegeConfig / XLTPrivilegeSpi

"""

from __future__ import annotations

import time
from dataclasses import dataclass

import py_xlt_api as xlt

import config as _demo_config
from privilege_spi import _PrivilegeSpi

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

__all__ = [
    "PrivilegeAccount",
    "XLTPrivilegeDemo",
    "default_config",
]


@dataclass
class PrivilegeAccount:
    account_name: str
    password: str
    pub_key_base64: str = "XXXXXXXXXXXXXX"


class XLTPrivilegeDemo:
    def __init__(self):
        self._api: xlt.XLTPrivilege = xlt.XLTPrivilege.create()
        self._spi = _PrivilegeSpi(self)

        self._session_id: int = 0
        self._login_success: bool = False
        self._login_finished: bool = False
        self._request_id: int = 0

        self._trade_token = 0
        self._auth_success = False
        self._auth_finished = False

    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, cfg: xlt.XLTPrivilegeConfig | None = None) -> bool:
        if cfg is None:
            cfg = default_config()

        ok = self._api.initialize(cfg, 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))
            return False

        print("initialize success")
        return True

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


    def login(self, account: PrivilegeAccount) -> bool:
        if self._session_id != 0:
            try:
                self._api.logout(self._session_id)
            except Exception:
                pass

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

        req_id = self.next_request_id()

        # 管理接口登录不需要 client_id（与 C++ demo 一致）
        ok = self._api.login(req_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 on_login
        self._spi.login_event.wait(timeout=10.0)
        if not self._login_finished:
            print("login timeout")
            return False
        return bool(self._login_success)


    def subscribe_trade_message(self) -> bool:
        trade_msg_type = xlt.REPORT | xlt.RAW_REPORT | xlt.ORDER_BOOK
        # trade_msg_type = xlt.REPORT

        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))
            return False

        print("subscribe_trade_message request send success")
        return True

    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:
        param = xlt.xlt_order_query_param_t()
        param.xid = 0
        ok = self._api.query_orders(self._session_id, self.next_request_id(), param, 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 = 500, account_index: int = 0) -> bool:
        query_ref = xlt.xlt_page_query_reference_t()
        ok = self._api.query_orders_by_page(self._session_id, self.next_request_id(), int(request_count), query_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 query_init_assets(self, account_index: int = 0) -> bool:
        ok = self._api.query_init_assets(self._session_id, self.next_request_id(), 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 query_brief_assets(self, account_index: int = 0) -> bool:
        ok = self._api.query_brief_assets(self._session_id, self.next_request_id(), int(account_index))
        if not ok:
            err = self._api.get_last_error()
            print("query_brief_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_brief_assets request send success")
        return True

    def query_brief_positions(self, account_index: int = 0) -> bool:
        ok = self._api.query_brief_positions(self._session_id, self.next_request_id(), "", xlt.ExchangeIndex_INIT, int(account_index))
        if not ok:
            err = self._api.get_last_error()
            print("query_brief_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_brief_positions request send success")
        return True

    def dump_final_file(self) -> bool:
        ok = self._api.dump_final_file(self._session_id, self.next_request_id())
        if not ok:
            err = self._api.get_last_error()
            print("dump_final_file 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_final_file request send success")
        return True

    def unbind_user_tgw(self, account_index: int) -> bool:
        ok = self._api.unbind_user_tgw(self._session_id, self.next_request_id(), int(account_index))
        if not ok:
            err = self._api.get_last_error()
            print("unbind_user_tgw 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("unbind_user_tgw request send success")
        return True

    # --- Fund transfer (counter) ---

    def submit_counter_fund_transfer_request(self, biz_no: str, account_index: int, side: int, amount: int) -> bool:
        req = xlt.xlt_counter_fund_transfer_request_t()
        req.biz_no = biz_no
        req.account_index = int(account_index)
        req.source = 0  # 0: 管理系统发起
        req.side = int(side)
        req.amount = int(amount)
        req.request_time = 0

        ok = self._api.submit_counter_fund_transfer_request(self._session_id, self.next_request_id(), req)
        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")
        return True

    def submit_counter_fund_transfer_result(
        self,
        source: int,
        biz_no: str,
        account_index: int,
        side: int,
        amount: int,
        status: int,
        error_msg: str = "",
    ) -> bool:
        r = xlt.xlt_counter_fund_transfer_result_t()
        r.biz_no = biz_no
        r.account_index = int(account_index)
        r.source = int(source)
        r.side = int(side)
        r.amount = int(amount)
        r.request_time = 0
        r.result_time = 0

        # C++ demo: if status==2 and error_msg not empty => set error_code_type=801 and error_msg.
        if int(status) == 2 and error_msg:
            r.error_info.error_code_type = 801
            r.error_info.error_code = 0
            r.error_info.error_msg = error_msg
        else:
            r.error_info.error_code_type = 0
            r.error_info.error_code = 0
            r.error_info.error_msg = ""

        r.status = int(status)

        ok = self._api.submit_counter_fund_transfer_result(self._session_id, self.next_request_id(), r)
        if not ok:
            err = self._api.get_last_error()
            print("submit_counter_fund_transfer_result 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_result send success")
        return True

    # --- Fund transfer (cluster) ---

    def submit_cluster_fund_transfer_request(self, biz_no: str, account_index: int, amount: int) -> bool:
        req = xlt.xlt_cluster_fund_transfer_request_t()
        req.biz_no = biz_no or ""
        req.account_index = int(account_index)
        req.source = 0
        req.amount = int(amount)
        req.request_time = 0

        ok = self._api.submit_cluster_fund_transfer_request(self._session_id, self.next_request_id(), req)
        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")
        return True

    def submit_cluster_fund_transfer_result(
        self,
        source: int,
        biz_no: str,
        account_index: int,
        amount: int,
        status: int,
        error_msg: str = "",
    ) -> bool:
        r = xlt.xlt_cluster_fund_transfer_result_t()
        r.biz_no = biz_no or ""
        r.account_index = int(account_index)
        r.source = int(source)
        r.amount = int(amount)
        r.request_time = 0
        r.result_time = 0
        r.status = int(status)

        if error_msg:
            r.error_info.error_code_type = 801
            r.error_info.error_code = 0
            r.error_info.error_msg = error_msg
        else:
            r.error_info.error_code_type = 0
            r.error_info.error_code = 0
            r.error_info.error_msg = ""

        ok = self._api.submit_cluster_fund_transfer_result(self._session_id, self.next_request_id(), r)
        if not ok:
            err = self._api.get_last_error()
            print("submit_cluster_fund_transfer_result 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_result send success")
        return True

    # --- Fund transfer exec ---

    def insert_fund_transfer_exec(
        self,
        biz_no: str,
        account_index: int,
        side: int,
        amount: int,
        check_flag: int,
        exec_type: int,
    ) -> bool:
        req = xlt.xlt_fund_transfer_exec_request_t()
        req.biz_no = biz_no
        req.account_index = int(account_index)
        req.side = int(side)
        req.amount = int(amount)
        req.check_flag = int(check_flag)
        req.exec_type = int(exec_type)

        ok = self._api.insert_fund_transfer_exec(self._session_id, self.next_request_id(), req)
        if not ok:
            err = self._api.get_last_error()
            print("insert_fund_transfer_exec 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("insert_fund_transfer_exec request send 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()

