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

from src.models import Pattern, Question, QuestionPattern
from src.schemas import (
    CommonMistake,
    LearningProgression,
    LearningQuestion,
    PatternTutorialResponse,
    PatternVariation,
    RelatedPattern,
    VisualizationExample,
)


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

    async def get_patterns(self) -> list[tuple[Pattern, int]]:
        query = (
            select(Pattern, func.count(QuestionPattern.question_id).label("question_count"))
            .outerjoin(QuestionPattern, Pattern.id == QuestionPattern.pattern_id)
            .group_by(Pattern.id)
            .order_by(Pattern.name)
        )
        result = await self.db.execute(query)
        return [(row[0], row[1]) for row in result.all()]

    async def get_pattern_by_slug(self, slug: str) -> tuple[Pattern, int] | None:
        query = (
            select(Pattern, func.count(QuestionPattern.question_id).label("question_count"))
            .outerjoin(QuestionPattern, Pattern.id == QuestionPattern.pattern_id)
            .where(Pattern.slug == slug)
            .group_by(Pattern.id)
        )
        result = await self.db.execute(query)
        row = result.first()
        if row:
            return (row[0], row[1])
        return None

    async def get_pattern_tutorial(self, slug: str) -> PatternTutorialResponse | None:
        result = await self.get_pattern_by_slug(slug)
        if not result:
            return None

        pattern, question_count = result

        questions_query = (
            select(Question, QuestionPattern.is_optimal)
            .join(QuestionPattern, Question.id == QuestionPattern.question_id)
            .where(QuestionPattern.pattern_id == pattern.id)
            .order_by(Question.difficulty, Question.title)
        )
        questions_result = await self.db.execute(questions_query)
        questions_with_optimal = [(row[0], row[1]) for row in questions_result.all()]

        learning_progression = self._build_learning_progression(questions_with_optimal)

        related = await self._resolve_patterns(pattern.related_patterns or [])
        prerequisites = await self._resolve_patterns(pattern.prerequisite_patterns or [])

        common_mistakes = None
        if pattern.common_mistakes:
            common_mistakes = [CommonMistake(**m) for m in pattern.common_mistakes]

        variations = None
        if pattern.variations:
            variations = [PatternVariation(**v) for v in pattern.variations]

        visualization_examples = None
        if pattern.visualization_examples:
            visualization_examples = [
                VisualizationExample(**e) for e in pattern.visualization_examples
            ]

        return PatternTutorialResponse(
            id=pattern.id,
            name=pattern.name,
            slug=pattern.slug,
            description=pattern.description,
            when_to_use=pattern.when_to_use,
            question_count=question_count,
            metaphor=pattern.metaphor,
            core_concept=pattern.core_concept,
            visualization=pattern.visualization,
            code_template=pattern.code_template,
            recognition_signals=pattern.recognition_signals,
            common_mistakes=common_mistakes,
            variations=variations,
            related_patterns=related,
            prerequisite_patterns=prerequisites,
            difficulty_level=pattern.difficulty_level,
            learning_progression=learning_progression,
            visualization_examples=visualization_examples,
        )

    def _build_learning_progression(
        self, questions_with_optimal: list[tuple[Question, bool]]
    ) -> LearningProgression:
        warmup: list[LearningQuestion] = []
        core: list[LearningQuestion] = []
        challenge: list[LearningQuestion] = []

        for q, is_optimal in questions_with_optimal:
            lq = LearningQuestion(
                id=q.id,
                title=q.title,
                slug=q.slug,
                difficulty=q.difficulty.value,
                leetcode_id=q.leetcode_id,
                is_optimal=is_optimal,
            )
            if q.difficulty.value == "easy":
                warmup.append(lq)
            elif q.difficulty.value == "medium":
                core.append(lq)
            else:
                challenge.append(lq)

        return LearningProgression(warmup=warmup, core=core, challenge=challenge)

    async def _resolve_patterns(self, slugs: list[str]) -> list[RelatedPattern]:
        if not slugs:
            return []

        query = select(Pattern).where(Pattern.slug.in_(slugs))
        result = await self.db.execute(query)
        patterns = result.scalars().all()

        return [
            RelatedPattern(slug=p.slug, name=p.name, description=p.description) for p in patterns
        ]
