from datetime import datetime
from typing import Any

from fastapi import HTTPException
from sqlalchemy import select, func, or_, desc
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from starlette import status

from src.security import hash_password
from src.user.models import User, UserRole
from src.user.schemas import UserCreate, UserResponse, UserRoleResponse
from src.utils.image_manager import handle_image_upload, ASSETS_URL


class UserService:
    @staticmethod
    async def create_user(
            db: AsyncSession,
            *,
            name: str,
            login: str,
            phone: str,
            password: str,
            role_id: int,
            logo_file=None
    ):
        # Перевірка чи логін вже існує
        result = await db.execute(select(User).where(User.login == login))
        existing_user = result.scalar_one_or_none()
        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Цей логін уже зайнятий, використайте інший"
            )

        new_user = User(
            name=name,
            login=login,
            phone=phone,
            password=hash_password(password),
            role_id=role_id
        )

        if logo_file is not None:
            uploaded_logo_url = await handle_image_upload(logo_file)
            new_user.logo = uploaded_logo_url if not uploaded_logo_url.endswith(
                "placeholder.webp") else uploaded_logo_url
        else:
            new_user.logo = f"{ASSETS_URL}placeholder.webp"

        db.add(new_user)
        await db.commit()
        await db.refresh(new_user)
        await db.refresh(new_user, attribute_names=["role"])

        user_data = {
            "user_id": new_user.user_id,
            "role_id": new_user.role_id,
            "name": new_user.name,
            "login": new_user.login,
            "phone": new_user.phone,
            "logo": new_user.logo,
            "date_added": new_user.date_added,
            "date_modify": new_user.date_modify,
            "role": {
                "role_id": new_user.role.role_id,
                "name": new_user.role.name
            } if new_user.role else None
        }

        return {
            "data": UserResponse.model_validate(user_data),
            "status": True
        }

    @staticmethod
    async def get_user_by_id(user_id: int, db: AsyncSession) -> User:
        result = await db.execute(
            select(User)
            .options(selectinload(User.role))
            .where(User.user_id == user_id)
        )
        user = result.scalar_one_or_none()
        if not user:
            raise HTTPException(status_code=404, detail="User not found")
        return user

    @staticmethod
    async def get_user_list(
            db: AsyncSession,
            needle: str | None = None,
            role_id: int | None = None,
            page: int = 1,
            limit: int = 10,
    ) -> dict[str, Any]:

        # Основний запит без пагінації
        base_stmt = (
            select(User)
            .options(selectinload(User.role))
            .where(~User.login.like("deleted_user_%"))
        )

        if role_id:
            base_stmt = base_stmt.where(User.role_id == role_id)

        if needle:
            like = f"%{needle.lower()}%"
            base_stmt = base_stmt.where(
                or_(
                    func.lower(User.name).ilike(like),
                    func.lower(User.login).ilike(like),
                    func.lower(User.phone).ilike(like),
                )
            )

        # Окремий запит для total — без offset/limit/order_by
        total_stmt = select(func.count()).select_from(base_stmt.subquery())
        total = (await db.execute(total_stmt)).scalar_one()

        # Додаємо пагінацію
        stmt = base_stmt.order_by(User.date_added.desc()).offset((page - 1) * limit).limit(limit)
        result = await db.execute(stmt)
        users = result.scalars().unique().all()

        user_responses = [
            UserResponse.model_validate(user, from_attributes=True)
            for user in users
        ]

        return {
            "data": user_responses,
            "total": total,
            "page": page,
            "limit": limit
        }

    @staticmethod
    async def update_user(
            user_id: int,
            db: AsyncSession,
            *,
            name: str | None = None,
            login: str | None = None,
            phone: str | None = None,
            password: str | None = None,
            role_id: int | None = None,
            logo_file=None,
    ):
        stmt = select(User).where(User.user_id == user_id).options(selectinload(User.role))
        result = await db.execute(stmt)
        user = result.scalar_one_or_none()

        if not user:
            raise HTTPException(status_code=404, detail="User not found")

        if name is not None:
            user.name = name
        if login is not None:
            user.login = login
        if phone is not None:
            user.phone = phone
        if password is not None:
            user.password = hash_password(password)
        if role_id is not None:
            user.role_id = role_id
        if logo_file is not None:
            user.logo = await handle_image_upload(logo_file)

        await db.commit()
        await db.refresh(user)
        await db.refresh(user, attribute_names=["role"])

        user_data = {
            "user_id": user.user_id,
            "role_id": user.role_id,
            "name": user.name,
            "login": user.login,
            "phone": user.phone,
            "logo": user.logo,
            "date_added": user.date_added,
            "date_modify": user.date_modify,
            "role": {
                "role_id": user.role.role_id,
                "name": user.role.name
            } if user.role else None
        }

        return {
            "data": UserResponse.model_validate(user_data),
            "status": True
        }

    @staticmethod
    async def delete_user(user_id: int, db: AsyncSession) -> dict:
        stmt = select(User).where(User.user_id == user_id)
        result = await db.execute(stmt)
        user = result.scalar_one_or_none()

        if not user:
            raise HTTPException(status_code=404, detail="User not found")

        user.login = f"deleted_user_{user.user_id}_{int(datetime.utcnow().timestamp())}"
        user.password = ""
        await db.commit()

        return {"status": True, "message": "User deleted"}

    @staticmethod
    async def get_all_roles(db: AsyncSession) -> dict:
        stmt = select(UserRole).order_by(desc(UserRole.role_id))
        result = await db.execute(stmt)
        roles = result.scalars().all()

        return {
            "data": [{"id": role.role_id, "name": role.name} for role in roles],
            "status": True,
        }

