from sqlalchemy.orm import Session

from app.core.security import get_password_hash, verify_password
from app.crud.base import CRUDBase
from app.models.role import Role
from app.models.user import User
from app.models.user_role import UserRole
from app.schemas.user import UserCreate, UserUpdate
from app.services.access.rbac_bootstrap_service import RBACBootstrapService
from common_logging import get_logger

logger = get_logger(__name__)


class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):

    def get_by_email(
        self, db: Session, *, email: str, tenant_id: int | None = None
    ) -> User | None:
        query = db.query(User).filter(User.email == email)
        if tenant_id is not None:
            query = query.filter(User.tenant_id == tenant_id)
        return query.first()

    def get_by_username(
        self, db: Session, *, username: str, tenant_id: int | None = None
    ) -> User | None:
        query = db.query(User).filter(User.username == username)
        if tenant_id is not None:
            query = query.filter(User.tenant_id == tenant_id)
        return query.first()

    def get_by_company(
        self, db: Session, *, tenant_id: int, skip: int = 0, limit: int = 100
    ) -> list[User]:
        return db.query(User).filter(User.tenant_id == tenant_id).offset(skip).limit(limit).all()

    def create(
        self,
        db: Session,
        *,
        obj_in: UserCreate,
        created_by: int | None = None,
        commit: bool = True,
    ) -> User:
        db_obj = User(
            email=obj_in.email,
            name=obj_in.name,
            hashed_password=get_password_hash(obj_in.password),
            tenant_id=obj_in.tenant_id,
            role=obj_in.role,
        )
        db.add(db_obj)
        if commit:
            db.commit()
            db.refresh(db_obj)
            logger.bind(user_id=db_obj.id).info("User created")
            try:
                RBACBootstrapService.sync_user_role_binding(db, db_obj)
            except Exception as e:
                logger.warning(f"Failed to assign role: {e}")
        return db_obj

    def authenticate(self, db: Session, *, email: str, password: str) -> User | None:
        user = self.get_by_email(db, email=email)
        if not user:
            return None
        if not verify_password(password, user.hashed_password):
            return None
        return user

    def update_password(
        self, db: Session, *, user: User, new_password: str, commit: bool = True
    ) -> User:
        user.hashed_password = get_password_hash(new_password)
        db.add(user)
        if commit:
            db.commit()
            db.refresh(user)
            logger.bind(user_id=user.id).info("User password updated")
        return user

    def is_active(self, user: User) -> bool:
        return user.is_active

    def is_platform_admin(self, user: User) -> bool:
        return user.role == "platform_admin"

    def is_customer_admin(self, user: User) -> bool:
        return user.role == "customer_admin"

    def get_roles(self, db: Session, *, user_id: int) -> list[Role]:
        user_roles = (
            db.query(UserRole)
            .filter(UserRole.user_id == user_id, not UserRole.is_deleted)
            .all()
        )
        role_ids = [ur.role_id for ur in user_roles]
        if not role_ids:
            return []
        return db.query(Role).filter(Role.id.in_(role_ids), not Role.is_deleted).all()

    def update_roles(self, db: Session, *, user_id: int, role_ids: list[int]) -> None:
        existing = (
            db.query(UserRole)
            .filter(UserRole.user_id == user_id, not UserRole.is_deleted)
            .all()
        )
        for ur in existing:
            ur.is_deleted = True
        for role_id in role_ids:
            user_role = UserRole(user_id=user_id, role_id=role_id)
            db.add(user_role)
        db.commit()
        logger.bind(user_id=user_id).info("User roles updated")


user = CRUDUser(User)
