from __future__ import annotations

import json
import re
from typing import Any

import bcrypt
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from sqlalchemy import delete, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.api.deps import get_admin_user, get_db, require_permission
from app.core.models.permission import AdminRole, AdminUserPermissionOverride, AdminUserRole
from app.core.models.user import User
from app.core.permissions.catalog import catalog_by_module, permission_keys
from app.core.services.permission_service import PermissionService

router = APIRouter(prefix="/admin", tags=["后台权限"])


class RoleIn(BaseModel):
    name: str = Field(..., min_length=1, max_length=120)
    code: str | None = Field(None, max_length=64)
    description: str | None = Field(None, max_length=500)
    is_active: bool = True


class RolePermissionsIn(BaseModel):
    permissions: list[dict[str, Any]] = Field(default_factory=list)


class UserRolesIn(BaseModel):
    role_ids: list[int] = Field(default_factory=list)


class UserOverridesIn(BaseModel):
    overrides: list[dict[str, Any]] = Field(default_factory=list)


class AdminUserCreateIn(BaseModel):
    email: str = Field(..., min_length=3, max_length=255)
    password: str = Field(..., min_length=6, max_length=128)
    name: str | None = Field(None, max_length=100)
    role: str = Field("staff", max_length=30)
    is_active: bool = True


class AdminUserUpdateIn(BaseModel):
    email: str | None = Field(None, min_length=3, max_length=255)
    password: str | None = Field(None, min_length=6, max_length=128)
    name: str | None = Field(None, max_length=100)
    role: str | None = Field(None, max_length=30)
    is_active: bool | None = None


ADMIN_USER_ROLES = {"owner", "admin", "staff"}


def _slugify(text: str) -> str:
    value = re.sub(r"[^a-zA-Z0-9_]+", "_", text.strip().lower()).strip("_")
    return value or "custom_role"


def _name_from_profile(user: User) -> str:
    profile = user.profile or {}
    if isinstance(profile, str):
        try:
            profile = json.loads(profile)
        except Exception:
            profile = {}
    return profile.get("Name") or profile.get("name") or user.email.split("@")[0]


def _profile_with_name(profile: dict | str | None, name: str | None) -> dict | None:
    if profile is None:
        profile_data: dict[str, Any] = {}
    elif isinstance(profile, str):
        try:
            profile_data = json.loads(profile)
            if not isinstance(profile_data, dict):
                profile_data = {}
        except Exception:
            profile_data = {}
    else:
        profile_data = dict(profile)

    if name is None:
        profile_data.pop("name", None)
        profile_data.pop("Name", None)
    else:
        profile_data["name"] = name.strip()
    return profile_data or None


def _normalize_email(email: str) -> str:
    value = email.strip().lower()
    if "@" not in value:
        raise HTTPException(status_code=400, detail="邮箱格式不正确")
    return value


def _hash_password(password: str) -> str:
    return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()


def _assert_role_allowed(role: str, current_user: User) -> None:
    if role not in ADMIN_USER_ROLES:
        raise HTTPException(status_code=400, detail="角色必须是 owner/admin/staff")
    if role == "owner" and current_user.role != "owner":
        raise HTTPException(status_code=403, detail="只有 owner 可以设置 owner 角色")


def _admin_user_out(user: User) -> dict:
    created_at = getattr(user, "created_at", None)
    updated_at = getattr(user, "updated_at", None)
    return {
        "id": user.id,
        "email": user.email,
        "name": _name_from_profile(user),
        "role": user.role,
        "is_active": bool(user.is_active),
        "tenant_id": user.tenant_id,
        "created_at": created_at.isoformat() if created_at else None,
        "updated_at": updated_at.isoformat() if updated_at else None,
    }


async def _get_tenant_user_or_404(db: AsyncSession, tenant_id: int, user_id: int) -> User:
    target = await db.get(User, user_id)
    if not target or target.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="用户不存在")
    return target


async def _ensure_unique_user_email(db: AsyncSession, tenant_id: int, email: str, exclude_user_id: int | None = None) -> None:
    stmt = select(User).where(User.tenant_id == tenant_id, User.email == email)
    if exclude_user_id is not None:
        stmt = stmt.where(User.id != exclude_user_id)
    existing = await db.execute(stmt)
    if existing.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="邮箱已存在")


def _role_out(role: AdminRole) -> dict:
    return {
        "id": role.id,
        "code": role.code,
        "name": role.name,
        "description": role.description,
        "is_system": bool(role.is_system),
        "is_active": bool(role.is_active),
        "permissions": [
            {
                "permission_key": item.permission_key,
                "effect": item.effect,
                "data_scope": item.data_scope,
            }
            for item in sorted(role.permissions, key=lambda x: x.permission_key)
        ],
        "created_at": role.created_at.isoformat() if role.created_at else None,
        "updated_at": role.updated_at.isoformat() if role.updated_at else None,
    }


@router.get("/permissions/catalog", summary="权限目录")
async def permission_catalog(_: User = Depends(require_permission("permissions.view"))):
    return {"modules": catalog_by_module()}


@router.get("/permissions/me", summary="当前管理员有效权限")
async def my_permissions(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_admin_user),
):
    effective = await PermissionService(db).get_effective_permissions(current_user)
    data = effective.to_dict()
    data["user"] = {
        "id": current_user.id,
        "email": current_user.email,
        "name": _name_from_profile(current_user),
        "role": current_user.role,
        "tenant_id": current_user.tenant_id,
    }
    return data


@router.get("/roles", summary="角色列表")
async def list_roles(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.view")),
):
    result = await db.execute(
        select(AdminRole)
        .options(selectinload(AdminRole.permissions))
        .where(AdminRole.tenant_id == current_user.tenant_id)
        .order_by(AdminRole.is_system.desc(), AdminRole.id.asc())
    )
    return [_role_out(role) for role in result.scalars().all()]


@router.post("/roles", summary="创建角色")
async def create_role(
    body: RoleIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.roles.create")),
):
    code = _slugify(body.code or body.name)
    existing = await db.execute(select(AdminRole).where(AdminRole.tenant_id == current_user.tenant_id, AdminRole.code == code))
    if existing.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="角色编码已存在")
    role = AdminRole(
        tenant_id=current_user.tenant_id,
        code=code,
        name=body.name,
        description=body.description,
        is_system=0,
        is_active=1 if body.is_active else 0,
    )
    db.add(role)
    await db.commit()
    await db.refresh(role)
    return _role_out(role)


@router.get("/roles/{role_id}", summary="角色详情")
async def get_role(
    role_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.view")),
):
    result = await db.execute(
        select(AdminRole).options(selectinload(AdminRole.permissions)).where(
            AdminRole.tenant_id == current_user.tenant_id,
            AdminRole.id == role_id,
        )
    )
    role = result.scalar_one_or_none()
    if not role:
        raise HTTPException(status_code=404, detail="角色不存在")
    return _role_out(role)


@router.put("/roles/{role_id}", summary="更新角色")
async def update_role(
    role_id: int,
    body: RoleIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.roles.update")),
):
    result = await db.execute(select(AdminRole).where(AdminRole.tenant_id == current_user.tenant_id, AdminRole.id == role_id))
    role = result.scalar_one_or_none()
    if not role:
        raise HTTPException(status_code=404, detail="角色不存在")
    if role.is_system and current_user.role != "owner":
        raise HTTPException(status_code=403, detail="只有 owner 可以编辑系统角色")
    role.name = body.name
    role.description = body.description
    if not role.is_system:
        role.code = _slugify(body.code or role.code)
    role.is_active = 1 if body.is_active else 0
    await db.commit()
    return await get_role(role_id, db, current_user)


@router.delete("/roles/{role_id}", summary="删除角色")
async def delete_role(
    role_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.roles.delete")),
):
    result = await db.execute(select(AdminRole).where(AdminRole.tenant_id == current_user.tenant_id, AdminRole.id == role_id))
    role = result.scalar_one_or_none()
    if not role:
        raise HTTPException(status_code=404, detail="角色不存在")
    if role.is_system:
        raise HTTPException(status_code=400, detail="系统角色不能删除")
    await db.delete(role)
    await db.commit()
    return {"ok": True}


@router.put("/roles/{role_id}/permissions", summary="保存角色权限")
async def save_role_permissions(
    role_id: int,
    body: RolePermissionsIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.roles.update")),
):
    result = await db.execute(select(AdminRole).where(AdminRole.tenant_id == current_user.tenant_id, AdminRole.id == role_id))
    role = result.scalar_one_or_none()
    if not role:
        raise HTTPException(status_code=404, detail="角色不存在")
    if role.is_system and current_user.role != "owner":
        raise HTTPException(status_code=403, detail="只有 owner 可以编辑系统角色权限")
    await PermissionService(db).replace_role_permissions(current_user.tenant_id, role_id, body.permissions)
    await db.commit()
    return await get_role(role_id, db, current_user)


@router.get("/users", summary="后台用户列表")
async def list_admin_users(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.users.view")),
):
    result = await db.execute(select(User).where(User.tenant_id == current_user.tenant_id).order_by(User.id.asc()))
    users = result.scalars().all()
    return [_admin_user_out(user) for user in users]


@router.post("/users", summary="创建后台用户")
async def create_admin_user(
    body: AdminUserCreateIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.users.update")),
):
    email = _normalize_email(body.email)
    _assert_role_allowed(body.role, current_user)
    await _ensure_unique_user_email(db, current_user.tenant_id, email)

    user = User(
        tenant_id=current_user.tenant_id,
        email=email,
        password_hash=_hash_password(body.password),
        role=body.role,
        is_active=1 if body.is_active else 0,
        profile=_profile_with_name(None, body.name),
    )
    db.add(user)
    await db.commit()
    await db.refresh(user)
    return _admin_user_out(user)


@router.put("/users/{user_id}", summary="更新后台用户")
async def update_admin_user(
    user_id: int,
    body: AdminUserUpdateIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.users.update")),
):
    target = await _get_tenant_user_or_404(db, current_user.tenant_id, user_id)
    if target.role == "owner" and current_user.role != "owner":
        raise HTTPException(status_code=403, detail="只有 owner 可以编辑 owner 账号")

    data = body.model_dump(exclude_unset=True)
    if "email" in data and data["email"] is not None:
        email = _normalize_email(data["email"])
        await _ensure_unique_user_email(db, current_user.tenant_id, email, exclude_user_id=user_id)
        target.email = email
    if "password" in data and data["password"]:
        target.password_hash = _hash_password(data["password"])
    if "name" in data:
        target.profile = _profile_with_name(target.profile, data["name"])
    if "role" in data and data["role"] is not None:
        _assert_role_allowed(data["role"], current_user)
        target.role = data["role"]
    if "is_active" in data and data["is_active"] is not None:
        if target.id == current_user.id and not data["is_active"]:
            raise HTTPException(status_code=400, detail="不能禁用当前登录账号")
        target.is_active = 1 if data["is_active"] else 0

    await db.commit()
    await db.refresh(target)
    return _admin_user_out(target)


@router.delete("/users/{user_id}", summary="删除后台用户")
async def delete_admin_user(
    user_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.users.update")),
):
    target = await _get_tenant_user_or_404(db, current_user.tenant_id, user_id)
    if target.id == current_user.id:
        raise HTTPException(status_code=400, detail="不能删除当前登录账号")
    if target.role == "owner":
        raise HTTPException(status_code=400, detail="owner 账号不能删除")
    await db.delete(target)
    await db.commit()
    return {"ok": True}


@router.get("/users/{user_id}/permissions", summary="用户权限详情")
async def get_user_permissions(
    user_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.users.view")),
):
    target = await _get_tenant_user_or_404(db, current_user.tenant_id, user_id)
    effective = await PermissionService(db).get_effective_permissions(target)
    return {
        "user": {
            "id": target.id,
            "email": target.email,
            "name": _name_from_profile(target),
            "role": target.role,
            "is_active": bool(target.is_active),
        },
        **effective.to_dict(),
    }


@router.put("/users/{user_id}/roles", summary="设置用户角色")
async def save_user_roles(
    user_id: int,
    body: UserRolesIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.roles.assign")),
):
    target = await _get_tenant_user_or_404(db, current_user.tenant_id, user_id)
    if target.role == "owner":
        raise HTTPException(status_code=400, detail="owner 不需要分配角色")
    await PermissionService(db).replace_user_roles(current_user.tenant_id, user_id, body.role_ids)
    await db.commit()
    return await get_user_permissions(user_id, db, current_user)


@router.put("/users/{user_id}/permission-overrides", summary="设置用户权限微调")
async def save_user_overrides(
    user_id: int,
    body: UserOverridesIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.users.override")),
):
    target = await _get_tenant_user_or_404(db, current_user.tenant_id, user_id)
    if target.role == "owner":
        raise HTTPException(status_code=400, detail="owner 不支持权限微调")

    known = permission_keys()
    for item in body.overrides:
        if item.get("permission_key") not in known:
            raise HTTPException(status_code=400, detail=f"未知权限: {item.get('permission_key')}")
        if item.get("effect") not in ("allow", "deny"):
            raise HTTPException(status_code=400, detail="effect 必须是 allow 或 deny")

    await PermissionService(db).replace_user_overrides(current_user.tenant_id, user_id, body.overrides, current_user.id)
    await db.commit()
    return await get_user_permissions(user_id, db, current_user)


@router.delete("/users/{user_id}/permission-overrides", summary="清空用户权限微调")
async def clear_user_overrides(
    user_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("permissions.users.override")),
):
    target = await _get_tenant_user_or_404(db, current_user.tenant_id, user_id)
    await db.execute(
        delete(AdminUserPermissionOverride).where(
            AdminUserPermissionOverride.tenant_id == current_user.tenant_id,
            AdminUserPermissionOverride.user_id == user_id,
        )
    )
    await db.commit()
    return await get_user_permissions(user_id, db, current_user)
