from __future__ import annotations

from typing import Any

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

from app.core.crypto import decrypt_secret, encrypt_secret
from app.models.provision_interface import ProvisionInterface
from app.schemas.product import ProvisionInterfaceCreate, ProvisionInterfaceUpdate
from app.services import provision_plugin_service


_MASK = "********"


async def list_interfaces(db: AsyncSession, plugin_id: str | None = None) -> list[ProvisionInterface]:
    stmt = select(ProvisionInterface).order_by(
        ProvisionInterface.sort_order.asc(),
        ProvisionInterface.id.desc(),
    )
    if plugin_id:
        stmt = stmt.where(ProvisionInterface.plugin_id == plugin_id)
    res = await db.execute(stmt)
    return list(res.scalars().all())


async def get_interface(db: AsyncSession, interface_id: int) -> ProvisionInterface:
    res = await db.execute(select(ProvisionInterface).where(ProvisionInterface.id == interface_id))
    item = res.scalar_one_or_none()
    if not item:
        raise HTTPException(status_code=404, detail="开通接口不存在")
    return item


async def create_interface(db: AsyncSession, data: ProvisionInterfaceCreate) -> ProvisionInterface:
    plugin_id = _normalize_plugin_id(data.plugin_id)
    provision_plugin_service.ensure_plugin_exists(plugin_id)
    item = ProvisionInterface(
        name=data.name.strip(),
        plugin_id=plugin_id,
        config=_stored_config(plugin_id, data.config),
        enabled=data.enabled,
        sort_order=data.sort_order,
        remark=data.remark,
    )
    db.add(item)
    await db.flush()
    await db.refresh(item)
    return item


async def update_interface(db: AsyncSession, interface_id: int, data: ProvisionInterfaceUpdate) -> ProvisionInterface:
    item = await get_interface(db, interface_id)
    update = data.model_dump(exclude_unset=True)
    plugin_id = _normalize_plugin_id(update.get("plugin_id", item.plugin_id))
    if "plugin_id" in update:
        provision_plugin_service.ensure_plugin_exists(plugin_id)
        item.plugin_id = plugin_id
    if "name" in update and update["name"] is not None:
        item.name = str(update["name"]).strip()
    if "enabled" in update and update["enabled"] is not None:
        item.enabled = bool(update["enabled"])
    if "sort_order" in update and update["sort_order"] is not None:
        item.sort_order = int(update["sort_order"])
    if "remark" in update:
        item.remark = update["remark"]
    if "config" in update and isinstance(update["config"], dict):
        item.config = _stored_config(item.plugin_id, update["config"], item.config)
    await db.flush()
    await db.refresh(item)
    return item


async def delete_interface(db: AsyncSession, interface_id: int) -> None:
    item = await get_interface(db, interface_id)
    await db.delete(item)
    await db.flush()


async def test_interface(db: AsyncSession, interface_id: int) -> dict[str, Any]:
    item = await get_interface(db, interface_id)
    if not item.enabled:
        raise HTTPException(status_code=400, detail="开通接口已禁用")
    result = await provision_plugin_service.test_plugin(item.plugin_id, runtime_config(item))
    ok = result.get("ok") is not False
    item.last_test_ok = ok
    item.last_test_message = str(result.get("message") or ("测试通过" if ok else "测试失败"))[:255]
    await db.flush()
    return result


def public_interface(item: ProvisionInterface) -> dict[str, Any]:
    plugin_name = item.plugin_id
    try:
        manifest = provision_plugin_service.get_plugin_manifest(item.plugin_id)
        plugin_name = str(manifest.get("name") or item.plugin_id)
    except Exception:
        pass
    return {
        "id": item.id,
        "name": item.name,
        "plugin_id": item.plugin_id,
        "plugin_name": plugin_name,
        "config": masked_config(item),
        "enabled": item.enabled,
        "sort_order": item.sort_order,
        "remark": item.remark,
        "last_test_ok": item.last_test_ok,
        "last_test_message": item.last_test_message,
        "created_at": item.created_at,
        "updated_at": item.updated_at,
    }


def summary(item: ProvisionInterface | None) -> dict[str, Any] | None:
    if not item:
        return None
    data = public_interface(item)
    data.pop("config", None)
    return data


def masked_config(item: ProvisionInterface) -> dict[str, Any]:
    stored = item.config if isinstance(item.config, dict) else {}
    values = stored.get("values") if isinstance(stored.get("values"), dict) else {}
    secret_keys = set(stored.get("secret_keys") if isinstance(stored.get("secret_keys"), list) else [])
    out: dict[str, Any] = {}
    for key, value in values.items():
        out[key] = _MASK if key in secret_keys and value not in (None, "") else value
    return out


def runtime_config(item: ProvisionInterface | None) -> dict[str, Any]:
    if not item:
        return {}
    stored = item.config if isinstance(item.config, dict) else {}
    values = stored.get("values") if isinstance(stored.get("values"), dict) else {}
    secret_keys = set(stored.get("secret_keys") if isinstance(stored.get("secret_keys"), list) else [])
    out: dict[str, Any] = {}
    for key, value in values.items():
        if key in secret_keys and value not in (None, ""):
            try:
                out[key] = decrypt_secret(str(value))
            except Exception:
                out[key] = ""
        else:
            out[key] = value
    return out


async def runtime_config_for_product(db: AsyncSession, product: Any | None) -> dict[str, Any]:
    interface_id = getattr(product, "provision_interface_id", None) if product is not None else None
    if not interface_id:
        return {}
    item = await get_interface(db, int(interface_id))
    if not item.enabled:
        raise HTTPException(status_code=400, detail="产品绑定的开通接口已禁用")
    plugin_id = str(getattr(product, "api_identifier", "") or "")
    if plugin_id and item.plugin_id != plugin_id:
        raise HTTPException(status_code=400, detail="产品绑定的开通接口与插件不一致")
    return runtime_config(item)


async def ensure_product_interface(db: AsyncSession, interface_id: int | None, plugin_id: str | None) -> None:
    if not interface_id:
        return
    item = await get_interface(db, int(interface_id))
    if plugin_id and item.plugin_id != plugin_id:
        raise HTTPException(status_code=400, detail="开通接口所属插件与产品插件不一致")


def _stored_config(
    plugin_id: str,
    values: dict[str, Any],
    existing: dict[str, Any] | None = None,
) -> dict[str, Any]:
    existing = existing if isinstance(existing, dict) else {}
    existing_values = existing.get("values") if isinstance(existing.get("values"), dict) else {}
    existing_secret_keys = set(existing.get("secret_keys") if isinstance(existing.get("secret_keys"), list) else [])
    secret_keys = _secret_keys(plugin_id, values)
    stored_values: dict[str, Any] = {}
    all_keys = set(existing_values.keys()) | {str(k) for k in values.keys()}
    for key in sorted(all_keys):
        value = values.get(key)
        is_secret = key in secret_keys or key in existing_secret_keys
        if is_secret and (value in (None, "", _MASK)):
            if key in existing_values:
                stored_values[key] = existing_values[key]
                continue
        if value is None:
            continue
        stored_values[key] = encrypt_secret(str(value)) if is_secret and value != "" else value
    return {
        "values": stored_values,
        "secret_keys": sorted(secret_keys | existing_secret_keys),
    }


def _secret_keys(plugin_id: str, values: dict[str, Any]) -> set[str]:
    keys: set[str] = set()
    try:
        manifest = provision_plugin_service.get_plugin_manifest(plugin_id)
    except Exception:
        manifest = {}
    for field in manifest.get("config_fields") or []:
        if not isinstance(field, dict):
            continue
        key = str(field.get("key") or "").strip()
        field_type = str(field.get("type") or "").lower()
        if key and (field.get("secret") or field_type == "password"):
            keys.add(key)
    for key in values.keys():
        text = str(key).lower()
        if "secret" in text or "password" in text or text.endswith("_key") or text in {"api_key", "mn_key", "mn_keye"}:
            keys.add(str(key))
    return keys


def _normalize_plugin_id(value: Any) -> str:
    plugin_id = str(value or "").strip()
    if not plugin_id:
        raise HTTPException(status_code=400, detail="请选择开通插件")
    return plugin_id
