"""上海 TDGW 交易所报文解析模块。

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

from __future__ import annotations

import struct

# ---------------------------------------------------------------------------
# 常量 (xs_data_type_exchange.h)
# ---------------------------------------------------------------------------
_PBU = 8
_SECURITY_ID = 12
_ACCOUNT = 13
_CREDIT_TAG = 2
_CLEARING_FIRM = 8
_BRANCH_ID = 8
_USER_INFO = 32
_ORD_CNFM_ID = 16
_TRD_CNFM_ID = 16
_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 _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_msg_head(data: bytes, off: int = 0) -> tuple:
    """解析 sh_tdgw_msg_head_t，返回 (dict, new_offset)。"""
    msg_type = _u32(data, off);       off += 4
    msg_seq_num = _u64(data, off);    off += 8
    msg_body_len = _u32(data, off);   off += 4
    return {
        'msg_type': msg_type,
        'msg_seq_num': msg_seq_num,
        'msg_body_len': msg_body_len,
    }, off


# ---------------------------------------------------------------------------
# 58: sh_tdgw_new_order_req_t - 新订单申报请求
# ---------------------------------------------------------------------------

def parse_new_order_req(data: bytes) -> dict:
    """解析上海新订单申报请求 (msg_type=58)。"""
    off = 0
    msg_type = _u32(data, off);         off += 4
    msg_seq_num = _u64(data, off);      off += 8
    msg_body_len = _u32(data, off);     off += 4
    biz_id = _u32(data, off);           off += 4
    biz_pbu = data[off:off+_PBU];      off += _PBU
    cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    security_id = data[off:off+_SECURITY_ID]; off += _SECURITY_ID
    account = data[off:off+_ACCOUNT];   off += _ACCOUNT
    owner_type = _u8(data, off);        off += 1
    side = _char(data, off);            off += 1
    price = _i64(data, off);            off += 8
    order_qty = _i64(data, off);        off += 8
    ord_type = _char(data, off);        off += 1
    time_in_force = _char(data, off);   off += 1
    transact_time = _u64(data, off);    off += 8
    credit_tag = data[off:off+_CREDIT_TAG]; off += _CREDIT_TAG
    clearing_firm = data[off:off+_CLEARING_FIRM]; off += _CLEARING_FIRM
    branch_id = data[off:off+_BRANCH_ID]; off += _BRANCH_ID
    user_info = data[off:off+_USER_INFO]; off += _USER_INFO
    check_sum = _u32(data, off);        off += 4

    return {
        'msg_type': msg_type,
        'msg_seq_num': msg_seq_num,
        'msg_body_len': msg_body_len,
        'biz_id': biz_id,
        'biz_pbu': _s(biz_pbu),
        'cl_ord_id': _s(cl_ord_id),
        'security_id': _s(security_id),
        'account': _s(account),
        'owner_type': owner_type,
        'side': side,
        'price': price,
        'order_qty': order_qty,
        'ord_type': ord_type,
        'time_in_force': time_in_force,
        'transact_time': transact_time,
        'credit_tag': _s(credit_tag),
        'clearing_firm': _s(clearing_firm),
        'branch_id': _s(branch_id),
        'user_info': _s(user_info),
        'check_sum': check_sum,
    }


# ---------------------------------------------------------------------------
# 61: sh_tdgw_cancel_order_req_t - 撤单申报请求
# ---------------------------------------------------------------------------

def parse_cancel_order_req(data: bytes) -> dict:
    """解析上海撤单申报请求 (msg_type=61)。"""
    off = 0
    msg_type = _u32(data, off);         off += 4
    msg_seq_num = _u64(data, off);      off += 8
    msg_body_len = _u32(data, off);     off += 4
    biz_id = _u32(data, off);           off += 4
    biz_pbu = data[off:off+_PBU];      off += _PBU
    cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    security_id = data[off:off+_SECURITY_ID]; off += _SECURITY_ID
    account = data[off:off+_ACCOUNT];   off += _ACCOUNT
    owner_type = _u8(data, off);        off += 1
    side = _char(data, off);            off += 1
    orig_cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    transact_time = _u64(data, off);    off += 8
    branch_id = data[off:off+_BRANCH_ID]; off += _BRANCH_ID
    user_info = data[off:off+_USER_INFO]; off += _USER_INFO
    check_sum = _u32(data, off);        off += 4

    return {
        'msg_type': msg_type,
        'msg_seq_num': msg_seq_num,
        'msg_body_len': msg_body_len,
        'biz_id': biz_id,
        'biz_pbu': _s(biz_pbu),
        'cl_ord_id': _s(cl_ord_id),
        'security_id': _s(security_id),
        'account': _s(account),
        'owner_type': owner_type,
        'side': side,
        'orig_cl_ord_id': _s(orig_cl_ord_id),
        'transact_time': transact_time,
        'branch_id': _s(branch_id),
        'user_info': _s(user_info),
        'check_sum': check_sum,
    }


# ---------------------------------------------------------------------------
# 204: sh_tdgw_reject_t - 申报拒绝
# ---------------------------------------------------------------------------

def parse_reject(data: bytes) -> dict:
    """解析上海申报拒绝 (msg_type=204)。"""
    off = 0
    msg_type = _u32(data, off);         off += 4
    msg_seq_num = _u64(data, off);      off += 8
    msg_body_len = _u32(data, off);     off += 4
    biz_id = _u32(data, off);           off += 4
    biz_pbu = data[off:off+_PBU];      off += _PBU
    cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    security_id = data[off:off+_SECURITY_ID]; off += _SECURITY_ID
    ord_rej_reason = _u32(data, off);   off += 4
    trade_date = _i32(data, off);       off += 4
    transact_time = _u64(data, off);    off += 8
    user_info = data[off:off+_USER_INFO]; off += _USER_INFO
    check_sum = _u32(data, off);        off += 4

    return {
        'msg_type': msg_type,
        'msg_seq_num': msg_seq_num,
        'msg_body_len': msg_body_len,
        'biz_id': biz_id,
        'biz_pbu': _s(biz_pbu),
        'cl_ord_id': _s(cl_ord_id),
        'security_id': _s(security_id),
        'ord_rej_reason': ord_rej_reason,
        'trade_date': trade_date,
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'check_sum': check_sum,
    }


# ---------------------------------------------------------------------------
# 32: sh_tdgw_new_cancel_confirm_t - 申报响应/撤单成功响应
# ---------------------------------------------------------------------------

def parse_new_cancel_confirm(data: bytes) -> dict:
    """解析上海新订单申报响应/撤单成功响应 (msg_type=32)。"""
    off = 0
    msg_type = _u32(data, off);         off += 4
    msg_seq_num = _u64(data, off);      off += 8
    msg_body_len = _u32(data, off);     off += 4
    pbu = data[off:off+_PBU];          off += _PBU
    set_id = _u32(data, off);           off += 4
    report_index = _u64(data, off);     off += 8
    biz_id = _u32(data, off);           off += 4
    exec_type = _char(data, off);       off += 1
    biz_pbu = data[off:off+_PBU];      off += _PBU
    cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    security_id = data[off:off+_SECURITY_ID]; off += _SECURITY_ID
    account = data[off:off+_ACCOUNT];   off += _ACCOUNT
    owner_type = _u8(data, off);        off += 1
    side = _char(data, off);            off += 1
    price = _i64(data, off);            off += 8
    order_qty = _i64(data, off);        off += 8
    leaves_qty = _i64(data, off);       off += 8
    cxl_qty = _i64(data, off);          off += 8
    ord_type = _char(data, off);        off += 1
    time_in_force = _char(data, off);   off += 1
    ord_status = _char(data, off);      off += 1
    credit_tag = data[off:off+_CREDIT_TAG]; off += _CREDIT_TAG
    orig_cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    clearing_firm = data[off:off+_CLEARING_FIRM]; off += _CLEARING_FIRM
    branch_id = data[off:off+_BRANCH_ID]; off += _BRANCH_ID
    ord_rej_reason = _u32(data, off);   off += 4
    ord_cnfm_id = data[off:off+_ORD_CNFM_ID]; off += _ORD_CNFM_ID
    orig_ord_cnfm_id = data[off:off+_ORD_CNFM_ID]; off += _ORD_CNFM_ID
    trade_date = _i32(data, off);       off += 4
    transact_time = _u64(data, off);    off += 8
    user_info = data[off:off+_USER_INFO]; off += _USER_INFO
    check_sum = _u32(data, off);        off += 4

    return {
        'msg_type': msg_type,
        'msg_seq_num': msg_seq_num,
        'msg_body_len': msg_body_len,
        'pbu': _s(pbu),
        'set_id': set_id,
        'report_index': report_index,
        'biz_id': biz_id,
        'exec_type': exec_type,
        'biz_pbu': _s(biz_pbu),
        'cl_ord_id': _s(cl_ord_id),
        'security_id': _s(security_id),
        'account': _s(account),
        'owner_type': owner_type,
        'side': side,
        'price': price,
        'order_qty': order_qty,
        'leaves_qty': leaves_qty,
        'cxl_qty': cxl_qty,
        'ord_type': ord_type,
        'time_in_force': time_in_force,
        'ord_status': ord_status,
        'credit_tag': _s(credit_tag),
        'orig_cl_ord_id': _s(orig_cl_ord_id),
        'clearing_firm': _s(clearing_firm),
        'branch_id': _s(branch_id),
        'ord_rej_reason': ord_rej_reason,
        'ord_cnfm_id': _s(ord_cnfm_id),
        'orig_ord_cnfm_id': _s(orig_ord_cnfm_id),
        'trade_date': trade_date,
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'check_sum': check_sum,
    }


# ---------------------------------------------------------------------------
# 59: sh_tdgw_cancel_fail_t - 撤单失败响应
# ---------------------------------------------------------------------------

def parse_cancel_fail(data: bytes) -> dict:
    """解析上海撤单失败响应 (msg_type=59)。"""
    off = 0
    msg_type = _u32(data, off);         off += 4
    msg_seq_num = _u64(data, off);      off += 8
    msg_body_len = _u32(data, off);     off += 4
    pbu = data[off:off+_PBU];          off += _PBU
    set_id = _u32(data, off);           off += 4
    report_index = _u64(data, off);     off += 8
    biz_id = _u32(data, off);           off += 4
    biz_pbu = data[off:off+_PBU];      off += _PBU
    cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    security_id = data[off:off+_SECURITY_ID]; off += _SECURITY_ID
    orig_cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    branch_id = data[off:off+_BRANCH_ID]; off += _BRANCH_ID
    cxl_rej_reason = _u32(data, off);   off += 4
    trade_date = _i32(data, off);       off += 4
    transact_time = _u64(data, off);    off += 8
    user_info = data[off:off+_USER_INFO]; off += _USER_INFO
    check_sum = _u32(data, off);        off += 4

    return {
        'msg_type': msg_type,
        'msg_seq_num': msg_seq_num,
        'msg_body_len': msg_body_len,
        'pbu': _s(pbu),
        'set_id': set_id,
        'report_index': report_index,
        'biz_id': biz_id,
        'biz_pbu': _s(biz_pbu),
        'cl_ord_id': _s(cl_ord_id),
        'security_id': _s(security_id),
        'orig_cl_ord_id': _s(orig_cl_ord_id),
        'branch_id': _s(branch_id),
        'cxl_rej_reason': cxl_rej_reason,
        'trade_date': trade_date,
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'check_sum': check_sum,
    }


# ---------------------------------------------------------------------------
# 103: sh_tdgw_new_order_report_t - 成交回报
# ---------------------------------------------------------------------------

def parse_new_order_report(data: bytes) -> dict:
    """解析上海成交回报 (msg_type=103)。"""
    off = 0
    msg_type = _u32(data, off);         off += 4
    msg_seq_num = _u64(data, off);      off += 8
    msg_body_len = _u32(data, off);     off += 4
    pbu = data[off:off+_PBU];          off += _PBU
    set_id = _u32(data, off);           off += 4
    report_index = _u64(data, off);     off += 8
    biz_id = _u32(data, off);           off += 4
    exec_type = _char(data, off);       off += 1
    biz_pbu = data[off:off+_PBU];      off += _PBU
    cl_ord_id = data[off:off+_CL_ORD_ID]; off += _CL_ORD_ID
    security_id = data[off:off+_SECURITY_ID]; off += _SECURITY_ID
    account = data[off:off+_ACCOUNT];   off += _ACCOUNT
    owner_type = _u8(data, off);        off += 1
    order_entry_time = _u64(data, off); off += 8
    last_px = _i64(data, off);          off += 8
    last_qty = _i64(data, off);         off += 8
    grosstrade_amt = _i64(data, off);   off += 8
    side = _char(data, off);            off += 1
    order_qty = _i64(data, off);        off += 8
    leaves_qty = _i64(data, off);       off += 8
    ord_status = _char(data, off);      off += 1
    credit_tag = data[off:off+_CREDIT_TAG]; off += _CREDIT_TAG
    clearing_firm = data[off:off+_CLEARING_FIRM]; off += _CLEARING_FIRM
    branch_id = data[off:off+_BRANCH_ID]; off += _BRANCH_ID
    trd_cnfm_id = data[off:off+_TRD_CNFM_ID]; off += _TRD_CNFM_ID
    ord_cnfm_id = data[off:off+_ORD_CNFM_ID]; off += _ORD_CNFM_ID
    trade_date = _i32(data, off);       off += 4
    transact_time = _u64(data, off);    off += 8
    user_info = data[off:off+_USER_INFO]; off += _USER_INFO
    check_sum = _u32(data, off);        off += 4

    return {
        'msg_type': msg_type,
        'msg_seq_num': msg_seq_num,
        'msg_body_len': msg_body_len,
        'pbu': _s(pbu),
        'set_id': set_id,
        'report_index': report_index,
        'biz_id': biz_id,
        'exec_type': exec_type,
        'biz_pbu': _s(biz_pbu),
        'cl_ord_id': _s(cl_ord_id),
        'security_id': _s(security_id),
        'account': _s(account),
        'owner_type': owner_type,
        'order_entry_time': order_entry_time,
        'last_px': last_px,
        'last_qty': last_qty,
        'grosstrade_amt': grosstrade_amt,
        'side': side,
        'order_qty': order_qty,
        'leaves_qty': leaves_qty,
        'ord_status': ord_status,
        'credit_tag': _s(credit_tag),
        'clearing_firm': _s(clearing_firm),
        'branch_id': _s(branch_id),
        'trd_cnfm_id': _s(trd_cnfm_id),
        'ord_cnfm_id': _s(ord_cnfm_id),
        'trade_date': trade_date,
        'transact_time': transact_time,
        'user_info': _s(user_info),
        'check_sum': check_sum,
    }


# ---------------------------------------------------------------------------
# 管理类消息
# ---------------------------------------------------------------------------

def parse_logon(data: bytes) -> dict:
    """解析上海登录 (tdgw_msg_logon_t, msg_type=40)。"""
    head, off = _parse_msg_head(data)
    sender_comp_id = data[off:off+32]; off += 32
    target_comp_id = data[off:off+32]; off += 32
    heartbeat_interval = _u16(data, off); off += 2
    prot_version = data[off:off+8]; off += 8
    trade_date = _u32(data, off); off += 4
    qsize = _u32(data, off); off += 4
    check_sum = _u32(data, off); off += 4
    return {**head,
        'sender_comp_id': _s(sender_comp_id),
        'target_comp_id': _s(target_comp_id),
        'heartbeat_interval': heartbeat_interval,
        'prot_version': _s(prot_version),
        'trade_date': trade_date,
        'qsize': qsize,
        'check_sum': check_sum,
    }


def parse_logout(data: bytes) -> dict:
    """解析上海登出 (tdgw_msg_logout_t, msg_type=41)。"""
    head, off = _parse_msg_head(data)
    session_status = _u32(data, off); off += 4
    text = data[off:off+64]; off += 64
    check_sum = _u32(data, off); off += 4
    return {**head,
        'session_status': session_status,
        'text': _s(text),
        'check_sum': check_sum,
    }


def parse_heartbeat(data: bytes) -> dict:
    """解析上海心跳 (tdgw_msg_heartbeat_t, msg_type=33)。"""
    head, off = _parse_msg_head(data)
    check_sum = _u32(data, off); off += 4
    return {**head, 'check_sum': check_sum}


def parse_platform_state(data: bytes) -> dict:
    """解析上海平台状态 (tdgw_msg_platform_state_t, msg_type=209)。"""
    head, off = _parse_msg_head(data)
    platform_id = _u16(data, off); off += 2
    platform_state = _u16(data, off); off += 2
    check_sum = _u32(data, off); off += 4
    return {**head,
        'platform_id': platform_id,
        'platform_state': platform_state,
        'check_sum': check_sum,
    }


def parse_report_finished(data: bytes) -> dict:
    """解析上海执行报告结束 (tdgw_msg_report_finished_t, msg_type=210)。"""
    head, off = _parse_msg_head(data)
    pbu = data[off:off+_PBU]; off += _PBU
    partition_no = _u32(data, off); off += 4
    report_index = _u64(data, off); off += 8
    check_sum = _u32(data, off); off += 4
    return {**head,
        'pbu': _s(pbu),
        'partition_no': partition_no,
        'report_index': report_index,
        'check_sum': check_sum,
    }


# ---------------------------------------------------------------------------
# 按 msg_type 自动分发
# ---------------------------------------------------------------------------

PARSERS = {
    58:  parse_new_order_req,        # 新订单申报
    61:  parse_cancel_order_req,     # 撤单申报
    204: parse_reject,               # 申报拒绝
    32:  parse_new_cancel_confirm,   # 申报响应 / 撤单成功
    59:  parse_cancel_fail,          # 撤单失败
    103: parse_new_order_report,     # 成交回报
    40:  parse_logon,
    41:  parse_logout,
    33:  parse_heartbeat,
    209: parse_platform_state,
    210: parse_report_finished,
}


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 TDGW msg_type {msg_type}'}
    return parser(data)

