"""货币转换服务

功能：
  1. 列出租户已配置货币
  2. 任意货币之间的金额转换（基于存储汇率）
  3. 从外部 API 拉取实时汇率（open.er-api.com，免费无需 key）
  4. 批量刷新租户所有 auto 模式货币的汇率

汇率约定：
  exchange_rate = 1 基准货币能兑换多少本货币
  基准货币：is_default=True，exchange_rate=1.0
  转换公式（基准→目标）：amount * target.exchange_rate
  转换公式（目标→基准）：amount / target.exchange_rate
  跨币种：amount_a / A.rate * B.rate
"""
from __future__ import annotations

import logging
from datetime import datetime, timezone
from decimal import Decimal, ROUND_HALF_UP
from typing import Optional

import httpx
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.models.currency import Currency

logger = logging.getLogger(__name__)

_RATE_API_URL = "https://open.er-api.com/v6/latest/{base}"
_REQUEST_TIMEOUT = 10  # seconds


class CurrencyService:

    # ── 查询 ──────────────────────────────────────────────────────────────────

    @staticmethod
    async def get_all(db: AsyncSession, tenant_id: int) -> list[Currency]:
        result = await db.execute(
            select(Currency)
            .where(Currency.tenant_id == tenant_id)
            .order_by(Currency.is_default.desc(), Currency.code)
        )
        return list(result.scalars().all())

    @staticmethod
    async def get_active(db: AsyncSession, tenant_id: int) -> list[Currency]:
        result = await db.execute(
            select(Currency)
            .where(Currency.tenant_id == tenant_id, Currency.is_active == True)
            .order_by(Currency.is_default.desc(), Currency.code)
        )
        return list(result.scalars().all())

    @staticmethod
    async def get_default(db: AsyncSession, tenant_id: int) -> Optional[Currency]:
        result = await db.execute(
            select(Currency).where(
                Currency.tenant_id == tenant_id,
                Currency.is_default == True,
            )
        )
        return result.scalar_one_or_none()

    @staticmethod
    async def get_by_code(
        db: AsyncSession, tenant_id: int, code: str
    ) -> Optional[Currency]:
        result = await db.execute(
            select(Currency).where(
                Currency.tenant_id == tenant_id,
                Currency.code == code.upper(),
            )
        )
        return result.scalar_one_or_none()

    # ── 金额转换 ──────────────────────────────────────────────────────────────

    @staticmethod
    def convert_amount(
        amount: Decimal,
        from_rate: Decimal,
        to_rate: Decimal,
        decimal_places: int = 2,
    ) -> Decimal:
        """根据两个货币相对基准货币的汇率做转换，结果四舍五入到指定小数位。"""
        if from_rate == Decimal("0"):
            raise ValueError("源货币汇率不能为 0")
        converted = amount / from_rate * to_rate
        quant = Decimal(10) ** -decimal_places
        return converted.quantize(quant, rounding=ROUND_HALF_UP)

    @classmethod
    async def convert(
        cls,
        db: AsyncSession,
        amount: Decimal,
        from_code: str,
        to_code: str,
        tenant_id: int,
        decimal_places: int = 2,
    ) -> Decimal:
        """从 from_code 货币转换到 to_code 货币。"""
        if from_code.upper() == to_code.upper():
            return amount

        from_cur = await CurrencyService.get_by_code(db, tenant_id, from_code)
        to_cur = await CurrencyService.get_by_code(db, tenant_id, to_code)

        if not from_cur:
            raise ValueError(f"货币 {from_code} 未在租户中配置")
        if not to_cur:
            raise ValueError(f"货币 {to_code} 未在租户中配置")

        return CurrencyService.convert_amount(
            amount, from_cur.exchange_rate, to_cur.exchange_rate, decimal_places
        )

    # ── 实时汇率获取 ──────────────────────────────────────────────────────────

    @staticmethod
    async def fetch_live_rates(base_code: str) -> dict[str, float]:
        """从 open.er-api.com 获取以 base_code 为基准的所有汇率。
        返回 {"USD": 0.138, "EUR": 0.127, ...}
        """
        url = _RATE_API_URL.format(base=base_code.upper())
        async with httpx.AsyncClient(timeout=_REQUEST_TIMEOUT) as client:
            resp = await client.get(url)
            resp.raise_for_status()
            data = resp.json()

        if data.get("result") != "success":
            raise RuntimeError(f"汇率 API 返回错误：{data.get('error-type', '未知')}")

        return data.get("rates", {})

    @classmethod
    async def refresh_auto_rates(
        cls, db: AsyncSession, tenant_id: int
    ) -> dict:
        """刷新该租户所有 rate_mode='auto' 的货币汇率。
        返回 {"updated": [...], "skipped": [...], "error": str|None}
        """
        currencies = await cls.get_all(db, tenant_id)
        default_cur = next((c for c in currencies if c.is_default), None)
        auto_currencies = [c for c in currencies if c.rate_mode == "auto" and not c.is_default]

        if not default_cur:
            return {"updated": [], "skipped": [], "error": "未设置基准货币"}
        if not auto_currencies:
            return {"updated": [], "skipped": [], "error": None}

        try:
            rates = await cls.fetch_live_rates(default_cur.code)
        except Exception as e:
            logger.error(f"[CurrencyService] 获取汇率失败: {e}")
            return {"updated": [], "skipped": [c.code for c in auto_currencies], "error": str(e)}

        updated, skipped = [], []
        now = datetime.now(timezone.utc).replace(tzinfo=None)

        for cur in auto_currencies:
            rate = rates.get(cur.code.upper())
            if rate is None:
                skipped.append(cur.code)
                logger.warning(f"[CurrencyService] API 未返回 {cur.code} 的汇率")
                continue
            cur.exchange_rate = Decimal(str(rate))
            cur.last_fetched_at = now
            updated.append(cur.code)

        await db.commit()
        return {"updated": updated, "skipped": skipped, "error": None}
