"""深圳 TGW 交易所报文解析模块。

所有结构体均为 pack(1) 大端编码。
"""

from __future__ import annotations

import struct

# ---------------------------------------------------------------------------
# 常量 (xs_data_type_exchange.h)
# ---------------------------------------------------------------------------
_APPL_ID = 3
_PBU = 6
_SEC_ID = 8
_SEC_ID_SRC = 4
_CLEAR_FIRM = 2
_USER_INFO = 8
_ACCOUNT = 12
_BRANCH_ID = 4
_ORDER_RES = 4
_SHARE_PROP = 2
_ORDER_ID = 16
_REJ_TEXT = 16
_BIZ_REJ_REF_ID = 10
_BIZ_REJ_TEXT = 50
_EXEC_ID = 16
_MEMBER_ID = 6
_INV_TYPE = 2
_INV_ID = 10
_INV_NAME = 120
_TRADER_CODE = 8
_SENDER_COMP_ID = 20
_TARGET_COMP_ID = 20
_PASSWORD = 16
_DEF_APPL_VER_ID = 32
_TEXT = 200
_CL_ORD_ID = 10  # CONST_EXCH_STR_LEN_CL_ORD_ID


# ---------------------------------------------------------------------------
# 辅助
# ---------------------------------------------------------------------------

def _s(b: bytes) -> str:
    """字节数组转字符串，去除尾部 \\x00 和空格。"""
    return b.split(b'\x00', 1)[0].decode('ascii', errors='replace').rstrip()


def _s_utf8(b: bytes) -> str:
    """字节数组转字符串 (可能包含中文)，去除尾部 \\x00。"""
    return b.split(b'\x00', 1)[0].decode('utf-8', errors='replace').rstrip()


def _char(data: bytes, off: int) -> str:
    return chr(data[off])


def _u8(data: bytes, off: int) -> int:
    return data[off]


def _u16(data: bytes, off: int) -> int:
    return struct.unpack_from('>H', data, off)[0]


def _u32(data: bytes, off: int) -> int:
    return struct.unpack_from('>I', data, off)[0]


def _i32(data: bytes, off: int) -> int:
    return struct.unpack_from('>i', data, off)[0]


def _u64(data: bytes, off: int) -> int:
    return struct.unpack_from('>Q', data, off)[0]


def _i64(data: bytes, off: int) -> int:
    return struct.unpack_from('>q', data, off)[0]


# ===========================================================================
# 请求: 新订单公共部分
# ===========================================================================

def _parse_new_order_req_common(data: bytes, off: int = 0) -> tuple:
    """解析 sz_tgw_new_order_req_common_t，返回 (dict, new_offset)。"""
    msg_type = _u32(data, off);                     off += 4
    body_length = _u32(data, off);                  off += 4
    appl_id = data[off:off+_APPL_ID];              off += _APPL_ID
    submitting_pbu_id = data[off:off+_PBU];        off += _PBU
    security_id = data[off:off+_SEC_ID];            off += _SEC_ID
    security_id_source = data[off:off+_SEC_ID_SRC]; off += _SEC_ID_SRC
    owner_type = _u16(data, off);                   off += 2
    clearing_firm = data[off:off+_CLEAR_FIRM];      off += _CLEAR_FIRM
    transact_time = _i64(data, off);                off += 8
    user_info = data[off:off+_USER_INFO];            off += _USER_INFO
    cl_ord_id = data[off:off+_CL_ORD_ID];           off += _CL_ORD_ID
    account_id = data[off:off+_ACCOUNT];             off += _ACCOUNT
    branch_id = data[off:off+_BRANCH_ID];            off += _BRANCH_ID
    order_res = data[off:off+_ORDER_RES];            off += _ORDER_RES
    side = _char(data, off);                        off += 1
    ord_type = _char(data, off);                    off += 1
    order_qty = _i64(data, off);                    off += 8
    price = _i64(data, off);                        off += 8
    d = {
        'msg_type': msg_type,
        'body_length': body_length,
        'appl_id': _s(appl_id),
        'submitting_pbu_id': _s(submitting_pbu_id),
        'security_id': _s(security_id),
        'security_id_source': _s(security_id_source),
        'owner_type': owner_type,
        'clearing_firm': _s(clearing_firm),
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'cl_ord_id': _s(cl_ord_id),
        'account_id': _s(account_id),
        'branch_id': _s(branch_id),
        'order_restrictions': _s(order_res),
        'side': side,
        'ord_type': ord_type,
        'order_qty': order_qty,
        'price': price,
    }
    return d, off


# ---------------------------------------------------------------------------
# 100101/104101: 现货/债券现券匹配成交新订单
# ---------------------------------------------------------------------------

def parse_cash_order_req(data: bytes) -> dict:
    """解析深交所现货/债券现券匹配成交新订单 (100101/104101)。"""
    d, off = _parse_new_order_req_common(data)
    d['stop_px'] = _i64(data, off);        off += 8
    d['min_qty'] = _i64(data, off);        off += 8
    d['max_price_levels'] = _u16(data, off); off += 2
    d['time_in_force'] = _char(data, off); off += 1
    d['cash_margin'] = _char(data, off);   off += 1
    d['check_sum'] = _u32(data, off);      off += 4
    return d


# ---------------------------------------------------------------------------
# 100201: 债券通用质押式回购新订单
# ---------------------------------------------------------------------------

def parse_pledge_repo_order_req(data: bytes) -> dict:
    """解析深交所债券通用质押式回购新订单 (100201)。"""
    d, off = _parse_new_order_req_common(data)
    d['stop_px'] = _i64(data, off);        off += 8
    d['min_qty'] = _i64(data, off);        off += 8
    d['max_price_levels'] = _u16(data, off); off += 2
    d['time_in_force'] = _char(data, off); off += 1
    d['check_sum'] = _u32(data, off);      off += 4
    return d


# ---------------------------------------------------------------------------
# 101201/101301: ETF/网上发行/配股认购新订单
# ---------------------------------------------------------------------------

def parse_etf_order_req(data: bytes) -> dict:
    """解析深交所 ETF/网上发行/配股认购新订单。"""
    d, off = _parse_new_order_req_common(data)
    d['check_sum'] = _u32(data, off);      off += 4
    return d


# 别名
parse_issue_order_req = parse_etf_order_req
parse_allot_order_req = parse_etf_order_req


# ---------------------------------------------------------------------------
# 101501: 债券转股回售新订单
# ---------------------------------------------------------------------------

def parse_bond_conv_order_req(data: bytes) -> dict:
    """解析深交所债券转股回售新订单 (101501)。"""
    d, off = _parse_new_order_req_common(data)
    d['share_property'] = _s(data[off:off+_SHARE_PROP]); off += _SHARE_PROP
    d['check_sum'] = _u32(data, off);      off += 4
    return d


# ---------------------------------------------------------------------------
# 190007: 撤单申报
# ---------------------------------------------------------------------------

def parse_cancel_order_req(data: bytes) -> dict:
    """解析深交所撤单申报 (190007)。"""
    off = 0
    msg_type = _u32(data, off);                     off += 4
    body_length = _u32(data, off);                  off += 4
    appl_id = data[off:off+_APPL_ID];              off += _APPL_ID
    submitting_pbu_id = data[off:off+_PBU];        off += _PBU
    security_id = data[off:off+_SEC_ID];            off += _SEC_ID
    security_id_source = data[off:off+_SEC_ID_SRC]; off += _SEC_ID_SRC
    owner_type = _u16(data, off);                   off += 2
    clearing_firm = data[off:off+_CLEAR_FIRM];      off += _CLEAR_FIRM
    transact_time = _i64(data, off);                off += 8
    user_info = data[off:off+_USER_INFO];            off += _USER_INFO
    cl_ord_id = data[off:off+_CL_ORD_ID];           off += _CL_ORD_ID
    orig_cl_ord_id = data[off:off+_CL_ORD_ID];      off += _CL_ORD_ID
    side = _char(data, off);                        off += 1
    order_id = data[off:off+_ORDER_ID];              off += _ORDER_ID
    order_qty = _i64(data, off);                    off += 8
    check_sum = _u32(data, off);                    off += 4

    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'appl_id': _s(appl_id),
        'submitting_pbu_id': _s(submitting_pbu_id),
        'security_id': _s(security_id),
        'security_id_source': _s(security_id_source),
        'owner_type': owner_type,
        'clearing_firm': _s(clearing_firm),
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'cl_ord_id': _s(cl_ord_id),
        'orig_cl_ord_id': _s(orig_cl_ord_id),
        'side': side,
        'order_id': _s(order_id),
        'order_qty': order_qty,
        'check_sum': check_sum,
    }


# ===========================================================================
# 回报: 撤单失败 (290008)
# ===========================================================================

def parse_cancel_fail(data: bytes) -> dict:
    """解析深交所撤单失败响应 (msg_type=290008)。"""
    off = 0
    msg_type = _u32(data, off);                     off += 4
    body_length = _u32(data, off);                  off += 4
    partition_no = _i32(data, off);                 off += 4
    report_index = _i64(data, off);                 off += 8
    appl_id = data[off:off+_APPL_ID];              off += _APPL_ID
    reporting_pbu = data[off:off+_PBU];             off += _PBU
    submitting_pbu = data[off:off+_PBU];            off += _PBU
    security_id = data[off:off+_SEC_ID];            off += _SEC_ID
    security_id_source = data[off:off+_SEC_ID_SRC]; off += _SEC_ID_SRC
    owner_type = _u16(data, off);                   off += 2
    clearing_firm = data[off:off+_CLEAR_FIRM];      off += _CLEAR_FIRM
    transact_time = _i64(data, off);                off += 8
    user_info = data[off:off+_USER_INFO];            off += _USER_INFO
    cl_ord_id = data[off:off+_CL_ORD_ID];           off += _CL_ORD_ID
    orig_cl_ord_id = data[off:off+_CL_ORD_ID];      off += _CL_ORD_ID
    side = _char(data, off);                        off += 1
    ord_status = _char(data, off);                  off += 1
    cxl_rej_reason = _u16(data, off);               off += 2
    reject_text = data[off:off+_REJ_TEXT];           off += _REJ_TEXT
    order_id = data[off:off+_ORDER_ID];              off += _ORDER_ID
    check_sum = _u32(data, off);                    off += 4

    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'partition_no': partition_no,
        'report_index': report_index,
        'appl_id': _s(appl_id),
        'reporting_pbu': _s(reporting_pbu),
        'submitting_pbu': _s(submitting_pbu),
        'security_id': _s(security_id),
        'security_id_source': _s(security_id_source),
        'owner_type': owner_type,
        'clearing_firm': _s(clearing_firm),
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'cl_ord_id': _s(cl_ord_id),
        'orig_cl_ord_id': _s(orig_cl_ord_id),
        'side': side,
        'ord_status': ord_status,
        'cxl_rej_reason': cxl_rej_reason,
        'reject_text': _s_utf8(reject_text),
        'order_id': _s(order_id),
        'check_sum': check_sum,
    }


# ===========================================================================
# 回报: 业务拒绝 (4)
# ===========================================================================

def parse_reject(data: bytes) -> dict:
    """解析深交所业务拒绝消息 (msg_type=4)。"""
    off = 0
    msg_type = _u32(data, off);                     off += 4
    body_length = _u32(data, off);                  off += 4
    appl_id = data[off:off+_APPL_ID];              off += _APPL_ID
    transact_time = _i64(data, off);                off += 8
    submitting_pbu = data[off:off+_PBU];            off += _PBU
    security_id = data[off:off+_SEC_ID];            off += _SEC_ID
    security_id_source = data[off:off+_SEC_ID_SRC]; off += _SEC_ID_SRC
    ref_seq_num = _i64(data, off);                  off += 8
    ref_msg_type = _u32(data, off);                 off += 4
    business_reject_ref_id = data[off:off+_BIZ_REJ_REF_ID]; off += _BIZ_REJ_REF_ID
    business_reject_reason = _u16(data, off);       off += 2
    business_reject_text = data[off:off+_BIZ_REJ_TEXT]; off += _BIZ_REJ_TEXT
    check_sum = _u32(data, off);                    off += 4

    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'appl_id': _s(appl_id),
        'transact_time': transact_time,
        'submitting_pbu': _s(submitting_pbu),
        'security_id': _s(security_id),
        'security_id_source': _s(security_id_source),
        'ref_seq_num': ref_seq_num,
        'ref_msg_type': ref_msg_type,
        'business_reject_ref_id': _s(business_reject_ref_id),
        'business_reject_reason': business_reject_reason,
        'business_reject_text': _s_utf8(business_reject_text),
        'check_sum': check_sum,
    }


# ===========================================================================
# 回报: 业务执行报告公共部分 (2xxx02)
# ===========================================================================

def _parse_new_cancel_cfm_common(data: bytes, off: int = 0) -> tuple:
    """解析 sz_tgw_new_cancel_cfm_common_t，返回 (dict, new_offset)。"""
    msg_type = _u32(data, off);                     off += 4
    body_length = _u32(data, off);                  off += 4
    partition_no = _i32(data, off);                 off += 4
    report_index = _i64(data, off);                 off += 8
    appl_id = data[off:off+_APPL_ID];              off += _APPL_ID
    reporting_pbu = data[off:off+_PBU];             off += _PBU
    submitting_pbu = data[off:off+_PBU];            off += _PBU
    security_id = data[off:off+_SEC_ID];            off += _SEC_ID
    security_id_source = data[off:off+_SEC_ID_SRC]; off += _SEC_ID_SRC
    owner_type = _u16(data, off);                   off += 2
    clearing_firm = data[off:off+_CLEAR_FIRM];      off += _CLEAR_FIRM
    transact_time = _i64(data, off);                off += 8
    user_info = data[off:off+_USER_INFO];            off += _USER_INFO
    order_id = data[off:off+_ORDER_ID];              off += _ORDER_ID
    cl_ord_id = data[off:off+_CL_ORD_ID];           off += _CL_ORD_ID
    orig_cl_ord_id = data[off:off+_CL_ORD_ID];      off += _CL_ORD_ID
    exec_id = data[off:off+_EXEC_ID];                off += _EXEC_ID
    exec_type = _char(data, off);                   off += 1
    ord_status = _char(data, off);                  off += 1
    cxl_rej_reason = _u16(data, off);               off += 2
    leaves_qty = _i64(data, off);                   off += 8
    cum_qty = _i64(data, off);                      off += 8
    side = _char(data, off);                        off += 1
    ord_type = _char(data, off);                    off += 1
    order_qty = _i64(data, off);                    off += 8
    price = _i64(data, off);                        off += 8
    account_id = data[off:off+_ACCOUNT];             off += _ACCOUNT
    branch_id = data[off:off+_BRANCH_ID];            off += _BRANCH_ID
    order_restrictions = data[off:off+_ORDER_RES];   off += _ORDER_RES

    d = {
        'msg_type': msg_type,
        'body_length': body_length,
        'partition_no': partition_no,
        'report_index': report_index,
        'appl_id': _s(appl_id),
        'reporting_pbu': _s(reporting_pbu),
        'submitting_pbu': _s(submitting_pbu),
        'security_id': _s(security_id),
        'security_id_source': _s(security_id_source),
        'owner_type': owner_type,
        'clearing_firm': _s(clearing_firm),
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'order_id': _s(order_id),
        'cl_ord_id': _s(cl_ord_id),
        'orig_cl_ord_id': _s(orig_cl_ord_id),
        'exec_id': _s(exec_id),
        'exec_type': exec_type,
        'ord_status': ord_status,
        'cxl_rej_reason': cxl_rej_reason,
        'leaves_qty': leaves_qty,
        'cum_qty': cum_qty,
        'side': side,
        'ord_type': ord_type,
        'order_qty': order_qty,
        'price': price,
        'account_id': _s(account_id),
        'branch_id': _s(branch_id),
        'order_restrictions': _s(order_restrictions),
    }
    return d, off


def parse_new_cancel_cfm_common(data: bytes) -> dict:
    """解析深交所业务执行报告公共部分。仅公共部分，无扩展字段。"""
    d, _ = _parse_new_cancel_cfm_common(data)
    return d


def parse_cash_new_cancel_cfm(data: bytes) -> dict:
    """解析深交所现货/债券现券匹配成交执行报告 (200102/204102)。"""
    d, off = _parse_new_cancel_cfm_common(data)
    d['stop_px'] = _i64(data, off);        off += 8
    d['min_qty'] = _i64(data, off);        off += 8
    d['max_price_levels'] = _u16(data, off); off += 2
    d['time_in_force'] = _char(data, off); off += 1
    d['cash_margin'] = _char(data, off);   off += 1
    d['check_sum'] = _u32(data, off);      off += 4
    return d


# 别名
parse_bond_match_new_cancel_cfm = parse_cash_new_cancel_cfm


def parse_pledge_repo_new_cancel_cfm(data: bytes) -> dict:
    """解析深交所债券通用质押式回购执行报告 (200202)。"""
    d, off = _parse_new_cancel_cfm_common(data)
    d['stop_px'] = _i64(data, off);        off += 8
    d['min_qty'] = _i64(data, off);        off += 8
    d['max_price_levels'] = _u16(data, off); off += 2
    d['time_in_force'] = _char(data, off); off += 1
    d['check_sum'] = _u32(data, off);      off += 4
    return d


def parse_etf_new_cancel_cfm(data: bytes) -> dict:
    """解析深交所 ETF 实时申购赎回执行报告 (201202)。"""
    d, off = _parse_new_cancel_cfm_common(data)
    d['insufficient_security_id'] = _s(data[off:off+_SEC_ID]); off += _SEC_ID
    d['no_security'] = _u32(data, off);    off += 4
    d['underlying_security_id'] = _s(data[off:off+_SEC_ID]); off += _SEC_ID
    d['underlying_security_id_source'] = _s(data[off:off+_SEC_ID_SRC]); off += _SEC_ID_SRC
    d['delivery_qty'] = _i64(data, off);   off += 8
    d['subst_cash'] = _i64(data, off);     off += 8
    d['check_sum'] = _u32(data, off);      off += 4
    return d


def parse_issue_new_cancel_cfm(data: bytes) -> dict:
    """解析深交所网上发行/债券转股/配股认购执行报告。"""
    d, off = _parse_new_cancel_cfm_common(data)
    d['check_sum'] = _u32(data, off);      off += 4
    return d


# 别名
parse_bond_conv_new_cancel_cfm = parse_issue_new_cancel_cfm
parse_allot_new_cancel_cfm = parse_issue_new_cancel_cfm


# ===========================================================================
# 回报: 成交执行报告公共部分 (2xxx15)
# ===========================================================================

def _parse_new_order_rpt_common(data: bytes, off: int = 0) -> tuple:
    """解析 sz_tgw_new_order_rpt_common_t，返回 (dict, new_offset)。"""
    msg_type = _u32(data, off);                     off += 4
    body_length = _u32(data, off);                  off += 4
    partition_no = _i32(data, off);                 off += 4
    report_index = _i64(data, off);                 off += 8
    appl_id = data[off:off+_APPL_ID];              off += _APPL_ID
    reporting_pbu = data[off:off+_PBU];             off += _PBU
    submitting_pbu = data[off:off+_PBU];            off += _PBU
    security_id = data[off:off+_SEC_ID];            off += _SEC_ID
    security_id_source = data[off:off+_SEC_ID_SRC]; off += _SEC_ID_SRC
    owner_type = _u16(data, off);                   off += 2
    clearing_firm = data[off:off+_CLEAR_FIRM];      off += _CLEAR_FIRM
    transact_time = _i64(data, off);                off += 8
    user_info = data[off:off+_USER_INFO];            off += _USER_INFO
    order_id = data[off:off+_ORDER_ID];              off += _ORDER_ID
    cl_ord_id = data[off:off+_CL_ORD_ID];           off += _CL_ORD_ID
    exec_id = data[off:off+_EXEC_ID];                off += _EXEC_ID
    exec_type = _char(data, off);                   off += 1
    ord_status = _char(data, off);                  off += 1
    last_px = _i64(data, off);                      off += 8
    last_qty = _i64(data, off);                     off += 8
    leaves_qty = _i64(data, off);                   off += 8
    cum_qty = _i64(data, off);                      off += 8
    side = _char(data, off);                        off += 1
    account_id = data[off:off+_ACCOUNT];             off += _ACCOUNT
    branch_id = data[off:off+_BRANCH_ID];            off += _BRANCH_ID

    d = {
        'msg_type': msg_type,
        'body_length': body_length,
        'partition_no': partition_no,
        'report_index': report_index,
        'appl_id': _s(appl_id),
        'reporting_pbu': _s(reporting_pbu),
        'submitting_pbu': _s(submitting_pbu),
        'security_id': _s(security_id),
        'security_id_source': _s(security_id_source),
        'owner_type': owner_type,
        'clearing_firm': _s(clearing_firm),
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'order_id': _s(order_id),
        'cl_ord_id': _s(cl_ord_id),
        'exec_id': _s(exec_id),
        'exec_type': exec_type,
        'ord_status': ord_status,
        'last_px': last_px,
        'last_qty': last_qty,
        'leaves_qty': leaves_qty,
        'cum_qty': cum_qty,
        'side': side,
        'account_id': _s(account_id),
        'branch_id': _s(branch_id),
    }
    return d, off


def parse_cash_order_rpt(data: bytes) -> dict:
    """解析深交所现货集中竞价交易成交执行报告 (200115)。"""
    d, off = _parse_new_order_rpt_common(data)
    d['cash_margin'] = _char(data, off);   off += 1
    d['check_sum'] = _u32(data, off);      off += 4
    return d


def parse_pledge_repo_order_rpt(data: bytes) -> dict:
    """解析深交所债券通用质押式回购成交执行报告 (200215)。"""
    d, off = _parse_new_order_rpt_common(data)
    d['maturity_date'] = _u32(data, off);  off += 4
    d['check_sum'] = _u32(data, off);      off += 4
    return d


def parse_bond_match_order_rpt(data: bytes) -> dict:
    """解析深交所债券现券交易成交执行报告 (204115)。"""
    d, off = _parse_new_order_rpt_common(data)
    d['cash_margin'] = _char(data, off);                                  off += 1
    d['settl_type'] = _u16(data, off);                                    off += 2
    d['settl_period'] = _u8(data, off);                                   off += 1
    d['counterparty_member_id'] = _s(data[off:off+_MEMBER_ID]);           off += _MEMBER_ID
    d['counterparty_investor_type'] = _s(data[off:off+_INV_TYPE]);        off += _INV_TYPE
    d['counterparty_investor_id'] = _s(data[off:off+_INV_ID]);            off += _INV_ID
    d['counterparty_investor_name'] = _s_utf8(data[off:off+_INV_NAME]);   off += _INV_NAME
    d['counterparty_trader_code'] = _s(data[off:off+_TRADER_CODE]);       off += _TRADER_CODE
    d['check_sum'] = _u32(data, off);                                     off += 4
    return d


# ===========================================================================
# 管理类消息
# ===========================================================================

def parse_login(data: bytes) -> dict:
    """解析深交所登录消息 (msg_type=1)。"""
    off = 0
    msg_type = _u32(data, off);           off += 4
    body_length = _u32(data, off);        off += 4
    sender_comp_id = data[off:off+_SENDER_COMP_ID]; off += _SENDER_COMP_ID
    target_comp_id = data[off:off+_TARGET_COMP_ID]; off += _TARGET_COMP_ID
    heart_bt = _i32(data, off);           off += 4
    pwd = data[off:off+_PASSWORD];        off += _PASSWORD
    app_ver = data[off:off+_DEF_APPL_VER_ID]; off += _DEF_APPL_VER_ID
    check_sum = _u32(data, off);          off += 4
    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'sender_comp_id': _s(sender_comp_id),
        'target_comp_id': _s(target_comp_id),
        'heart_bt': heart_bt,
        'pwd': _s(pwd),
        'app_ver': _s(app_ver),
        'check_sum': check_sum,
    }


def parse_logout(data: bytes) -> dict:
    """解析深交所登出消息 (msg_type=2)。"""
    off = 0
    msg_type = _u32(data, off);           off += 4
    body_length = _u32(data, off);        off += 4
    session_status = _i32(data, off);     off += 4
    text = data[off:off+_TEXT];           off += _TEXT
    check_sum = _u32(data, off);          off += 4
    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'session_status': session_status,
        'text': _s_utf8(text),
        'check_sum': check_sum,
    }


def parse_heartbeat(data: bytes) -> dict:
    """解析深交所心跳消息 (msg_type=3)。"""
    off = 0
    msg_type = _u32(data, off);           off += 4
    body_length = _u32(data, off);        off += 4
    check_sum = _u32(data, off);          off += 4
    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'check_sum': check_sum,
    }


def parse_platform_status(data: bytes) -> dict:
    """解析深交所平台状态消息 (msg_type=6)。"""
    off = 0
    msg_type = _u32(data, off);           off += 4
    body_length = _u32(data, off);        off += 4
    platform_id = _u16(data, off);        off += 2
    platform_status = _u16(data, off);    off += 2
    check_sum = _u32(data, off);          off += 4
    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'platform_id': platform_id,
        'platform_status': platform_status,
        'check_sum': check_sum,
    }


def parse_report_finished(data: bytes) -> dict:
    """解析深交所回报同步结束消息 (msg_type=7)。"""
    off = 0
    msg_type = _u32(data, off);           off += 4
    body_length = _u32(data, off);        off += 4
    partition_no = _i32(data, off);       off += 4
    report_index = _i64(data, off);       off += 8
    platform_id = _u16(data, off);        off += 2
    check_sum = _u32(data, off);          off += 4
    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'partition_no': partition_no,
        'report_index': report_index,
        'platform_id': platform_id,
        'check_sum': check_sum,
    }


def parse_platform_info(data: bytes) -> dict:
    """解析深交所平台信息消息 (msg_type=9)，含不定长分区号数组。"""
    off = 0
    msg_type = _u32(data, off);           off += 4
    body_length = _u32(data, off);        off += 4
    platform_id = _u16(data, off);        off += 2
    no_partitions = _u32(data, off);      off += 4
    partitions = []
    for _ in range(no_partitions):
        if off + 4 > len(data):
            break
        partitions.append(_i32(data, off)); off += 4
    return {
        'msg_type': msg_type,
        'body_length': body_length,
        'platform_id': platform_id,
        'no_partitions': no_partitions,
        'partition_no': partitions,
    }


# ===========================================================================
# 按 msg_type 自动分发
# ===========================================================================

PARSERS = {
    # 请求
    100101: parse_cash_order_req,
    104101: parse_cash_order_req,            # 债券现券匹配成交（结构同现货）
    100201: parse_pledge_repo_order_req,
    101201: parse_etf_order_req,
    101301: parse_issue_order_req,
    190007: parse_cancel_order_req,
    # 执行报告 (2xxx02)
    200102: parse_cash_new_cancel_cfm,
    204102: parse_bond_match_new_cancel_cfm,
    200202: parse_pledge_repo_new_cancel_cfm,
    201202: parse_etf_new_cancel_cfm,
    201302: parse_issue_new_cancel_cfm,
    # 成交回报 (2xxx15)
    200115: parse_cash_order_rpt,
    200215: parse_pledge_repo_order_rpt,
    204115: parse_bond_match_order_rpt,
    # 撤单失败
    290008: parse_cancel_fail,
    # 业务拒绝
    4:      parse_reject,
    # 管理
    1:      parse_login,
    2:      parse_logout,
    3:      parse_heartbeat,
    6:      parse_platform_status,
    7:      parse_report_finished,
    9:      parse_platform_info,
}


def parse_message(data: bytes) -> dict:
    """根据 msg_type 自动选择深圳解析函数。"""
    msg_type = _u32(data, 0)
    parser = PARSERS.get(msg_type)
    if parser is None:
        return {'msg_type': msg_type, '_error': f'unknown TGW msg_type {msg_type}'}
    return parser(data)

