import os
from pathlib import Path
from typing import Literal
import httpx
import pandas as pd
from mcp.server.fastmcp import FastMCP
from supabase import create_client, Client
from openai import AsyncAzureOpenAI


# 取得資料檔案路徑（從套件目錄讀取）
DATA_FILE = Path(__file__).parent / "case_data.csv"
TAX_RULES_FILE = Path(__file__).parent / "tax_rules.csv"

# Serper API 設定
SERPER_API_URL = "https://google.serper.dev/search"

# 建立 FastMCP server
mcp = FastMCP("case-search")


def load_case_data():
    """載入案件資料 CSV 檔案"""
    try:
        df = pd.read_csv(DATA_FILE)
        return df
    except FileNotFoundError:
        raise FileNotFoundError(f"找不到資料檔案: {DATA_FILE}")
    except Exception as e:
        raise Exception(f"讀取資料檔案時發生錯誤: {e}")


def load_tax_rules():
    """載入稅務規則 CSV 檔案"""
    try:
        df = pd.read_csv(TAX_RULES_FILE)
        return df
    except FileNotFoundError:
        raise FileNotFoundError(f"找不到稅務規則檔案: {TAX_RULES_FILE}")
    except Exception as e:
        raise Exception(f"讀取稅務規則檔案時發生錯誤: {e}")


def get_rules_by_tax_type(tax_type: str):
    """根據稅務類型篩選規則"""
    df = load_tax_rules()

    # 過濾符合稅務類型的規則
    filtered_rules = df[df['tax_type'] == tax_type]

    if filtered_rules.empty:
        return None

    # 轉換為字典列表
    rules_list = filtered_rules.to_dict('records')

    # 處理 NaN 值
    for rule in rules_list:
        for key, value in rule.items():
            if pd.isna(value):
                rule[key] = None

    return rules_list


def search_case_by_ban(ban: str):
    """根據統一編號(ban)查詢營業人稅務資訊（不區分大小寫）"""
    df = load_case_data()

    # 搜尋符合的營業人（不區分大小寫）
    result = df[df['ban'].str.upper() == ban.upper()]

    if result.empty:
        return None

    # 將結果轉換為字典並處理 NaN 值
    case_info = result.iloc[0].to_dict()

    # 將 NaN 轉換為 None（JSON null）
    for key, value in case_info.items():
        if pd.isna(value):
            case_info[key] = None

    return case_info


@mcp.tool()
def search_case(ban: str) -> dict:
    """
    根據統一編號(ban)查詢營業人稅務資訊（不區分大小寫）。
    輸入統一編號，回傳該營業人的完整稅務資訊，包括：
    - 基本資料：統一編號(ban)、營業人名稱(company_name)、行業別(industry_type)、課稅方式(tax_method)
    - 111年度：銷項金額(yr111_sales)、進項金額(yr111_purchases)、期末存貨(yr111_inventory)、
               存貨/銷項比(yr111_inv_sales_ratio)、存貨/進項比(yr111_inv_purch_ratio)、
               加值率(yr111_vat_rate)、實繳稅額(yr111_tax_paid)
    - 112年度：銷項金額(yr112_sales)、進項金額(yr112_purchases)、期末存貨(yr112_inventory)、
               存貨/銷項比(yr112_inv_sales_ratio)、存貨/進項比(yr112_inv_purch_ratio)、
               加值率(yr112_vat_rate)、實繳稅額(yr112_tax_paid)

    Args:
        ban: 統一編號（不區分大小寫），例如：BAN1111, ban1112, Ban1113
    """
    if not ban:
        raise ValueError("請提供統一編號(ban)")

    case_info = search_case_by_ban(ban)

    if case_info is None:
        raise ValueError(f"找不到統一編號 {ban} 的資料")

    return case_info


@mcp.tool()
def get_risk_rules(tax_type: Literal["營業稅", "貨物稅", "所得稅"]) -> dict:
    """
    取得指定稅務類型的風險稽核規則。
    輸入稅務類型（營業稅、貨物稅、所得稅），回傳該類型的所有風險規則資訊。

    每條規則包含：
    - rule_id: 規則編號
    - tax_type: 稅務類型
    - rule_name: 規則名稱
    - description: 規則描述
    - risk_category: 風險等級 (High/Medium/Low)
    - logic_condition: 邏輯判斷條件
    - suggested_action: 建議稽核行動

    Args:
        tax_type: 稅務類型，可選擇「營業稅」、「貨物稅」或「所得稅」

    Returns:
        包含規則列表的字典，若規則尚未建立則回傳提示訊息
    """
    if not tax_type:
        raise ValueError("請選擇稅務類型")

    rules = get_rules_by_tax_type(tax_type)

    if rules is None:
        return {
            "status": "not_available",
            "message": f"{tax_type}規則內容尚未建立，請先建立相關規則",
            "tax_type": tax_type,
            "rules": []
        }

    return {
        "status": "success",
        "tax_type": tax_type,
        "rule_count": len(rules),
        "rules": rules
    }


@mcp.tool()
async def web_search(query: str, num_results: int = 10) -> dict:
    """
    使用 Serper API 進行 Google 網路搜尋。
    需要在環境變數中設定 SERPER_API_KEY。

    Args:
        query: 搜尋關鍵字
        num_results: 返回結果數量，預設為 10，最大為 100

    Returns:
        搜尋結果，包含網頁標題、連結、摘要等資訊
    """
    if not query:
        raise ValueError("請提供搜尋關鍵字")

    api_key = os.getenv("SERPER_API_KEY")
    if not api_key:
        raise ValueError(
            "請在環境變數中設定 SERPER_API_KEY。"
            "可在 https://serper.dev 取得 API Key"
        )

    # 限制結果數量
    num_results = min(max(1, num_results), 100)

    try:
        async with httpx.AsyncClient() as client:
            response = await client.post(
                SERPER_API_URL,
                headers={
                    "X-API-KEY": api_key,
                    "Content-Type": "application/json"
                },
                json={
                    "q": query,
                    "num": num_results
                },
                timeout=30.0
            )
            response.raise_for_status()
            return response.json()
    except httpx.TimeoutException:
        raise ValueError("搜尋請求逾時，請稍後再試")
    except httpx.HTTPStatusError as e:
        if e.response.status_code == 401:
            raise ValueError("SERPER_API_KEY 無效，請確認 API Key 是否正確")
        elif e.response.status_code == 429:
            raise ValueError("API 請求次數超過限制，請稍後再試")
        else:
            raise ValueError(f"搜尋請求失敗: HTTP {e.response.status_code}")
    except Exception as e:
        raise ValueError(f"搜尋時發生錯誤: {str(e)}")


@mcp.tool()
def math_calculate(
    operation: Literal["add", "subtract", "multiply", "divide"],
    a: float,
    b: float
) -> dict:
    """
    執行基本數學運算。

    提供四種基本數學運算功能：
    - add: 加法運算 (a + b)
    - subtract: 減法運算 (a - b)
    - multiply: 乘法運算 (a × b)
    - divide: 除法運算 (a ÷ b)

    Args:
        operation: 運算類型，可選擇「add」、「subtract」、「multiply」或「divide」
        a: 第一個運算數
        b: 第二個運算數

    Returns:
        包含運算結果的字典，包含以下欄位：
        - operation: 執行的運算類型
        - operands: 運算數 (a, b)
        - result: 運算結果
        - expression: 運算式表達
    """
    if operation == "add":
        result = a + b
        symbol = "+"
    elif operation == "subtract":
        result = a - b
        symbol = "-"
    elif operation == "multiply":
        result = a * b
        symbol = "×"
    elif operation == "divide":
        if b == 0:
            raise ValueError("除數不能為零")
        result = a / b
        symbol = "÷"
    else:
        raise ValueError(f"不支援的運算類型: {operation}")

    return {
        "operation": operation,
        "operands": {"a": a, "b": b},
        "result": result,
        "expression": f"{a} {symbol} {b} = {result}"
    }


@mcp.tool()
async def search_knowledge_base(
    query: str,
    limit: int = 5,
    threshold: float = 0.5,
    metadata_filter: dict | None = None
) -> list[dict]:
    """
    根據輸入的查詢關鍵字，從向量知識庫中檢索最相關的文件片段。
    適用於查找查核指引、稅法解釋或過往案例。

    Args:
        query: 搜尋的關鍵字或自然語言問題
        limit: 回傳的結果數量上限 (預設 5)
        threshold: 相似度閾值 (0.0 ~ 1.0) (預設 0.5)
        metadata_filter: (選填) 針對 metadata 欄位的過濾條件 (目前版本暫未支援後端過濾)

    Returns:
        包含排序後的相關文件列表
    """
    # 1. Check Env Vars
    supabase_url = os.getenv("SUPABASE_URL")
    supabase_key = os.getenv("SUPABASE_KEY")
    azure_api_key = os.getenv("AZURE_OPENAI_KEY")
    azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
    azure_api_version = os.getenv("AZURE_OPENAI_API_VERSION", "2023-05-15")  # 預設值
    azure_embedding_deployment = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT")

    missing_vars = []
    if not supabase_url: missing_vars.append("SUPABASE_URL")
    if not supabase_key: missing_vars.append("SUPABASE_KEY")
    if not azure_api_key: missing_vars.append("AZURE_OPENAI_KEY")
    if not azure_endpoint: missing_vars.append("AZURE_OPENAI_ENDPOINT")
    if not azure_embedding_deployment: missing_vars.append("AZURE_OPENAI_EMBEDDING_DEPLOYMENT")

    if missing_vars:
        raise ValueError(f"缺少必要的環境變數: {', '.join(missing_vars)}")

    # 2. Generate Embedding
    try:
        openai_client = AsyncAzureOpenAI(
            api_key=azure_api_key,
            api_version=azure_api_version,
            azure_endpoint=azure_endpoint
        )

        embedding_response = await openai_client.embeddings.create(
            input=query,
            model=azure_embedding_deployment
        )
        embedding = embedding_response.data[0].embedding
    except Exception as e:
        raise ValueError(f"Embedding 生成失敗: {str(e)}")

    # 3. Call Supabase RPC
    try:
        supabase: Client = create_client(supabase_url, supabase_key)

        rpc_params = {
            "query_embedding": embedding,
            "match_threshold": threshold,
            "match_count": limit
        }

        # Note: metadata_filter is defined in spec but DB function support is pending

        response = supabase.rpc("match_documents", rpc_params).execute()

        # 4. Process Results
        results = []
        for item in response.data:
            results.append({
                "content": item.get("content"),
                "similarity": item.get("similarity"),
                "metadata": item.get("metadata")
            })

        return results

    except Exception as e:
        raise ValueError(f"Supabase 搜尋失敗: {str(e)}. 請確認資料庫中存在 'match_documents' RPC 函式。")


def main() -> None:
    """主程式進入點"""
    mcp.run()
