from __future__ import annotations

import json
import secrets

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import delete, func, or_, select
from sqlalchemy.orm import Session

from app.api.pagination import page_result
from app.core.database import get_db
from app.core.dependencies import get_current_admin
from app.core.responses import api_success
from app.models.admin import Admin
from app.models.ai_config import SystemConfig
from app.models.entitlement import EntitlementPlan
from app.models.sso import SsoClient, SsoLoginAudit, UserExternalIdentity
from app.models.user import User
from app.schemas.sso import SsoClientSaveRequest, SsoClientUpdateRequest, SsoPublicConfigRequest
from app.services.admin_service import OperationLogService
from app.services.secret_service import SecretService
from app.services.sso_service import _json_list


router = APIRouter()


@router.get("/sso/config")
def get_sso_config(
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    value = db.scalar(
        select(SystemConfig.config_value).where(SystemConfig.config_key == "sso_user_client_url")
    )
    return api_success({"userClientUrl": value or ""})


@router.put("/sso/config")
def save_sso_config(
    payload: SsoPublicConfigRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    config = db.scalar(
        select(SystemConfig).where(SystemConfig.config_key == "sso_user_client_url")
    )
    if config is None:
        config = SystemConfig(
            config_key="sso_user_client_url",
            config_value=payload.userClientUrl,
            description="其他应用完成免登录后进入的千问千答用户端公网地址。",
            updated_by=current_admin.id,
        )
        db.add(config)
    else:
        config.config_value = payload.userClientUrl
        config.updated_by = current_admin.id
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="sso",
        action="save_public_url",
        target_id=config.id,
    )
    db.commit()
    return api_success({"userClientUrl": payload.userClientUrl})


@router.get("/sso/client/list")
def list_sso_clients(
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    identity_counts = (
        select(
            UserExternalIdentity.client_id.label("client_id"),
            func.count(UserExternalIdentity.id).label("identity_count"),
        )
        .group_by(UserExternalIdentity.client_id)
        .subquery()
    )
    rows = db.execute(
        select(SsoClient, func.coalesce(identity_counts.c.identity_count, 0), EntitlementPlan.name)
        .outerjoin(identity_counts, identity_counts.c.client_id == SsoClient.id)
        .outerjoin(EntitlementPlan, EntitlementPlan.id == SsoClient.default_entitlement_plan_id)
        .order_by(SsoClient.created_at.desc(), SsoClient.id.desc())
    ).all()
    return api_success([_client_item(client, int(identity_count), plan_name) for client, identity_count, plan_name in rows])


@router.post("/sso/client")
def create_sso_client(
    payload: SsoClientSaveRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    app_id = payload.appId.strip()
    if db.scalar(select(SsoClient.id).where(SsoClient.app_id == app_id)) is not None:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="应用ID已存在")
    _validate_default_plan(db, payload.allowAutoRegister, payload.defaultEntitlementPlanId)
    plaintext_secret = secrets.token_urlsafe(32)
    client = SsoClient(
        app_id=app_id,
        name=payload.name.strip(),
        client_secret=SecretService.encrypt(plaintext_secret),
        redirect_uris=json.dumps(payload.redirectUris, ensure_ascii=False),
        allow_auto_register=1 if payload.allowAutoRegister else 0,
        default_entitlement_plan_id=payload.defaultEntitlementPlanId,
        status=payload.status,
        created_by=current_admin.id,
    )
    db.add(client)
    db.flush()
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="sso",
        action="create_client",
        target_id=client.id,
    )
    db.commit()
    db.refresh(client)
    return api_success({**_client_item(client, 0), "clientSecret": plaintext_secret})


@router.put("/sso/client/{client_id}")
def update_sso_client(
    client_id: int,
    payload: SsoClientUpdateRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    client = _require_client(db, client_id)
    _validate_default_plan(db, payload.allowAutoRegister, payload.defaultEntitlementPlanId)
    client.name = payload.name.strip()
    client.redirect_uris = json.dumps(payload.redirectUris, ensure_ascii=False)
    client.allow_auto_register = 1 if payload.allowAutoRegister else 0
    client.default_entitlement_plan_id = payload.defaultEntitlementPlanId
    client.status = payload.status
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="sso",
        action="update_client",
        target_id=client.id,
    )
    db.commit()
    identity_count = db.scalar(
        select(func.count(UserExternalIdentity.id)).where(UserExternalIdentity.client_id == client.id)
    ) or 0
    plan_name = db.scalar(select(EntitlementPlan.name).where(EntitlementPlan.id == client.default_entitlement_plan_id))
    return api_success(_client_item(client, identity_count, plan_name))


@router.post("/sso/client/{client_id}/secret/rotate")
def rotate_sso_client_secret(
    client_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    client = _require_client(db, client_id)
    plaintext_secret = secrets.token_urlsafe(32)
    client.client_secret = SecretService.encrypt(plaintext_secret)
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="sso",
        action="rotate_secret",
        target_id=client.id,
    )
    db.commit()
    return api_success({"clientId": client.id, "clientSecret": plaintext_secret})


@router.get("/sso/identity/list")
def list_sso_identities(
    clientId: int | None = Query(default=None, gt=0),
    keyword: str = Query(default="", max_length=100),
    page: int = Query(default=1, ge=1),
    pageSize: int = Query(default=10, ge=10, le=100),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    filters = []
    if clientId:
        filters.append(UserExternalIdentity.client_id == clientId)
    if keyword.strip():
        value = f"%{keyword.strip()}%"
        filters.append(
            or_(
                UserExternalIdentity.external_user_id.ilike(value),
                User.phone.ilike(value),
                User.name.ilike(value),
                SsoClient.name.ilike(value),
            )
        )
    total = db.scalar(
        select(func.count(UserExternalIdentity.id))
        .join(SsoClient, SsoClient.id == UserExternalIdentity.client_id)
        .join(User, User.id == UserExternalIdentity.user_id)
        .where(*filters)
    ) or 0
    rows = db.execute(
        select(UserExternalIdentity, SsoClient, User)
        .join(SsoClient, SsoClient.id == UserExternalIdentity.client_id)
        .join(User, User.id == UserExternalIdentity.user_id)
        .where(*filters)
        .order_by(UserExternalIdentity.last_login_at.desc(), UserExternalIdentity.id.desc())
        .offset((page - 1) * pageSize)
        .limit(pageSize)
    ).all()
    items = [_identity_item(identity, client, user) for identity, client, user in rows]
    return api_success(page_result(items, total=total, page=page, page_size=pageSize))


@router.delete("/sso/identity/{identity_id}")
def delete_sso_identity(
    identity_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    identity = db.get(UserExternalIdentity, identity_id)
    if identity is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="账号绑定不存在")
    db.execute(delete(UserExternalIdentity).where(UserExternalIdentity.id == identity_id))
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="sso",
        action="unlink_identity",
        target_id=identity_id,
    )
    db.commit()
    return api_success()


@router.get("/sso/audit/list")
def list_sso_audits(
    clientId: int | None = Query(default=None, gt=0),
    auditStatus: str = Query(default="", max_length=20),
    page: int = Query(default=1, ge=1),
    pageSize: int = Query(default=10, ge=10, le=100),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    filters = []
    if clientId:
        filters.append(SsoLoginAudit.client_id == clientId)
    if auditStatus:
        filters.append(SsoLoginAudit.status == auditStatus.upper())
    total = db.scalar(select(func.count(SsoLoginAudit.id)).where(*filters)) or 0
    rows = db.execute(
        select(SsoLoginAudit, SsoClient)
        .outerjoin(SsoClient, SsoClient.id == SsoLoginAudit.client_id)
        .where(*filters)
        .order_by(SsoLoginAudit.created_at.desc(), SsoLoginAudit.id.desc())
        .offset((page - 1) * pageSize)
        .limit(pageSize)
    ).all()
    items = [_audit_item(audit, client) for audit, client in rows]
    return api_success(page_result(items, total=total, page=page, page_size=pageSize))


def _require_client(db: Session, client_id: int) -> SsoClient:
    client = db.get(SsoClient, client_id)
    if client is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="接入应用不存在")
    return client


def _client_item(client: SsoClient, identity_count: int, plan_name: str | None = None) -> dict:
    return {
        "id": client.id,
        "appId": client.app_id,
        "name": client.name,
        "redirectUris": _json_list(client.redirect_uris),
        "allowAutoRegister": bool(client.allow_auto_register),
        "defaultEntitlementPlanId": client.default_entitlement_plan_id,
        "defaultEntitlementPlanName": plan_name,
        "status": client.status,
        "identityCount": identity_count,
        "lastUsedAt": client.last_used_at,
        "createdAt": client.created_at,
        "updatedAt": client.updated_at,
    }


def _validate_default_plan(db: Session, allow_auto_register: bool, plan_id: int | None) -> None:
    if not allow_auto_register:
        return
    plan = db.get(EntitlementPlan, plan_id) if plan_id else None
    if plan is None or plan.status != 1 or plan.plan_type == "teacher":
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="默认权益版本不存在或已停用")


def _identity_item(identity: UserExternalIdentity, client: SsoClient, user: User) -> dict:
    return {
        "id": identity.id,
        "clientId": client.id,
        "clientName": client.name,
        "appId": client.app_id,
        "userId": user.id,
        "userName": user.name,
        "phone": user.phone,
        "externalUserId": identity.external_user_id,
        "displayNameSnapshot": identity.display_name_snapshot,
        "lastLoginAt": identity.last_login_at,
        "createdAt": identity.created_at,
    }


def _audit_item(audit: SsoLoginAudit, client: SsoClient | None) -> dict:
    return {
        "id": audit.id,
        "appId": client.app_id if client else None,
        "clientName": client.name if client else None,
        "userId": audit.user_id,
        "externalUserId": audit.external_user_id,
        "action": audit.action,
        "status": audit.status,
        "errorMessage": audit.error_message,
        "ip": audit.ip,
        "createdAt": audit.created_at,
    }
