from uuid import UUID

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

from src.models import Category, Difficulty, Pattern, Question
from src.models.question import QuestionPattern


class QuestionService:
    def __init__(self, db: AsyncSession) -> None:
        self.db = db

    async def get_questions(
        self,
        *,
        page: int = 1,
        limit: int = 20,
        difficulties: list[Difficulty] | None = None,
        category_slug: str | None = None,
        pattern_slug: str | None = None,
        search: str | None = None,
    ) -> tuple[list[Question], int]:
        query = (
            select(Question)
            .options(
                selectinload(Question.categories),
                selectinload(Question.patterns),
                selectinload(Question.question_patterns).selectinload(QuestionPattern.pattern),
            )
            .order_by(Question.created_at.desc())
        )

        count_query = select(func.count(Question.id))

        if difficulties:
            query = query.where(Question.difficulty.in_(difficulties))
            count_query = count_query.where(Question.difficulty.in_(difficulties))

        if category_slug:
            query = query.join(Question.categories).where(Category.slug == category_slug)
            count_query = count_query.join(Question.categories).where(
                Category.slug == category_slug
            )

        if pattern_slug:
            query = query.join(Question.patterns).where(Pattern.slug == pattern_slug)
            count_query = count_query.join(Question.patterns).where(Pattern.slug == pattern_slug)

        if search:
            search_filter = Question.title.ilike(f"%{search}%")
            query = query.where(search_filter)
            count_query = count_query.where(search_filter)

        total_result = await self.db.execute(count_query)
        total = total_result.scalar() or 0

        offset = (page - 1) * limit
        query = query.offset(offset).limit(limit)

        result = await self.db.execute(query)
        questions = list(result.scalars().unique().all())

        return questions, total

    async def get_question_by_slug(self, slug: str) -> Question | None:
        query = (
            select(Question)
            .options(
                selectinload(Question.categories),
                selectinload(Question.patterns),
                selectinload(Question.question_patterns).selectinload(QuestionPattern.pattern),
                selectinload(Question.explanation),
                selectinload(Question.solutions),
            )
            .where(Question.slug == slug)
        )
        result = await self.db.execute(query)
        return result.scalar_one_or_none()

    async def get_question_by_id(self, question_id: UUID) -> Question | None:
        query = (
            select(Question)
            .options(
                selectinload(Question.categories),
                selectinload(Question.patterns),
                selectinload(Question.question_patterns).selectinload(QuestionPattern.pattern),
                selectinload(Question.explanation),
                selectinload(Question.solutions),
            )
            .where(Question.id == question_id)
        )
        result = await self.db.execute(query)
        return result.scalar_one_or_none()
