from sqlalchemy import select
from sqlalchemy.orm import Session, joinedload, selectinload

from app.core.security import hash_password
from app.models.rbac import Role
from app.models.user import User
from app.schemas.user import UserCreate, UserPasswordUpdate, UserUpdate


class UserRepository:
    @staticmethod
    def get_all(db: Session) -> list[User]:
        return list(
            db.scalars(
                select(User)
                .options(
                    joinedload(User.primary_role).selectinload(Role.permissions),
                )
                .order_by(User.created_at.desc())
            )
        )

    @staticmethod
    def get_by_id(db: Session, user_id: int) -> User | None:
        return db.scalar(
            select(User)
            .where(User.id == user_id)
            .options(joinedload(User.primary_role).selectinload(Role.permissions))
        )

    @staticmethod
    def get_by_username(db: Session, username: str) -> User | None:
        return db.scalar(
            select(User)
            .where(User.username == username)
            .options(joinedload(User.primary_role).selectinload(Role.permissions))
        )

    @staticmethod
    def find_conflict(
        db: Session,
        *,
        username: str,
        email: str,
        exclude_user_id: int | None = None,
    ) -> tuple[str, User] | None:
        username_query = select(User).where(User.username == username)
        email_query = select(User).where(User.email == email)

        if exclude_user_id is not None:
            username_query = username_query.where(User.id != exclude_user_id)
            email_query = email_query.where(User.id != exclude_user_id)

        username_user = db.scalar(username_query)
        if username_user is not None:
            return ("username", username_user)

        email_user = db.scalar(email_query)
        if email_user is not None:
            return ("email", email_user)

        return None

    @staticmethod
    def create(db: Session, user: UserCreate) -> User:
        db_user = User(
            first_name=user.first_name,
            last_name=user.last_name,
            username=user.username,
            email=str(user.email),
            role=user.role,
            role_id=user.role_id,
            is_active=user.is_active,
            password_hash=hash_password(user.password),
        )

        db.add(db_user)
        db.commit()
        db.refresh(db_user)

        return UserRepository.get_by_id(db, db_user.id) or db_user

    @staticmethod
    def update(db: Session, db_user: User, user: UserUpdate) -> User:
        db_user.first_name = user.first_name
        db_user.last_name = user.last_name
        db_user.username = user.username
        db_user.email = str(user.email)
        db_user.role = user.role
        if user.role_id is not None:
            db_user.role_id = user.role_id
        db_user.is_active = user.is_active

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

        db.commit()
        db.refresh(db_user)

        return UserRepository.get_by_id(db, db_user.id) or db_user

    @staticmethod
    def update_password(
        db: Session,
        db_user: User,
        password_update: UserPasswordUpdate,
    ) -> User:
        db_user.password_hash = hash_password(password_update.password)

        db.commit()
        db.refresh(db_user)

        return db_user

    @staticmethod
    def delete(db: Session, db_user: User) -> None:
        db.delete(db_user)
        db.commit()
