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

from app.core.roles import ROLE_ADMIN, ROLE_AGENT, ROLE_OEM
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


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


def _list_users_filters(
    *,
    username: str | None = None,
    device_serial: 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 device_serial and device_serial.strip():
        conditions.append(User.device_serial.ilike(f"%{device_serial.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,
    device_serial: 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,
        device_serial=device_serial,
        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_owner_ids(
    db: AsyncSession,
    *,
    oem_id: int | None,
    agent_id: int | None,
) -> None:
    if oem_id is not None:
        oem = await db.get(User, oem_id)
        if oem is None or oem.role_id != ROLE_OEM:
            raise AdminUserError("所选 OEM 不存在")
    if agent_id is not None:
        agent = await db.get(User, agent_id)
        if agent is None or agent.role_id != ROLE_AGENT:
            raise AdminUserError("所选代理不存在")


async def create_user(db: AsyncSession, body: AdminUserCreate,oem: User) -> User:
    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("手机号已被使用")

    await _validate_owner_ids(db, oem_id=oem.id, agent_id=body.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=body.agent_id,
        vip_end_time=body.vip_end_time,
    )
    db.add(user)
    await db.flush()
    await db.refresh(user)
    return user


async def update_user(
    db: AsyncSession,
    oem: User,
    user_id: int,
    body: AdminUserUpdate,
    *,
    actor_id: int,
) -> User:
    user = await db.get(User, user_id)
    if user is None:
        raise AdminUserError("用户不存在")
    body.oem_id=oem.id
    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 and body.role_id != ROLE_ADMIN:
            raise AdminUserError("不能修改自己的管理员角色")
        user.role_id = body.role_id

    if body.oem_id is not None or body.agent_id is not None:
        await _validate_owner_ids(
            db,
            oem_id=body.oem_id if body.oem_id is not None else user.oem_id,
            agent_id=body.agent_id if body.agent_id is not None else user.agent_id,
        )
    if body.oem_id is not None:
        user.oem_id = body.oem_id
    if body.agent_id is not None:
        user.agent_id = body.agent_id

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

    if body.clear_vip_end_time:
        user.vip_end_time = None
    elif body.vip_end_time is not None:
        user.vip_end_time = body.vip_end_time

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


async def clear_device_serial(db: AsyncSession, user_id: int) -> User:
    """管理员解绑：将用户 device_serial 置空。"""
    user = await db.get(User, user_id)
    if user is None:
        raise AdminUserError("用户不存在")
    if not (user.device_serial or "").strip():
        raise AdminUserError("该用户未绑定设备")
    user.device_serial = ""
    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 db.get(User, user_id)
    if user is None:
        raise AdminUserError("用户不存在")
    if user.oem_id != oem.id:
        raise AdminUserError("无权限删除该用户")
    await db.delete(user)
