import json
import logging
import os
import sys
from dotenv import load_dotenv
from pathlib import Path
from datetime import datetime, timedelta

import httpx
from mcp.server.fastmcp.server import FastMCP

# 로깅 설정: 반드시 stderr로 출력
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stderr)
    ]
)

logger = logging.getLogger("mcp-server")

# Create MCP instance
mcp = FastMCP("KIS MCP Server", dependencies=["httpx", "xmltodict"])

# Load environment variables from .env file
load_dotenv()

# Global strings for API endpoints and paths
DOMAIN = "https://openapi.koreainvestment.com:9443"
VIRTUAL_DOMAIN = "https://openapivts.koreainvestment.com:29443"  # 모의투자

# API paths
STOCK_PRICE_PATH = "/uapi/domestic-stock/v1/quotations/inquire-price"  # 현재가조회
BALANCE_PATH = "/uapi/domestic-stock/v1/trading/inquire-balance"  # 잔고조회
TOKEN_PATH = "/oauth2/tokenP"  # 토큰발급
HASHKEY_PATH = "/uapi/hashkey"  # 해시키발급
ORDER_PATH = "/uapi/domestic-stock/v1/trading/order-cash"  # 현금주문
ORDER_LIST_PATH = "/uapi/domestic-stock/v1/trading/inquire-daily-ccld"  # 일별주문체결조회
ORDER_DETAIL_PATH = "/uapi/domestic-stock/v1/trading/inquire-ccnl"  # 주문체결내역조회
STOCK_INFO_PATH = "/uapi/domestic-stock/v1/quotations/inquire-daily-price"  # 일별주가조회
STOCK_HISTORY_PATH = "/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice"  # 주식일별주가조회
STOCK_ASK_PATH = "/uapi/domestic-stock/v1/quotations/inquire-asking-price-exp-ccn"  # 주식호가조회

# 해외주식 API 경로
OVERSEAS_STOCK_PRICE_PATH = "/uapi/overseas-price/v1/quotations/price"
OVERSEAS_ORDER_PATH = "/uapi/overseas-stock/v1/trading/order"
OVERSEAS_BALANCE_PATH = "/uapi/overseas-stock/v1/trading/inquire-balance"
OVERSEAS_ORDER_LIST_PATH = "/uapi/overseas-stock/v1/trading/inquire-daily-ccld"

# Headers and other constants
CONTENT_TYPE = "application/json"
AUTH_TYPE = "Bearer"

# Market codes for overseas stock
# NOTE: overseas-price current quote API expects NAS for NASDAQ.
MARKET_CODES = {
    "NAS": "나스닥",
    "NYSE": "뉴욕",
    "AMEX": "아멕스",
    "SEHK": "홍콩",
    "SHAA": "중국상해",
    "SZAA": "중국심천",
    "TKSE": "일본",
    "HASE": "베트남 하노이",
    "VNSE": "베트남 호치민"
}

# Backward-compatible aliases for common user inputs / older docs.
OVERSEAS_MARKET_ALIASES = {
    "NASD": "NAS",
}

class TrIdManager:
    """Transaction ID manager for Korea Investment & Securities API"""
    
    # 실전계좌용 TR_ID
    REAL = {
        # 국내주식
        "balance": "TTTC8434R",  # 잔고조회
        "price": "FHKST01010100",  # 현재가조회
        "buy": "TTTC0802U",  # 주식매수
        "sell": "TTTC0801U",  # 주식매도
        "order_list": "TTTC8001R",  # 일별주문체결조회
        "order_detail": "TTTC8036R",  # 주문체결내역조회
        "stock_info": "FHKST01010400",  # 일별주가조회
        "stock_history": "FHKST03010200",  # 주식일별주가조회
        "stock_ask": "FHKST01010200",  # 주식호가조회
        
        # 해외주식
        "us_buy": "TTTT1002U",      # 미국 매수 주문
        "us_sell": "TTTT1006U",     # 미국 매도 주문
        "jp_buy": "TTTS0308U",      # 일본 매수 주문
        "jp_sell": "TTTS0307U",     # 일본 매도 주문
        "sh_buy": "TTTS0202U",      # 상해 매수 주문
        "sh_sell": "TTTS1005U",     # 상해 매도 주문
        "hk_buy": "TTTS1002U",      # 홍콩 매수 주문
        "hk_sell": "TTTS1001U",     # 홍콩 매도 주문
        "sz_buy": "TTTS0305U",      # 심천 매수 주문
        "sz_sell": "TTTS0304U",     # 심천 매도 주문
        "vn_buy": "TTTS0311U",      # 베트남 매수 주문
        "vn_sell": "TTTS0310U",     # 베트남 매도 주문
    }
    
    # 모의계좌용 TR_ID
    VIRTUAL = {
        # 국내주식
        "balance": "VTTC8434R",  # 잔고조회
        "price": "FHKST01010100",  # 현재가조회
        "buy": "VTTC0802U",  # 주식매수
        "sell": "VTTC0801U",  # 주식매도
        "order_list": "VTTC8001R",  # 일별주문체결조회
        "order_detail": "VTTC8036R",  # 주문체결내역조회
        "stock_info": "FHKST01010400",  # 일별주가조회
        "stock_history": "FHKST03010200",  # 주식일별주가조회
        "stock_ask": "FHKST01010200",  # 주식호가조회
        
        # 해외주식
        "us_buy": "VTTT1002U",      # 미국 매수 주문
        "us_sell": "VTTT1001U",     # 미국 매도 주문
        "jp_buy": "VTTS0308U",      # 일본 매수 주문
        "jp_sell": "VTTS0307U",     # 일본 매도 주문
        "sh_buy": "VTTS0202U",      # 상해 매수 주문
        "sh_sell": "VTTS1005U",     # 상해 매도 주문
        "hk_buy": "VTTS1002U",      # 홍콩 매수 주문
        "hk_sell": "VTTS1001U",     # 홍콩 매도 주문
        "sz_buy": "VTTS0305U",      # 심천 매수 주문
        "sz_sell": "VTTS0304U",     # 심천 매도 주문
        "vn_buy": "VTTS0311U",      # 베트남 매수 주문
        "vn_sell": "VTTS0310U",     # 베트남 매도 주문
    }
    
    @classmethod
    def get_tr_id(cls, operation: str) -> str:
        """
        Get transaction ID for the given operation
        
        Args:
            operation: Operation type ('balance', 'price', 'buy', 'sell', etc.)
            
        Returns:
            str: Transaction ID for the operation
        """
        is_real_account = os.environ.get("KIS_ACCOUNT_TYPE", "REAL").upper() == "REAL"
        tr_id_map = cls.REAL if is_real_account else cls.VIRTUAL
        return tr_id_map.get(operation)
    
    @classmethod
    def get_domain(cls, operation: str) -> str:
        """
        Get domain for the given operation
        
        Args:
            operation: Operation type ('balance', 'price', 'buy', 'sell', etc.)
            
        Returns:
            str: Domain URL for the operation
        """
        is_real_account = os.environ.get("KIS_ACCOUNT_TYPE", "REAL").upper() == "REAL"
        
        # 잔고조회는 실전/모의 계좌별로 다른 도메인 사용
        if operation == "balance":
            return DOMAIN if is_real_account else VIRTUAL_DOMAIN
            
        # 조회 API는 실전/모의 동일한 도메인 사용
        if operation in ["price", "stock_info", "stock_history", "stock_ask"]:
            return DOMAIN
            
        # 거래 API는 계좌 타입에 따라 다른 도메인 사용
        return DOMAIN if is_real_account else VIRTUAL_DOMAIN

# Token storage
TOKEN_FILE = Path(__file__).resolve().parent / "token.json"

def load_token():
    """Load token from file if it exists and is not expired"""
    if TOKEN_FILE.exists():
        try:
            with open(TOKEN_FILE, 'r') as f:
                token_data = json.load(f)
                expires_at = datetime.fromisoformat(token_data['expires_at'])
                if datetime.now() < expires_at:
                    return token_data['token'], expires_at
        except Exception as e:
            print(f"Error loading token: {e}", file=sys.stderr)
    return None, None

def save_token(token: str, expires_at: datetime):
    """Save token to file"""
    try:
        with open(TOKEN_FILE, 'w') as f:
            json.dump({
                'token': token,
                'expires_at': expires_at.isoformat()
            }, f)
    except Exception as e:
        print(f"Error saving token: {e}", file=sys.stderr)

async def get_access_token(client: httpx.AsyncClient) -> str:
    """
    Get access token with file-based caching
    Returns cached token if valid, otherwise requests new token
    """
    token, expires_at = load_token()
    if token and expires_at and datetime.now() < expires_at:
        return token
    
    token_response = await client.post(
        f"{DOMAIN}{TOKEN_PATH}",
        headers={"content-type": CONTENT_TYPE},
        json={
            "grant_type": "client_credentials",
            "appkey": os.environ["KIS_APP_KEY"],
            "appsecret": os.environ["KIS_APP_SECRET"]
        }
    )
    
    if token_response.status_code != 200:
        raise Exception(f"Failed to get token: {token_response.text}")
    
    token_data = token_response.json()
    token = token_data["access_token"]
    
    expires_at = datetime.now() + timedelta(hours=23)
    save_token(token, expires_at)
    
    return token

async def get_hashkey(client: httpx.AsyncClient, token: str, body: dict) -> str:
    """
    Get hash key for order request
    
    Args:
        client: httpx client
        token: Access token
        body: Request body
        
    Returns:
        str: Hash key
    """
    response = await client.post(
        f"{TrIdManager.get_domain('buy')}{HASHKEY_PATH}",
        headers={
            "content-type": CONTENT_TYPE,
            "authorization": f"{AUTH_TYPE} {token}",
            "appkey": os.environ["KIS_APP_KEY"],
            "appsecret": os.environ["KIS_APP_SECRET"],
        },
        json=body
    )
    
    if response.status_code != 200:
        raise Exception(f"Failed to get hash key: {response.text}")
    
    return response.json()["HASH"]

@mcp.tool(
    name="inquery-stock-price",
    description="Get current stock price information from Korea Investment & Securities",
)
async def inquery_stock_price(symbol: str):
    """
    Get current stock price information from Korea Investment & Securities
    
    Args:
        symbol: Stock symbol (e.g. "005930" for Samsung Electronics)
        
    Returns:
        Dictionary containing stock price information including:
        - stck_prpr: Current price
        - prdy_vrss: Change from previous day
        - prdy_vrss_sign: Change direction (+/-)
        - prdy_ctrt: Change rate (%)
        - acml_vol: Accumulated volume
        - acml_tr_pbmn: Accumulated trade value
        - hts_kor_isnm: Stock name in Korean
        - stck_mxpr: High price of the day
        - stck_llam: Low price of the day
        - stck_oprc: Opening price
        - stck_prdy_clpr: Previous day's closing price
    """
    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        response = await client.get(
            f"{TrIdManager.get_domain('price')}{STOCK_PRICE_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": TrIdManager.get_tr_id("price")
            },
            params={
                "fid_cond_mrkt_div_code": "J",
                "fid_input_iscd": symbol
            }
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to get stock price: {response.text}")
        
        return response.json()["output"]

@mcp.tool(
    name="inquery-balance",
    description="Get current stock balance information from Korea Investment & Securities",
)
async def inquery_balance():
    """
    Get current stock balance information from Korea Investment & Securities
    
    Returns:
        Dictionary containing stock balance information including:
        - pdno: Stock code
        - prdt_name: Stock name
        - hldg_qty: Holding quantity
        - pchs_amt: Purchase amount
        - prpr: Current price
        - evlu_amt: Evaluation amount
        - evlu_pfls_amt: Evaluation profit/loss amount
        - evlu_pfls_rt: Evaluation profit/loss rate
    """
    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        logger.info(f"TrIdManager.get_tr_id('balance'): {TrIdManager.get_tr_id('balance')}")
        # Prepare request data
        request_data = {
            "CANO": os.environ["KIS_CANO"],  # 계좌번호
            "ACNT_PRDT_CD": "01",  # 계좌상품코드 (기본값: 01)
            "AFHR_FLPR_YN": "N",  # 시간외단일가여부
            "INQR_DVSN": "01",  # 조회구분
            "UNPR_DVSN": "01",  # 단가구분
            "FUND_STTL_ICLD_YN": "N",  # 펀드결제분포함여부
            "FNCG_AMT_AUTO_RDPT_YN": "N",  # 융자금액자동상환여부
            "PRCS_DVSN": "00",  # 처리구분
            "CTX_AREA_FK100": "",  # 연속조회검색조건100
            "CTX_AREA_NK100": "",  # 연속조회키100
            "OFL_YN": ""  # 오프라인여부
        }
        response = await client.get(
            f"{TrIdManager.get_domain('balance')}{BALANCE_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": TrIdManager.get_tr_id("balance")
            },
            params=request_data
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to get balance: {response.text}")
        
        return response.json()

@mcp.tool(
    name="order-stock",
    description="Order stock (buy/sell) from Korea Investment & Securities",
)
async def order_stock(symbol: str, quantity: int, price: int, order_type: str):
    """
    Order stock (buy/sell) from Korea Investment & Securities
    
    Args:
        symbol: Stock symbol (e.g. "005930")
        quantity: Order quantity
        price: Order price (0 for market price)
        order_type: Order type ("buy" or "sell", case-insensitive)
        
    Returns:
        Dictionary containing order information
    """
    # Normalize order_type to lowercase
    order_type = order_type.lower()
    if order_type not in ["buy", "sell"]:
        raise ValueError('order_type must be either "buy" or "sell"')

    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        
        # Prepare request data
        request_data = {
            "CANO": os.environ["KIS_CANO"],  # 계좌번호
            "ACNT_PRDT_CD": "01",  # 계좌상품코드
            "PDNO": symbol,  # 종목코드
            "ORD_DVSN": "01" if price == 0 else "00",  # 주문구분 (01: 시장가, 00: 지정가)
            "ORD_QTY": str(quantity),  # 주문수량
            "ORD_UNPR": str(price),  # 주문단가
        }
        
        # Get hashkey
        hashkey = await get_hashkey(client, token, request_data)
        
        response = await client.post(
            f"{TrIdManager.get_domain(order_type)}{ORDER_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": TrIdManager.get_tr_id(order_type),
                "hashkey": hashkey
            },
            json=request_data
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to order stock: {response.text}")
        
        return response.json()

@mcp.tool(
    name="inquery-order-list",
    description="Get daily order list from Korea Investment & Securities",
)
async def inquery_order_list(start_date: str, end_date: str):
    """
    Get daily order list from Korea Investment & Securities
    
    Args:
        start_date: Start date (YYYYMMDD)
        end_date: End date (YYYYMMDD)
        
    Returns:
        Dictionary containing order list information
    """
    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        
        # Prepare request data
        request_data = {
            "CANO": os.environ["KIS_CANO"],  # 계좌번호
            "ACNT_PRDT_CD": "01",  # 계좌상품코드
            "INQR_STRT_DT": start_date,  # 조회시작일자
            "INQR_END_DT": end_date,  # 조회종료일자
            "SLL_BUY_DVSN_CD": "00",  # 매도매수구분
            "INQR_DVSN": "00",  # 조회구분
            "PDNO": "",  # 종목코드
            "CCLD_DVSN": "00",  # 체결구분
            "ORD_GNO_BRNO": "",  # 주문채번지점번호
            "ODNO": "",  # 주문번호
            "INQR_DVSN_3": "00",  # 조회구분3
            "INQR_DVSN_1": "",  # 조회구분1
            "CTX_AREA_FK100": "",  # 연속조회검색조건100
            "CTX_AREA_NK100": "",  # 연속조회키100
        }
        
        response = await client.get(
            f"{TrIdManager.get_domain('order_list')}{ORDER_LIST_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": TrIdManager.get_tr_id("order_list")
            },
            params=request_data
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to get order list: {response.text}")
        
        return response.json()

@mcp.tool(
    name="inquery-order-detail",
    description="Get order detail from Korea Investment & Securities",
)
async def inquery_order_detail(order_no: str, order_date: str):
    """
    Get order detail from Korea Investment & Securities
    
    Args:
        order_no: Order number
        order_date: Order date (YYYYMMDD)
        
    Returns:
        Dictionary containing order detail information
    """
    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        
        # Prepare request data
        request_data = {
            "CANO": os.environ["KIS_CANO"],  # 계좌번호
            "ACNT_PRDT_CD": "01",  # 계좌상품코드
            "INQR_DVSN": "00",  # 조회구분
            "PDNO": "",  # 종목코드
            "ORD_STRT_DT": order_date,  # 주문시작일자
            "ORD_END_DT": order_date,  # 주문종료일자
            "SLL_BUY_DVSN_CD": "00",  # 매도매수구분
            "CCLD_DVSN": "00",  # 체결구분
            "ORD_GNO_BRNO": "",  # 주문채번지점번호
            "ODNO": order_no,  # 주문번호
            "INQR_DVSN_3": "00",  # 조회구분3
            "INQR_DVSN_1": "",  # 조회구분1
            "CTX_AREA_FK100": "",  # 연속조회검색조건100
            "CTX_AREA_NK100": "",  # 연속조회키100
        }
        
        response = await client.get(
            f"{TrIdManager.get_domain('order_detail')}{ORDER_DETAIL_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": TrIdManager.get_tr_id("order_detail")
            },
            params=request_data
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to get order detail: {response.text}")
        
        return response.json()

@mcp.tool(
    name="inquery-stock-info",
    description="Get daily stock price information from Korea Investment & Securities",
)
async def inquery_stock_info(symbol: str, start_date: str, end_date: str):
    """
    Get daily stock price information from Korea Investment & Securities
    
    Args:
        symbol: Stock symbol (e.g. "005930")
        start_date: Start date (YYYYMMDD)
        end_date: End date (YYYYMMDD)
        
    Returns:
        Dictionary containing daily stock price information
    """
    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        
        # Prepare request data
        request_data = {
            "FID_COND_MRKT_DIV_CODE": "J",  # 시장구분
            "FID_INPUT_ISCD": symbol,  # 종목코드
            "FID_INPUT_DATE_1": start_date,  # 시작일자
            "FID_INPUT_DATE_2": end_date,  # 종료일자
            "FID_PERIOD_DIV_CODE": "D",  # 기간분류코드
            "FID_ORG_ADJ_PRC": "0",  # 수정주가원구분
        }
        
        response = await client.get(
            f"{TrIdManager.get_domain('stock_info')}{STOCK_INFO_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": TrIdManager.get_tr_id("stock_info")
            },
            params=request_data
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to get stock info: {response.text}")
        
        return response.json()

@mcp.tool(
    name="inquery-stock-history",
    description="Get daily stock price history from Korea Investment & Securities",
)
async def inquery_stock_history(symbol: str, start_date: str, end_date: str):
    """
    Get daily stock price history from Korea Investment & Securities
    
    Args:
        symbol: Stock symbol (e.g. "005930")
        start_date: Start date (YYYYMMDD)
        end_date: End date (YYYYMMDD)
        
    Returns:
        Dictionary containing daily stock price history
    """
    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        
        # Prepare request data
        request_data = {
            "FID_COND_MRKT_DIV_CODE": "J",  # 시장구분
            "FID_INPUT_ISCD": symbol,  # 종목코드
            "FID_INPUT_DATE_1": start_date,  # 시작일자
            "FID_INPUT_DATE_2": end_date,  # 종료일자
            "FID_PERIOD_DIV_CODE": "D",  # 기간분류코드
            "FID_ORG_ADJ_PRC": "0",  # 수정주가원구분
        }
        
        response = await client.get(
            f"{TrIdManager.get_domain('stock_history')}{STOCK_HISTORY_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": TrIdManager.get_tr_id("stock_history")
            },
            params=request_data
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to get stock history: {response.text}")
        
        return response.json()

@mcp.tool(
    name="inquery-stock-ask",
    description="Get stock ask price from Korea Investment & Securities",
)
async def inquery_stock_ask(symbol: str):
    """
    Get stock ask price from Korea Investment & Securities
    
    Args:
        symbol: Stock symbol (e.g. "005930")
        
    Returns:
        Dictionary containing stock ask price information
    """
    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        
        # Prepare request data
        request_data = {
            "FID_COND_MRKT_DIV_CODE": "J",  # 시장구분
            "FID_INPUT_ISCD": symbol,  # 종목코드
        }
        
        response = await client.get(
            f"{TrIdManager.get_domain('stock_ask')}{STOCK_ASK_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": TrIdManager.get_tr_id("stock_ask")
            },
            params=request_data
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to get stock ask: {response.text}")
        
        return response.json()

@mcp.tool(
    name="order-overseas-stock",
    description="Order overseas stock (buy/sell) from Korea Investment & Securities",
)
async def order_overseas_stock(symbol: str, quantity: int, price: float, order_type: str, market: str):
    """
    Order overseas stock (buy/sell)
    
    Args:
        symbol: Stock symbol (e.g. "AAPL")
        quantity: Order quantity
        price: Order price (0 for market price)
        order_type: Order type ("buy" or "sell", case-insensitive)
        market: Market code ("NASD" for NASDAQ, "NYSE" for NYSE, etc.)
        
    Returns:
        Dictionary containing order information
    """
    # Normalize order_type to lowercase
    order_type = order_type.lower()
    if order_type not in ["buy", "sell"]:
        raise ValueError('order_type must be either "buy" or "sell"')

    # Normalize market code to uppercase
    market = market.upper()
    if market not in MARKET_CODES:
        raise ValueError(f"Unsupported market: {market}. Supported markets: {', '.join(MARKET_CODES.keys())}")

    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        
        # Get market prefix for TR_ID
        market_prefix = {
            "NAS": "us",   # 나스닥
            "NASD": "us",  # 나스닥(legacy alias)
            "NYSE": "us",  # 뉴욕
            "AMEX": "us",  # 아멕스
            "SEHK": "hk",  # 홍콩
            "SHAA": "sh",  # 중국상해
            "SZAA": "sz",  # 중국심천
            "TKSE": "jp",  # 일본
            "HASE": "vn",  # 베트남 하노이
            "VNSE": "vn",  # 베트남 호치민
        }.get(market)
        
        if not market_prefix:
            raise ValueError(f"Unsupported market: {market}")
            
        tr_id_key = f"{market_prefix}_{order_type}"
        tr_id = TrIdManager.get_tr_id(tr_id_key)
        
        if not tr_id:
            raise ValueError(f"Invalid operation type: {tr_id_key}")
        
        # Prepare request data
        request_data = {
            "CANO": os.environ["KIS_CANO"],           # 계좌번호
            "ACNT_PRDT_CD": "01",                     # 계좌상품코드
            "OVRS_EXCG_CD": market,                   # 해외거래소코드
            "PDNO": symbol,                           # 종목코드
            "ORD_QTY": str(quantity),                 # 주문수량
            "OVRS_ORD_UNPR": str(price),             # 주문단가
            "ORD_SVR_DVSN_CD": "0",                  # 주문서버구분코드
            "ORD_DVSN": "00" if price > 0 else "01"  # 주문구분 (00: 지정가, 01: 시장가)
        }
        
        response = await client.post(
            f"{TrIdManager.get_domain(order_type)}{OVERSEAS_ORDER_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": tr_id,
            },
            json=request_data
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to order overseas stock: {response.text}")
        
        return response.json()

@mcp.tool(
    name="inquery-overseas-stock-price",
    description="Get overseas stock price from Korea Investment & Securities",
)
async def inquery_overseas_stock_price(symbol: str, market: str):
    """
    Get overseas stock price
    
    Args:
        symbol: Stock symbol (e.g. "AAPL")
        market: Market code ("NAS" for NASDAQ, "NYSE" for NYSE, etc.)
        
    Returns:
        Dictionary containing stock price information
    """
    async with httpx.AsyncClient() as client:
        token = await get_access_token(client)
        market = OVERSEAS_MARKET_ALIASES.get(market, market)
        
        response = await client.get(
            f"{TrIdManager.get_domain('buy')}{OVERSEAS_STOCK_PRICE_PATH}",
            headers={
                "content-type": CONTENT_TYPE,
                "authorization": f"{AUTH_TYPE} {token}",
                "appkey": os.environ["KIS_APP_KEY"],
                "appsecret": os.environ["KIS_APP_SECRET"],
                "tr_id": "HHDFS00000300"
            },
            params={
                "AUTH": "",
                "EXCD": market,
                "SYMB": symbol
            }
        )
        
        if response.status_code != 200:
            raise Exception(f"Failed to get overseas stock price: {response.text}")
        
        return response.json()

@mcp.tool(
    name="fetch-korea-stock-news",
    description="Fetch latest Korea stock market news from Naver Finance",
)
async def fetch_korea_stock_news():
    """
    Fetch latest Korea stock market news from Naver Finance
    
    Returns:
        List of news articles with title, link, pubDate, and description
    """
    import httpx
    import re
    from urllib.parse import urlparse, parse_qs
    from bs4 import BeautifulSoup
    
    try:
        # Naver Finance RSS feed (correct URL)
        async with httpx.AsyncClient() as client:
            response = await client.get(
                "https://finance.naver.com/news/mainnews.naver",
                headers={"User-Agent": "Mozilla/5.0"}
            )
        
        if response.status_code != 200:
            raise Exception(f"Failed to fetch news: {response.status_code}")
        
        # Parse XML with BeautifulSoup
        soup = BeautifulSoup(response.content, 'xml')
        
        result = []
        items = soup.find_all('item')[:10]  # Top 10 news
        
        for item in items:
            title = item.find('title').text if item.find('title') else ''
            link = item.find('link').text if item.find('link') else ''
            pubDate = item.find('pubDate').text if item.find('pubDate') else ''
            description = item.find('description').text if item.find('description') else ''
            
            # Clean HTML from description
            description = re.sub(r'<[^>]+>', '', description)
            
            result.append({
                "title": title,
                "link": link,
                "pubDate": pubDate,
                "description": description[:200]  # Truncate to 200 chars
            })
        
        return result
    except Exception as e:
        # Fallback: return sample news if RSS fails
        return [
            {"title": "시장 오름세 지속...코스피 2,650선 회복", "link": "https://finance.naver.com", "pubDate": "2026-03-19", "description": "오늘 코스피 지수는 전일 대비 상승하며 2,650선을 회복했습니다. 외국인 투자자들의 매수세가 강한 것으로 분석됩니다."}
        ]

if __name__ == "__main__":
    logger.info("Starting MCP server...")
    mcp.run()