import httpx
from typing import Dict, Optional, Any
from app.services.kis_auth import kis_auth
from app.core.rate_limiter import global_rate_limiter
from app.db.database import SessionLocal
from app.db.models import ApiSettings 
from sqlalchemy import select

class KisClient:
    """
    Brokerage Service Interface for KIS API.
    Implements Section 9 Integration Map.
    """
    
    # Domestic URLs
    URL_DOMESTIC_ORDER = "/uapi/domestic-stock/v1/trading/order-cash"
    URL_DOMESTIC_PRICE = "/uapi/domestic-stock/v1/quotations/inquire-price"
    URL_DOMESTIC_BALANCE = "/uapi/domestic-stock/v1/trading/inquire-balance"
    
    # Overseas URLs
    URL_OVERSEAS_ORDER = "/uapi/overseas-stock/v1/trading/order"
    URL_OVERSEAS_PRICE = "/uapi/overseas-price/v1/quotations/price"
    URL_OVERSEAS_BALANCE = "/uapi/overseas-stock/v1/trading/inquire-balance"

    def __init__(self):
        pass

    async def _get_settings(self):
        async with SessionLocal() as session:
            stmt = select(ApiSettings).where(ApiSettings.id == 1)
            result = await session.execute(stmt)
            return result.scalar_one_or_none()

    async def _call_api(self, method: str, url_path: str, tr_id: str, params: Dict = None, data: Dict = None) -> Dict:
        """
        Common API Caller with Rate Limiting and Auth.
        """
        # 1. Rate Limit
        await global_rate_limiter.wait()
        
        # 2. Get Token & Base URL
        # Assuming Real Environment for now. TODO: Support Virtual
        base_url = kis_auth.BASE_URL_REAL
        token = await kis_auth.get_access_token()
        settings = await self._get_settings()
        
        if not settings:
            raise ValueError("Settings not found")

        # 3. Headers
        headers = {
            "Content-Type": "application/json",
            "authorization": f"Bearer {token}",
            "appkey": settings.appKey,
            "appsecret": settings.appSecret,
            "tr_id": tr_id,
            "tr_cont": "",
            "custtype": "P"
        }
        
        full_url = f"{base_url}{url_path}"
        
        async with httpx.AsyncClient() as client:
            if method == "GET":
                resp = await client.get(full_url, headers=headers, params=params)
            else:
                resp = await client.post(full_url, headers=headers, json=data)
            
            # TODO: Handle 401 Re-Auth Logic here (catch, clear token, retry)
            
            resp.raise_for_status()
            return resp.json()

    # -----------------------------
    # 1. Current Price
    # -----------------------------
    async def get_current_price(self, market: str, code: str) -> float:
        if market == "Domestic":
            # TR_ID: FHKST01010100
            params = {
                "FID_COND_MRKT_DIV_CODE": "J",
                "FID_INPUT_ISCD": code
            }
            res = await self._call_api("GET", self.URL_DOMESTIC_PRICE, "FHKST01010100", params=params)
            return float(res['output']['stck_prpr'])
            
        elif market == "Overseas":
            # TR_ID: HHDFS00000300
            # Need Exchange Code (e.g., NASD). Assuming NASD for generic US or mapped.
            # Ideally code should be 'NASD:AAPL' or separate excg param.
            # For now assuming 'NASD' if not provided implicitly.
            # Or code is just 'AAPL'.
            excg = "NASD" # Default
            params = {
                "AUTH": "",
                "EXCD": excg,
                "SYMB": code
            }
            res = await self._call_api("GET", self.URL_OVERSEAS_PRICE, "HHDFS00000300", params=params)
            return float(res['output']['last'])
        
        return 0.0

    # -----------------------------
    # 2. Balance
    # -----------------------------
    async def get_balance(self, market: str) -> Dict:
        settings = await self._get_settings()
        acc_no = settings.accountNumber
        # acc_no is 8 digits. Split? "500xxx-01" -> 500xxx, 01
        if '-' in acc_no:
            cano, prdt = acc_no.split('-')
        else:
            cano = acc_no[:8]
            prdt = acc_no[8:]

        if market == "Domestic":
            # TR_ID: TTTC8434R (Real)
            params = {
                "CANO": cano,
                "ACNT_PRDT_CD": prdt,
                "AFHR_FLPR_YN": "N",
                "OFL_YN": "",
                "INQR_DVSN": "02",
                "UNPR_DVSN": "01",
                "FUND_STTL_ICLD_YN": "N",
                "FNCG_AMT_AUTO_RDPT_YN": "N",
                "PRCS_DVSN": "01",
                "CTX_AREA_FK100": "",
                "CTX_AREA_NK100": ""
            }
            res = await self._call_api("GET", self.URL_DOMESTIC_BALANCE, "TTTC8434R", params=params)
            return res
            
        elif market == "Overseas":
            # TR_ID: TTTS3012R (Real)
            params = {
                "CANO": cano,
                "ACNT_PRDT_CD": prdt,
                "OVRS_EXCG_CD": "NASD", # Default
                "TR_CRCY_CD": "USD",
                "CTX_AREA_FK200": "",
                "CTX_AREA_NK200": ""
            }
            res = await self._call_api("GET", self.URL_OVERSEAS_BALANCE, "TTTS3012R", params=params)
            return res
            
        return {}

    # -----------------------------
    # 3. Order
    # -----------------------------
    async def place_order(self, market: str, side: str, code: str, quantity: int, price: float) -> Dict:
        """
        side: 'buy' or 'sell'
        price: 0 for Market? KIS logic varies.
        """
        settings = await self._get_settings()
        if '-' in settings.accountNumber:
            cano, prdt = settings.accountNumber.split('-')
        else:
            cano = settings.accountNumber[:8]
            prdt = settings.accountNumber[8:]

        if market == "Domestic":
            # TR_ID: TTT 0802U (Buy), 0801U (Sell) -> using sample 0012U/0011U
            # 0012U (Buy), 0011U (Sell)
            tr_id = "TTTC0012U" if side == "buy" else "TTTC0011U"
            
            data = {
                "CANO": cano,
                "ACNT_PRDT_CD": prdt,
                "PDNO": code,
                "ORD_DVSN": "00", # Limit (00). 01=Market
                "ORD_QTY": str(quantity),
                "ORD_UNPR": str(int(price)), # Cash Order requires integer price string
            }
            return await self._call_api("POST", self.URL_DOMESTIC_ORDER, tr_id, data=data)
            
        elif market == "Overseas":
             # TR_ID: TTTT1002U (US Buy), TTTT1006U (US Sell)
             # Assuming US (NASD)
             tr_id = "TTTT1002U" if side == "buy" else "TTTT1006U"
             
             data = {
                "CANO": cano,
                "ACNT_PRDT_CD": prdt,
                "OVRS_EXCG_CD": "NASD",
                "PDNO": code,
                "ORD_QTY": str(quantity),
                "OVRS_ORD_UNPR": str(price),
                "ORD_SVR_DVSN_CD": "0",
                "ORD_DVSN": "00" # Limit
             }
             return await self._call_api("POST", self.URL_OVERSEAS_ORDER, tr_id, data=data)

kis_client = KisClient()
