from __future__ import annotations

from dataclasses import dataclass
from typing import BinaryIO

from ..core.logger import AppLogger
from ..core.permissions import VALID_ROLES
from ..core.security import create_access_token, hash_password, verify_password
from ..core.settings import AppSettings
from ..database.orm import UserORM
from ..io.storage import FileStorage
from ..repositories import UserRepository

logger = AppLogger.get_logger(__name__)


@dataclass(frozen=True)
class AuthSession:
    """登录成功后的短生命周期对象，只承载用户和签发出的 token。"""

    user: UserORM
    access_token: str
    token_type: str = "bearer"


class AuthService:
    """认证应用服务：登录、创建用户和首次启动超级用户初始化。"""

    def __init__(self, repository: UserRepository, settings: AppSettings):
        self.repository = repository
        self.settings = settings
        self.storage = FileStorage(settings)

    def login(self, username: str, password: str) -> AuthSession:
        logger.info("用户登录校验开始 username=%s", username)
        user = self.repository.get_by_username(username)
        if user is None or not user.is_active:
            logger.error("用户登录失败 username=%s reason=user_not_found_or_inactive", username)
            raise ValueError("用户名或密码错误")
        if not verify_password(password, user.password_hash):
            logger.error("用户登录失败 username=%s reason=invalid_password", username)
            raise ValueError("用户名或密码错误")
        token = create_access_token(
            user_id=user.id,
            username=user.username,
            role=user.role,
            secret_key=self.settings.auth_secret_key,
            expires_minutes=self.settings.access_token_expire_minutes,
        )
        logger.info("用户登录成功 user_id=%s username=%s role=%s", user.id, user.username, user.role)
        return AuthSession(user=user, access_token=token)

    def create_user(
        self,
        *,
        username: str,
        password: str,
        role: str,
        display_name: str = "",
        email: str = "",
        phone: str = "",
        department: str = "",
        position_title: str = "",
    ) -> UserORM:
        logger.info("创建用户开始 username=%s role=%s", username, role)
        if role not in VALID_ROLES:
            logger.error("创建用户失败 username=%s reason=invalid_role role=%s", username, role)
            raise ValueError(f"无效角色：{role}")
        if self.repository.get_by_username(username):
            logger.error("创建用户失败 username=%s reason=duplicate_username", username)
            raise ValueError("用户名已存在")
        user = self.repository.create_user(
            username=username,
            password_hash=hash_password(password),
            role=role,
            display_name=display_name,
            email=email,
            phone=phone,
            department=department,
            position_title=position_title,
        )
        logger.info("创建用户成功 user_id=%s username=%s role=%s", user.id, user.username, user.role)
        return user

    def update_profile(
        self,
        user_id: int,
        *,
        display_name: str,
        email: str,
        phone: str,
        department: str,
        position_title: str,
    ) -> UserORM:
        logger.info("修改个人资料开始 user_id=%s", user_id)
        user = self.repository.update_profile(
            user_id,
            display_name=display_name,
            email=email,
            phone=phone,
            department=department,
            position_title=position_title,
        )
        logger.info("修改个人资料成功 user_id=%s username=%s", user.id, user.username)
        return user

    def update_avatar(self, user_id: int, filename: str, stream: BinaryIO) -> UserORM:
        logger.info("修改头像开始 user_id=%s filename=%s", user_id, filename)
        avatar_url = self.storage.save_avatar(filename, stream)
        user = self.repository.update_profile(user_id, avatar_url=avatar_url)
        logger.info("修改头像成功 user_id=%s avatar_url=%s", user.id, avatar_url)
        return user

    def change_password(
        self,
        user_id: int,
        *,
        old_password: str,
        new_password: str,
        confirm_password: str,
    ) -> UserORM:
        logger.info("修改密码开始 user_id=%s", user_id)
        user = self.repository.get_by_id(user_id)
        if user is None or not user.is_active:
            logger.error("修改密码失败 user_id=%s reason=user_not_found_or_inactive", user_id)
            raise ValueError("用户不存在或已停用")
        if not verify_password(old_password, user.password_hash):
            logger.error("修改密码失败 user_id=%s username=%s reason=invalid_old_password", user.id, user.username)
            raise ValueError("原密码错误")
        if new_password != confirm_password:
            logger.error("修改密码失败 user_id=%s username=%s reason=password_confirm_mismatch", user.id, user.username)
            raise ValueError("两次输入的新密码不一致")
        if verify_password(new_password, user.password_hash):
            logger.error("修改密码失败 user_id=%s username=%s reason=same_as_old_password", user.id, user.username)
            raise ValueError("新密码不能与原密码相同")

        updated_user = self.repository.update_password_hash(user.id, hash_password(new_password))
        logger.info("修改密码成功 user_id=%s username=%s", updated_user.id, updated_user.username)
        return updated_user

    def list_users(self) -> list[UserORM]:
        users = self.repository.list_users()
        logger.info("查询用户列表成功 count=%s", len(users))
        return users

    def ensure_bootstrap_superuser(self) -> None:
        if self.repository.has_users():
            logger.info("用户表已有数据，跳过初始化超级管理员")
            return
        self.repository.create_user(
            username=self.settings.bootstrap_superuser_username,
            password_hash=hash_password(self.settings.bootstrap_superuser_password),
            role="superuser",
            display_name=self.settings.bootstrap_superuser_display_name,
        )
        logger.info("默认超级管理员初始化完成 username=%s", self.settings.bootstrap_superuser_username)
