from __future__ import annotations

from sqlalchemy.ext.asyncio import AsyncSession

from src.domain.composition_recipes.repository import CompositionRecipeRepository
from src.domain.composition_recipes.schemas import CompositionRecipe, RecipeReadiness
from src.domain.composition_recipes.validation import readiness_for_recipe, validate_recipe


class CompositionRecipeService:
    def __init__(self, db: AsyncSession):
        self.repo = CompositionRecipeRepository(db)

    @staticmethod
    def from_record(record) -> CompositionRecipe:
        return CompositionRecipe.model_validate(
            {
                key: getattr(record, key)
                for key in CompositionRecipe.model_fields
                if hasattr(record, key)
            }
        )

    async def get(self, slug: str, *, active_only: bool = True) -> CompositionRecipe | None:
        record = await self.repo.get_by_slug(slug, active_only=active_only)
        return self.from_record(record) if record else None

    async def resolve_for_prompt_suggestion(
        self, suggestion_id, *, active_only: bool = True
    ) -> CompositionRecipe | None:
        record = await self.repo.get_for_prompt_suggestion(suggestion_id, active_only=active_only)
        return self.from_record(record) if record else None

    async def recipe_for_discovery_state(
        self,
        state: dict,
        *,
        source_prompt: str,
        project_type: str | None = None,
    ) -> CompositionRecipe | None:
        """Pinned closures are loaded by slug. A null pin is an explicit miss.

        Unpinned legacy states still resolve from the prompt.
        """
        if state.get("composition_recipe_pinned"):
            slug = state.get("composition_recipe_slug")
            if not slug:
                return None
            return await self.get(slug)
        return await self.resolve_for_text(source_prompt, project_type)

    async def pin_ready_composition(
        self,
        state: dict,
        *,
        spec: dict,
        project_type: str | None,
    ) -> dict:
        """Freeze the recipe and customer lists when a discovery turn is ready."""
        from src.domain.app_blocks.planner import source_prompt_from_discovery
        from src.domain.app_blocks.public import customer_composition_for_spec

        source_prompt = source_prompt_from_discovery(state)
        recipe = await self.resolve_for_text(source_prompt, project_type)
        return {
            "composition_recipe_pinned": True,
            "composition_recipe_slug": recipe.slug if recipe else None,
            "customer_composition": customer_composition_for_spec(
                spec, source_prompt=source_prompt, composition_recipe=recipe
            ),
        }

    async def resolve_for_text(
        self, text: str, project_type: str | None = None
    ) -> CompositionRecipe | None:
        from src.domain.prompt_suggestions.service import PromptSuggestionService

        suggestions = await PromptSuggestionService(self.repo.db).suggest_for_text(
            text, project_type
        )
        for suggestion in suggestions:
            recipe = await self.resolve_for_prompt_suggestion(suggestion.id)
            if recipe is not None:
                return recipe
        return None

    def validate(self, recipe: CompositionRecipe):
        return validate_recipe(recipe)

    def readiness(self, recipe: CompositionRecipe) -> RecipeReadiness:
        return readiness_for_recipe(recipe)
