from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.roles import ROLE_AGENT
from app.core.security import hash_password
from app.models.user import User
from app.schemas.admin_user import AdminUserCreate, AdminUserUpdate

DEFAULT_PAGE_SIZE = 20
MAX_PAGE_SIZE = 200

# OEM 创建用户时允许的角色：普通用户、代理
OEM_CREATABLE_ROLE_IDS = frozenset({0, ROLE_AGENT})


class AdminUserError(Exception):
    def __init__(self, message: str) -> None:
        self.message = message
        super().__init__(message)


def _list_users_filters(
    *,
    username: str | None = None,
    oem_id: int | None = None,
    agent_id: int | None = None,
    role_id: int | None = None,
) -> list:
    conditions = []
    if username and username.strip():
        conditions.append(User.username.ilike(f"%{username.strip()}%"))
    if oem_id is not None:
        conditions.append(User.oem_id == oem_id)
    if agent_id is not None:
        conditions.append(User.agent_id == agent_id)
    if role_id is not None:
        conditions.append(User.role_id == role_id)
    return conditions


async def list_users(
    db: AsyncSession,
    *,
    page: int = 1,
    page_size: int = DEFAULT_PAGE_SIZE,
    username: str | None = None,
    oem_id: int | None = None,
    agent_id: int | None = None,
    role_id: int | None = None,
) -> tuple[list[User], int]:
    page_size = min(max(page_size, 1), MAX_PAGE_SIZE)
    page = max(page, 1)
    offset = (page - 1) * page_size
    conditions = _list_users_filters(
        username=username,
        oem_id=oem_id,
        agent_id=agent_id,
        role_id=role_id,
    )

    count_stmt = select(func.count()).select_from(User)
    list_stmt = select(User).order_by(User.id.desc())
    if conditions:
        count_stmt = count_stmt.where(*conditions)
        list_stmt = list_stmt.where(*conditions)

    total = await db.scalar(count_stmt) or 0
    result = await db.scalars(list_stmt.offset(offset).limit(page_size))
    return list(result.all()), total


async def _validate_agent_under_oem(
    db: AsyncSession,
    *,
    oem_id: int,
    agent_id: int | None,
) -> None:
    if agent_id is None:
        return
    agent = await db.get(User, agent_id)
    if agent is None or agent.role_id != ROLE_AGENT:
        raise AdminUserError("所选代理不存在")
    if agent.oem_id != oem_id:
        raise AdminUserError("所选代理不属于当前 OEM")


def _validate_creatable_role(role_id: int) -> None:
    if role_id not in OEM_CREATABLE_ROLE_IDS:
        raise AdminUserError("只能创建普通用户或代理")


async def create_user(db: AsyncSession, body: AdminUserCreate, oem: User) -> User:
    _validate_creatable_role(body.role_id)
    if body.vip_end_time is not None:
        raise AdminUserError("创建用户时不能设置 VIP 到期时间")

    existing = await db.scalar(select(User).where(User.username == body.username))
    if existing is not None:
        raise AdminUserError("用户名已存在")

    if body.phone:
        phone_taken = await db.scalar(select(User).where(User.phone == body.phone))
        if phone_taken is not None:
            raise AdminUserError("手机号已被使用")

    agent_id = body.agent_id
    if body.role_id == ROLE_AGENT:
        agent_id = None
    await _validate_agent_under_oem(db, oem_id=oem.id, agent_id=agent_id)

    user = User(
        username=body.username,
        password_hash=hash_password(body.password),
        phone=body.phone,
        role_id=body.role_id,
        oem_id=oem.id,
        agent_id=agent_id,
        vip_end_time=None,
    )
    db.add(user)
    await db.flush()
    await db.refresh(user)
    return user


async def _get_user_under_oem(db: AsyncSession, user_id: int, oem: User) -> User:
    user = await db.get(User, user_id)
    if user is None:
        raise AdminUserError("用户不存在")
    if user.oem_id != oem.id:
        raise AdminUserError("无权限操作该用户")
    return user


async def update_user(
    db: AsyncSession,
    oem: User,
    user_id: int,
    body: AdminUserUpdate,
    *,
    actor_id: int,
) -> User:
    user = await _get_user_under_oem(db, user_id, oem)

    if body.username is not None and body.username != user.username:
        taken = await db.scalar(select(User).where(User.username == body.username))
        if taken is not None:
            raise AdminUserError("用户名已存在")
        user.username = body.username

    if body.phone is not None:
        if body.phone != user.phone:
            taken = await db.scalar(select(User).where(User.phone == body.phone))
            if taken is not None:
                raise AdminUserError("手机号已被使用")
        user.phone = body.phone

    if body.role_id is not None:
        if user_id == actor_id:
            raise AdminUserError("不能修改自己的角色")
        _validate_creatable_role(body.role_id)
        user.role_id = body.role_id

    new_agent_id = body.agent_id if body.agent_id is not None else user.agent_id
    effective_role = body.role_id if body.role_id is not None else user.role_id
    if effective_role == ROLE_AGENT:
        new_agent_id = None
    elif body.agent_id is not None:
        await _validate_agent_under_oem(db, oem_id=oem.id, agent_id=new_agent_id)
    if body.agent_id is not None or effective_role == ROLE_AGENT:
        user.agent_id = new_agent_id

    if body.password:
        user.password_hash = hash_password(body.password)

    if body.clear_vip_end_time or body.vip_end_time is not None:
        raise AdminUserError("不能设置 VIP 到期时间")

    await db.flush()
    await db.refresh(user)
    return user


async def delete_user(db: AsyncSession, oem: User, user_id: int, *, actor_id: int) -> None:
    if user_id == actor_id:
        raise AdminUserError("不能删除当前登录账号")

    user = await _get_user_under_oem(db, user_id, oem)
    await db.delete(user)
