import asyncio
import json
import time
import hmac
import base64
import hashlib
import websockets
import os

# 顯示合併後的所有持倉資料
def clear_screen():
    """清除終端屏幕"""
    if os.name == 'nt':  # Windows
        os.system('cls')
    else:  # Linux/Mac
        os.system('clear')

# OKX API Key 資訊
# API_KEY = "0a943a02-e16f-474d-b6b4-431c4b865639"
# SECRET_KEY = "45ACC4249524C4D5AED30DB4EE75CBDD"

API_KEY = "4a4d18ed-f671-4a26-92cb-c34388407d41"
SECRET_KEY = "A209BB83BEBC32727E3E807E43310554"
PASSPHRASE = "@89687828cC"

# 生成 OKX WebSocket 認證簽名
def generate_signature(timestamp, method="GET", request_path="/users/self/verify"):
    message = f"{timestamp}{method}{request_path}"
    mac = hmac.new(SECRET_KEY.encode(), message.encode(), hashlib.sha256)
    return base64.b64encode(mac.digest()).decode()

def parse_positions(data, positions_cache):
    try:
        msg = json.loads(data)
        if "data" in msg:
            positions = msg["data"]
            if not isinstance(positions, list) or len(positions) == 0:
                print("❌ 沒有有效的持倉數據")
                return  # 如果持倉數據不正確則返回

            # 合併新收到的持倉資料到緩存中
            for pos in positions:
                pos_id = pos.get('instId')  # 使用合約ID作為唯一標識符
                if pos_id and pos_id not in positions_cache:
                    positions_cache[pos_id] = pos

            # 顯示合併後的所有持倉資料
            print("\r", end="")  # 清空之前的輸出
            # 清除屏幕並顯示合併後的所有持倉資料
            clear_screen()
            # 顯示表格欄位標題
            print(f"\n{'序號':<6}{'合約':<20}{'持倉類型':<12}{'持倉方向':<12}{'持倉數量':<12}{'開倉均價':<12}{'槓桿倍數':<10}{'保證金模式':<14}{'保證金比率':<14}{'清算價格':<12}{'未實現盈虧':<18}{'已實現盈虧':<18}")

            # 顯示合併後所有持倉
            for idx, pos in enumerate(positions_cache.values(), start=1):
                direction = '多單 (Long)' if float(pos.get('pos', '0')) > 0 else '空單 (Short)'
                print(f"{idx:<6}{pos.get('instId', 'N/A'):<20}{pos.get('instType', 'N/A'):<12}"
                      f"{direction:<12}{pos.get('pos', 'N/A'):<12}{pos.get('avgPx', 'N/A'):<12}"
                      f"{pos.get('lever', 'N/A'):<10}{pos.get('mgnMode', 'N/A'):<14}"
                      f"{pos.get('mgnRatio', 'N/A'):<24}{pos.get('liqPx', 'N/A'):<22}"
                      f"{pos.get('upl', 'N/A'):<38}{pos.get('realizedPnl', 'N/A'):<18}")

        else:
            print("❌ 未找到 'data' 字段:", data)

    except json.JSONDecodeError:
        print("❌ 解析 JSON 失敗:", data)

# 建立 WebSocket 連線
async def connect_okx():
    url = "wss://ws.okx.com:8443/ws/v5/private"
    
    async with websockets.connect(url) as ws:
        print("✅ 連線成功，開始認證...")

        # 取得時間戳
        timestamp = str(int(time.time()))

        # 準備認證訊息
        auth_msg = {
            "op": "login",
            "args": [
                {
                    "apiKey": API_KEY,
                    "passphrase": PASSPHRASE,
                    "timestamp": timestamp,
                    "sign": generate_signature(timestamp)
                }
            ]
        }

        # 發送認證請求
        await ws.send(json.dumps(auth_msg))
        response = await ws.recv()
        print("🔑 認證結果:", response)

        # 訂閱持倉資訊
        subscribe_msg = {
            "op": "subscribe",
            "args": [
                {
                    "channel": "positions",
                    "instType": "SWAP"  # 可選值: MARGIN, SWAP, FUTURES, OPTION
                }
            ]
        }

        await ws.send(json.dumps(subscribe_msg))
        print("📡 已訂閱持倉資訊")

        positions_cache = {}  # 用於存儲持倉資料的緩存

        # 持續接收數據並解析
        while True:
            data = await ws.recv()
            parse_positions(data, positions_cache)

# 啟動 WebSocket 連線
asyncio.run(connect_okx())
