from typing import Any, Dict
from fastapi import APIRouter, Depends, HTTPException, Header
from prisma import Prisma

from turbo_agent_auth.db import client
from turbo_agent_auth.config.runtime import get_callback_url, get_ip_allow_list, get_api_key
from turbo_agent_auth.services.auth_service import AuthService, default_auth_schemas
from turbo_agent_auth.schemas import (
    PlatformCreate,
    PlatformUpdate,
    PlatformOut,
    AuthMethodCreate,
    AuthMethodUpdate,
    AuthMethodOut,
    SecretUpsert,
    SecretOut,
    SecretUpdate,
    SecretRemindRequest,
    SecretLoginRequest,
    ParseResponseRequest,
    ParseResponseResult,
    DefaultSchemaOut,
    AuthType,
    CallbackInfo,
    IpAllowListInfo,
    SecretLoginWithExecution,
    SecretInvalidateRequest,
    SecretInvalidNotifiedRequest,
    PreAuthRequest,
    LoginExecutionResult,
)


def get_service() -> AuthService:
    if not client.is_connected():
        # FastAPI startup 应该已连接，双保险
        raise HTTPException(status_code=500, detail="数据库未连接")
    return AuthService(client)


async def _require_api_key(authorization: str | None = Header(None)) -> None:
    """若配置了 API Key，则校验 `Authorization: Bearer <key>`。

    未配置时直接放行；格式不匹配或密钥不一致返回 401。
    """
    key = get_api_key()
    if not key:
        return
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Invalid API key")
    token = authorization[len("Bearer "):].strip()
    if token != key:
        raise HTTPException(status_code=401, detail="Invalid API key")


router = APIRouter(prefix="/v1", tags=["core"], dependencies=[Depends(_require_api_key)])


@router.get("/ping", summary="Ping test")
async def ping():
    return {"pong": True}


@router.get("/runtime/callback-url", response_model=CallbackInfo, summary="获取服务回调地址")
async def runtime_callback_url():
    callback = get_callback_url()
    return CallbackInfo(callbackUrl=callback, configured=bool(callback))


@router.get("/runtime/ip-allowlist", response_model=IpAllowListInfo, summary="获取出口 IP 白名单")
async def runtime_ip_allowlist():
    ips = get_ip_allow_list()
    return IpAllowListInfo(ipAllowList=ips, configured=bool(ips))


# 平台 CRUD
@router.post("/platforms", response_model=PlatformOut, summary="创建平台")
async def create_platform(payload: PlatformCreate, svc: AuthService = Depends(get_service)):
    p = await svc.create_platform(payload)
    return PlatformOut(**p.model_dump())


@router.get("/platforms", response_model=list[PlatformOut], summary="平台列表")
async def list_platforms(svc: AuthService = Depends(get_service)):
    items = await svc.list_platforms()
    return [PlatformOut(**i.model_dump()) for i in items]


@router.get("/platforms/{platform_id}", response_model=PlatformOut, summary="平台详情")
async def get_platform(platform_id: str, svc: AuthService = Depends(get_service)):
    p = await svc.get_platform(platform_id)
    if not p:
        raise HTTPException(status_code=404, detail="平台不存在")
    return PlatformOut(**p.model_dump())


@router.patch("/platforms/{platform_id}", response_model=PlatformOut, summary="更新平台")
async def update_platform(platform_id: str, payload: PlatformUpdate, svc: AuthService = Depends(get_service)):
    p = await svc.update_platform(platform_id, payload)
    if not p:
        raise HTTPException(status_code=404, detail="平台不存在")
    return PlatformOut(**p.model_dump())


# 授权方式 CRUD
@router.post("/auth-methods", response_model=AuthMethodOut, summary="创建授权方式")
async def create_auth_method(payload: AuthMethodCreate, svc: AuthService = Depends(get_service)):
    try:
        am = await svc.create_auth_method(payload)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    return AuthMethodOut(**am.model_dump())


@router.get("/auth-methods/{auth_method_id}", response_model=AuthMethodOut, summary="授权方式详情")
async def get_auth_method(auth_method_id: str, svc: AuthService = Depends(get_service)):
    am = await svc.get_auth_method(auth_method_id)
    if not am:
        raise HTTPException(status_code=404, detail="授权方式不存在")
    return AuthMethodOut(**am.model_dump())


@router.get("/platforms/{platform_id}/auth-methods", response_model=list[AuthMethodOut], summary="平台下授权方式列表")
async def list_auth_methods(platform_id: str, svc: AuthService = Depends(get_service)):
    items = await svc.list_auth_methods(platform_id)
    return [AuthMethodOut(**i.model_dump()) for i in items]


@router.patch("/auth-methods/{auth_method_id}", response_model=AuthMethodOut, summary="更新授权方式")
async def update_auth_method(auth_method_id: str, payload: AuthMethodUpdate, svc: AuthService = Depends(get_service)):
    am = await svc.update_auth_method(auth_method_id, payload)
    if not am:
        raise HTTPException(status_code=404, detail="授权方式不存在")
    return AuthMethodOut(**am.model_dump())


@router.post("/auth-methods/{auth_method_id}/pre-auth", response_model=LoginExecutionResult, summary="执行预授权登录（不创建秘钥）")
async def pre_auth_login_flow(
    auth_method_id: str,
    payload: PreAuthRequest,
    svc: AuthService = Depends(get_service)
):
    """
    使用提供的登录字段执行预授权登录流程。
    此操作不会创建 Secret 记录，仅返回执行结果和提取的数据（如 Token/Cookies）。
    """
    am = await svc.get_auth_method(auth_method_id)
    if not am:
        raise HTTPException(status_code=404, detail="授权方式不存在")
    
    try:
        result = await svc.pre_auth_login_flow(am, payload.loginPayload, payload.proxy)
        return result
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


# 默认schema查询
@router.get("/auth-types/default-schemas", response_model=list[DefaultSchemaOut], summary="获取所有授权类型默认schema")
async def list_default_schemas():
    result: list[DefaultSchemaOut] = []
    for t in AuthType:
        schema = default_auth_schemas.get(t.value)
        if schema:
            result.append(
                DefaultSchemaOut(
                    authType=t,
                    description=schema.get("description"),
                    loginFlow=schema.get("loginFlow"),
                    loginFieldsSchema=schema.get("loginFieldsSchema"),
                    refreshFlow=schema.get("refreshFlow"),
                    refreshFieldsSchema=schema.get("refreshFieldsSchema"),
                    authFieldsSchema=schema.get("authFieldsSchema"),
                    authFieldPlacements=schema.get("authFieldPlacements"),
                    responseMapping=schema.get("responseMapping"),
                )
            )
    return result


@router.get("/auth-types/default-schemas/{auth_type}", response_model=DefaultSchemaOut, summary="按类型获取默认schema")
async def get_default_auth_schema(auth_type: AuthType):
    """
    获取指定授权类型的默认字段定义、配置和响应映射。
    """
    schema = default_auth_schemas.get(auth_type.value)
    if not schema:
        raise HTTPException(status_code=404, detail=f"Default schema for auth type '{auth_type}' not found.")
    return DefaultSchemaOut(
        authType=auth_type,
        description=schema.get("description"),
        loginFlow=schema.get("loginFlow"),
        loginFieldsSchema=schema.get("loginFieldsSchema"),
        refreshFlow=schema.get("refreshFlow"),
        refreshFieldsSchema=schema.get("refreshFieldsSchema"),
        authFieldsSchema=schema.get("authFieldsSchema"),
        authFieldPlacements=schema.get("authFieldPlacements"),
        responseMapping=schema.get("responseMapping"),
    )

async def _secret_with_usage_mapping(svc: AuthService, s) -> SecretOut:
    data = s.model_dump()
    am = await svc.get_auth_method(s.authMethodId)
    usage = am.authFieldPlacements if (am and isinstance(am.authFieldPlacements, dict)) else None
    data["usageMapping"] = usage
    if am and am.type:
        data["type"] = am.type
    # 回填 isValid 缺省（兼容尚未生成最新 Prisma Client 的情况）
    if "isValid" not in data:
        data["isValid"] = True
    return SecretOut(**data)


# 秘钥 Upsert / 查询
@router.post("/auth-methods/{auth_method_id}/secret", response_model=SecretLoginWithExecution, summary="创建或更新秘钥并执行首次授权，返回执行详情")
async def upsert_secret(auth_method_id: str, payload: SecretUpsert, proxy: str | None = None, svc: AuthService = Depends(get_service)):
    am = await svc.get_auth_method(auth_method_id)
    if not am:
        raise HTTPException(status_code=404, detail="授权方式不存在")
    try:
        s = await svc.upsert_secret(am, payload)
        exec_result = None
        # 始终尝试首次登录（需要 loginPayload）
        if payload.loginPayload is not None and am.loginFlow:
            try:
                s, exec_result = await svc.perform_login_with_trace(
                    s.id,
                    login_payload=payload.loginPayload,
                    store_login_payload=bool(payload.autoLoginEnabled),
                    proxy=proxy,
                )
            except ValueError as le:
                raise HTTPException(status_code=400, detail=str(le))
        if exec_result is None:
            # 未执行登录构造空结果
            exec_result = {
                "steps": [],
                "finalExtracted": {},
                "finalFieldSources": {},
                "finalExpiresAt": None,
                "success": False,
                "error": "首次授权未执行（缺少 loginPayload 或未配置 loginFlow）",
            }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    s_out = await _secret_with_usage_mapping(svc, s)
    return SecretLoginWithExecution(secret=s_out, execution=exec_result)

@router.get("/auth-methods/{auth_method_id}/secret", response_model=SecretOut, summary="查询秘钥（可按名称）")
async def get_secret(auth_method_id: str, name: str | None = None, svc: AuthService = Depends(get_service)):
    s = await svc.get_secret(auth_method_id, name=name)
    if not s:
        raise HTTPException(status_code=404, detail="秘钥不存在")
    return await _secret_with_usage_mapping(svc, s)


@router.get("/auth-methods/{auth_method_id}/secrets", response_model=list[SecretOut], summary="列出授权方式下的所有秘钥")
async def list_secrets(auth_method_id: str, svc: AuthService = Depends(get_service)):
    items = await svc.list_secrets(auth_method_id)
    result: list[SecretOut] = []
    for i in items:
        result.append(await _secret_with_usage_mapping(svc, i))
    return result


@router.get("/secrets/{secret_id}", response_model=SecretOut, summary="根据ID获取秘钥")
async def get_secret_by_id(secret_id: str, svc: AuthService = Depends(get_service)):
    s = await svc.get_secret_by_id(secret_id)
    if not s:
        raise HTTPException(status_code=404, detail="秘钥不存在")
    return await _secret_with_usage_mapping(svc, s)


@router.patch("/secrets/{secret_id}", response_model=SecretOut, summary="更新秘钥")
async def update_secret(secret_id: str, payload: SecretUpdate, svc: AuthService = Depends(get_service)):
    data: Dict[str, Any] = {k: v for k, v in payload.model_dump(exclude_none=True).items()}
    try:
        s = await svc.update_secret(secret_id, data)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    if not s:
        raise HTTPException(status_code=404, detail="秘钥不存在")
    run_login = False
    if payload.autoLoginEnabled is True:
        run_login = True
    elif payload.autoLoginEnabled is None and payload.loginPayload is not None and getattr(s, "autoLoginEnabled", False):
        run_login = True
    if run_login:
        try:
            s = await svc.perform_login(secret_id, login_payload=payload.loginPayload, store_login_payload=True)
        except ValueError as e:
            raise HTTPException(status_code=400, detail=str(e))
    return await _secret_with_usage_mapping(svc, s)


@router.post("/secrets/{secret_id}/remind", response_model=SecretOut, summary="记录提醒并可更新提醒邮箱（示例，占位）")
async def remind_secret(secret_id: str, payload: SecretRemindRequest, svc: AuthService = Depends(get_service)):
    s = await svc.remind_secret(secret_id, payload)
    if not s:
        raise HTTPException(status_code=404, detail="秘钥不存在")
    return await _secret_with_usage_mapping(svc, s)


@router.post("/secrets/{secret_id}/login", response_model=SecretOut, summary="执行登录流程并更新秘钥")
async def login_secret(secret_id: str, payload: SecretLoginRequest | None = None, svc: AuthService = Depends(get_service)):
    payload = payload or SecretLoginRequest()
    try:
        s = await svc.perform_login(
            secret_id,
            login_payload=payload.loginPayload,
            store_login_payload=payload.storeLoginPayload,
            proxy=payload.proxy,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    return await _secret_with_usage_mapping(svc, s)


@router.post("/secrets/{secret_id}/login/trace", response_model=SecretLoginWithExecution, summary="执行登录并返回各阶段执行情况")
async def login_secret_with_trace(secret_id: str, payload: SecretLoginRequest | None = None, svc: AuthService = Depends(get_service)):
    payload = payload or SecretLoginRequest()
    try:
        s, exec_result = await svc.perform_login_with_trace(
            secret_id,
            login_payload=payload.loginPayload,
            store_login_payload=payload.storeLoginPayload,
            proxy=payload.proxy,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    s_out = await _secret_with_usage_mapping(svc, s)
    return SecretLoginWithExecution(secret=s_out, execution=exec_result)


@router.patch("/secrets/{secret_id}/with-execution", response_model=SecretLoginWithExecution, summary="更新秘钥并返回登录执行详情")
async def update_secret_with_execution(secret_id: str, payload: SecretUpdate, proxy: str | None = None, svc: AuthService = Depends(get_service)):
    data: Dict[str, Any] = {k: v for k, v in payload.model_dump(exclude_none=True).items()}
    try:
        s = await svc.update_secret(secret_id, data)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    if not s:
        raise HTTPException(status_code=404, detail="秘钥不存在")

    run_login = False
    if payload.autoLoginEnabled is True:
        run_login = True
    elif payload.autoLoginEnabled is None and payload.loginPayload is not None and getattr(s, "autoLoginEnabled", False):
        run_login = True

    if not run_login:
        s_out = await _secret_with_usage_mapping(svc, s)
        return SecretLoginWithExecution(secret=s_out, execution={"steps": [], "finalExtracted": {}, "finalFieldSources": {}, "finalExpiresAt": None, "success": False, "error": "login not executed"})

    try:
        s2, exec_result = await svc.perform_login_with_trace(secret_id, login_payload=payload.loginPayload, store_login_payload=True, proxy=proxy)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    s_out = await _secret_with_usage_mapping(svc, s2)
    return SecretLoginWithExecution(secret=s_out, execution=exec_result)


@router.delete("/secrets/{secret_id}", summary="删除秘钥")
async def delete_secret(secret_id: str, svc: AuthService = Depends(get_service)):
    ok = await svc.delete_secret(secret_id)
    if not ok:
        raise HTTPException(status_code=404, detail="秘钥不存在")
    return {"deleted": True}


@router.post("/secrets/{secret_id}/invalidate", response_model=SecretOut, summary="外部标记秘钥为失效")
async def invalidate_secret(secret_id: str, payload: SecretInvalidateRequest | None = None, svc: AuthService = Depends(get_service)):
    payload = payload or SecretInvalidateRequest()
    s = await svc.invalidate_secret(secret_id, payload.reason)
    if not s:
        raise HTTPException(status_code=404, detail="秘钥不存在")
    return await _secret_with_usage_mapping(svc, s)


@router.post("/secrets/{secret_id}/invalid-notified", response_model=SecretOut, summary="标记失效通知状态")
async def mark_invalid_notified(secret_id: str, payload: SecretInvalidNotifiedRequest, svc: AuthService = Depends(get_service)):
    s = await svc.mark_invalid_notified(secret_id, payload.notified)
    if not s:
        raise HTTPException(status_code=404, detail="秘钥不存在")
    return await _secret_with_usage_mapping(svc, s)


# 解析接口返回（抽取token等）
@router.post("/auth-methods/{auth_method_id}/parse", response_model=ParseResponseResult, summary="解析授权接口返回")
async def parse_response(
    auth_method_id: str,
    body: Dict[str, Any] | None = None,
    headers: Dict[str, Any] | None = None,
    cookies: Dict[str, Any] | None = None,
    rawBody: str | None = None,
    svc: AuthService = Depends(get_service),
):
    req = ParseResponseRequest(
        authMethodId=auth_method_id,
        body=body or {},
        headers=headers or {},
        cookies=cookies or {},
        rawBody=rawBody,
    )
    try:
        result = await svc.parse_response(req)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    return result
