from __future__ import annotations

import secrets
import string
import time
from decimal import ROUND_HALF_UP, Decimal
from typing import Any
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit

from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession

from app.services import notification_plugin_service, provider_loader, system_setting_service


PAYMENT_METHOD_LABELS = {
    "alipay": "支付宝",
    "wxpay": "微信支付",
    "qqpay": "QQ钱包",
}


def yuan_to_cents(yuan: Decimal) -> int:
    return int((yuan * 100).to_integral_value(rounding=ROUND_HALF_UP))


def format_amount(yuan: Decimal) -> str:
    return str(yuan.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))


def gen_out_trade_no() -> str:
    ts = int(time.time() * 1000)
    rand = "".join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(6))
    return f"RC{ts}{rand}"


def gen_notify_token() -> str:
    return secrets.token_urlsafe(24)


def payment_method_from_plugin(plugin: dict[str, Any]) -> str:
    for capability in plugin.get("capabilities") or []:
        text = str(capability)
        if text.startswith("method:"):
            return text.split(":", 1)[1]
    return str(plugin.get("id") or "")


def _split_enabled(value: Any) -> list[str]:
    return [item.strip() for item in str(value or "").split(",") if item.strip()]


async def enabled_payment_plugins(db: AsyncSession) -> list[dict[str, Any]]:
    settings = await system_setting_service.ensure_system_settings(db)
    enabled_ids = _split_enabled(settings["payment.enabled_plugins"].value)
    if not enabled_ids:
        return []

    plugins = {str(item.get("id")): item for item in notification_plugin_service.list_plugins("payment")}
    rows: list[dict[str, Any]] = []
    for plugin_id in enabled_ids:
        plugin = plugins.get(plugin_id)
        if not plugin:
            continue
        method = payment_method_from_plugin(plugin)
        rows.append(
            {
                "id": plugin_id,
                "name": plugin.get("name") or plugin_id,
                "description": plugin.get("description") or "",
                "method": method,
                "method_label": PAYMENT_METHOD_LABELS.get(method, method),
                "capabilities": plugin.get("capabilities") or [],
            }
        )
    return rows


async def get_enabled_payment_provider(db: AsyncSession, plugin_id: str | None):
    options = await enabled_payment_plugins(db)
    if not options:
        raise HTTPException(status_code=400, detail="暂无可用支付方式")
    selected_id = plugin_id or str(options[0]["id"])
    if selected_id not in {str(item["id"]) for item in options}:
        raise HTTPException(status_code=400, detail="支付方式不可用或未启用")

    return await get_payment_provider(db, selected_id)


async def get_payment_provider(db: AsyncSession, plugin_id: str):
    plugin = notification_plugin_service.get_plugin("payment", plugin_id)
    config = await system_setting_service.get_payment_provider_config(db)
    try:
        provider = provider_loader.create_payment_provider(config, str(plugin["provider"]))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"支付插件加载失败：{exc}") from exc
    return provider, plugin, config


def _append_query(url: str, values: dict[str, str]) -> str:
    parts = urlsplit(url)
    query = dict(parse_qsl(parts.query, keep_blank_values=True))
    query.update(values)
    return urlunsplit((parts.scheme, parts.netloc, parts.path, urlencode(query), parts.fragment))


def build_notify_url(
    *,
    config: Any,
    plugin_id: str,
    token: str,
    backend_url: str,
    request_base_url: str,
) -> str:
    template = str(getattr(config, "PAYMENT_EPAY_NOTIFY_URL", "") or "").strip()
    if "{plugin_id}" in template or "{token}" in template:
        return template.format(plugin_id=plugin_id, token=token)

    if template:
        base = template.rstrip("/")
    else:
        public_base = (backend_url or request_base_url).rstrip("/")
        base = f"{public_base}/api/v1/recharge/notify"

    last_part = base.rsplit("/", 1)[-1]
    if not last_part.startswith("epay_"):
        base = f"{base}/{plugin_id}"
    return _append_query(base, {"token": token})


def build_return_url(*, config: Any, frontend_url: str) -> str:
    configured = str(getattr(config, "PAYMENT_EPAY_RETURN_URL", "") or "").strip()
    if configured:
        return configured
    return f"{frontend_url.rstrip('/')}/finance"
