# coding:utf-8
import redis
import json
import datetime
from typing import Dict, Optional, List
from config_loader import get_redis_config

POSITIONS_KEY = 'qmt:positions'           # Hash: stock_code -> quantity
PENDING_KEY = 'qmt:pending_orders'        # Hash: stock_code -> json(order_info)
META_KEY = 'qmt:meta'                     # Hash: various metadata
PLAN_BUY_KEY = 'qmt:plan:buy'             # Hash: stock_code -> json(plan)
PLAN_SELL_KEY = 'qmt:plan:sell'           # Hash: stock_code -> json(plan)
PLAN_CLEARANCE_KEY = 'qmt:plan:clearance' # Hash: stock_code -> json(clearance_request)
PLAN_VERSION_KEY = 'qmt:plan:version'     # String/integer: version bump on change
PLAN_SYNCED_VERSION_KEY = 'qmt:plan:synced_version'  # 上次从DB同步的版本

class RedisStateManager:
    def __init__(self, logger=None):
        self.logger = logger
        cfg = get_redis_config()
        pool = redis.ConnectionPool(
            host=cfg.get('host', 'localhost'),
            port=cfg.get('port', 6379),
            db=cfg.get('db', 0),
            password=cfg.get('password'),
            socket_timeout=cfg.get('socket_timeout', 5),
            decode_responses=True,
        )
        self.r = redis.Redis(connection_pool=pool)

    # Positions
    def get_all_positions(self) -> Dict[str, int]:
        data = self.r.hgetall(POSITIONS_KEY)
        return {k: int(v) for k, v in data.items()} if data else {}

    def get_position(self, stock_code: str) -> int:
        v = self.r.hget(POSITIONS_KEY, stock_code)
        return int(v) if v is not None else 0

    def set_position(self, stock_code: str, quantity: int) -> None:
        if quantity > 0:
            self.r.hset(POSITIONS_KEY, stock_code, quantity)
        else:
            self.r.hdel(POSITIONS_KEY, stock_code)
        self._touch_meta('positions_updated_at')

    def incr_position(self, stock_code: str, delta: int) -> int:
        new_qty = self.r.hincrby(POSITIONS_KEY, stock_code, delta)
        if new_qty <= 0:
            self.r.hdel(POSITIONS_KEY, stock_code)
            new_qty = 0
        self._touch_meta('positions_updated_at')
        return new_qty

    # Pending Orders
    def get_all_pending(self) -> Dict[str, Dict]:
        data = self.r.hgetall(PENDING_KEY)
        return {k: json.loads(v) for k, v in data.items()} if data else {}

    def get_pending(self, stock_code: str) -> Optional[Dict]:
        # 优先从独立key读取（带过期时间）
        pending_item_key = f"{PENDING_KEY}:{stock_code}"
        v = self.r.get(pending_item_key)
        if v:
            return json.loads(v)
        # 如果独立key不存在，尝试从Hash读取（兼容旧数据）
        v = self.r.hget(PENDING_KEY, stock_code)
        return json.loads(v) if v else None

    def set_pending(self, stock_code: str, order_info: Dict) -> None:
        # 使用独立的key以支持过期时间（6小时）
        pending_item_key = f"{PENDING_KEY}:{stock_code}"
        self.r.setex(
            pending_item_key, 
            6 * 3600,  # 6小时过期
            json.dumps(order_info, ensure_ascii=False)
        )
        # 同时在Hash中记录，方便get_all_pending查询
        self.r.hset(PENDING_KEY, stock_code, json.dumps(order_info, ensure_ascii=False))
        self._touch_meta('pending_updated_at')

    def del_pending(self, stock_code: str) -> None:
        # 删除独立key和Hash中的记录
        pending_item_key = f"{PENDING_KEY}:{stock_code}"
        self.r.delete(pending_item_key)
        self.r.hdel(PENDING_KEY, stock_code)
        self._touch_meta('pending_updated_at')

    def clear_pending(self) -> None:
        # 删除Hash中的所有记录
        stock_codes = list(self.r.hkeys(PENDING_KEY))
        # 删除所有独立的pending key
        if stock_codes:
            pending_keys = [f"{PENDING_KEY}:{code}" for code in stock_codes]
            self.r.delete(*pending_keys)
        # 删除Hash本身
        self.r.delete(PENDING_KEY)
        self._touch_meta('pending_cleared_at')

    # Meta
    def _touch_meta(self, field: str) -> None:
        self.r.hset(META_KEY, field, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

    def clear_all(self) -> None:
        pipe = self.r.pipeline()
        pipe.delete(POSITIONS_KEY)
        pipe.delete(PENDING_KEY)
        pipe.delete('qmt_order_mapping')  # 清理订单映射
        pipe.execute()
        self._touch_meta('cleared_at')
    
    # 订单ID映射管理
    def set_qmt_order_mapping(self, qmt_order_id: str, our_order_id: str) -> None:
        """建立QMT内部订单ID到我们订单ID的映射"""
        self.r.hset('qmt_order_mapping', qmt_order_id, our_order_id)
    
    def get_our_order_id(self, qmt_order_id: str) -> Optional[str]:
        """根据QMT内部订单ID获取我们的订单ID"""
        return self.r.hget('qmt_order_mapping', qmt_order_id)
    
    def del_qmt_order_mapping(self, qmt_order_id: str) -> None:
        """删除订单ID映射"""
        self.r.hdel('qmt_order_mapping', qmt_order_id)

    # Plans (buy/sell)
    def get_buy_plan(self, stock_code: str) -> Optional[Dict]:
        v = self.r.hget(PLAN_BUY_KEY, stock_code)
        return json.loads(v) if v else None

    def get_sell_plan(self, stock_code: str) -> Optional[Dict]:
        v = self.r.hget(PLAN_SELL_KEY, stock_code)
        return json.loads(v) if v else None

    def set_buy_plan(self, stock_code: str, plan: Dict) -> None:
        self.r.hset(PLAN_BUY_KEY, stock_code, json.dumps(plan, ensure_ascii=False))
        self.bump_plan_version()

    def set_sell_plan(self, stock_code: str, plan: Dict) -> None:
        self.r.hset(PLAN_SELL_KEY, stock_code, json.dumps(plan, ensure_ascii=False))
        self.bump_plan_version()

    def del_buy_plan(self, stock_code: str) -> None:
        self.r.hdel(PLAN_BUY_KEY, stock_code)
        self.bump_plan_version()

    def del_sell_plan(self, stock_code: str) -> None:
        self.r.hdel(PLAN_SELL_KEY, stock_code)
        self.bump_plan_version()

    def get_all_buy_plans(self) -> Dict[str, Dict]:
        data = self.r.hgetall(PLAN_BUY_KEY)
        return {k: json.loads(v) for k, v in data.items()} if data else {}

    def get_all_sell_plans(self) -> Dict[str, Dict]:
        data = self.r.hgetall(PLAN_SELL_KEY)
        return {k: json.loads(v) for k, v in data.items()} if data else {}

    def get_buy_codes(self) -> List[str]:
        return list(self.r.hkeys(PLAN_BUY_KEY))

    def get_sell_codes(self) -> List[str]:
        return list(self.r.hkeys(PLAN_SELL_KEY))

    def get_plan_version(self) -> int:
        v = self.r.get(PLAN_VERSION_KEY)
        try:
            return int(v) if v is not None else 0
        except Exception:
            return 0

    def bump_plan_version(self) -> int:
        return int(self.r.incr(PLAN_VERSION_KEY))

    def clear_buy_plans(self) -> None:
        self.r.delete(PLAN_BUY_KEY)
        self.bump_plan_version()

    def clear_sell_plans(self) -> None:
        self.r.delete(PLAN_SELL_KEY)
        self.bump_plan_version()

    # synced version helpers
    def get_synced_version(self) -> int:
        v = self.r.get(PLAN_SYNCED_VERSION_KEY)
        try:
            return int(v) if v is not None else 0
        except Exception:
            return 0

    def set_synced_version(self, version: int) -> None:
        self.r.set(PLAN_SYNCED_VERSION_KEY, version)

    # Clearance Plans
    def get_clearance_plan(self, stock_code: str) -> Optional[Dict]:
        v = self.r.hget(PLAN_CLEARANCE_KEY, stock_code)
        return json.loads(v) if v else None

    def set_clearance_plan(self, stock_code: str, plan: Dict) -> None:
        self.r.hset(PLAN_CLEARANCE_KEY, stock_code, json.dumps(plan, ensure_ascii=False))

    def del_clearance_plan(self, stock_code: str) -> None:
        self.r.hdel(PLAN_CLEARANCE_KEY, stock_code)

    def get_all_clearance_plans(self) -> Dict[str, Dict]:
        data = self.r.hgetall(PLAN_CLEARANCE_KEY)
        return {k: json.loads(v) for k, v in data.items()} if data else {}

    def clear_clearance_plans(self) -> None:
        self.r.delete(PLAN_CLEARANCE_KEY)
