from __future__ import annotations

import re
from sqlalchemy.ext.asyncio import AsyncSession
from src.domain.prompt_suggestions.models import PromptSuggestion
from src.domain.prompt_suggestions.repository import PromptSuggestionRepository


def _normalize(value: str) -> str:
    return re.sub(r"\s+", " ", re.sub(r"[^a-z0-9]+", " ", (value or "").lower())).strip()


def _matches_phrase(haystack: str, phrase: str) -> bool:
    candidate = _normalize(phrase)
    return bool(candidate and re.search(rf"(?:^| ){re.escape(candidate)}(?:$| )", haystack))


def _any_keyword_matches(haystack: str, keywords: list | None) -> bool:
    return any(_matches_phrase(haystack, str(keyword)) for keyword in keywords or [])


def _all_keywords_match(haystack: str, keywords: list | None) -> bool:
    return all(_matches_phrase(haystack, str(keyword)) for keyword in keywords or [])


class PromptSuggestionService:
    def __init__(self, db: AsyncSession):
        self.db = db
        self.repo = PromptSuggestionRepository(db)

    async def suggest(self, project_name: str, project_type: str | None = None) -> list[PromptSuggestion]:
        return await self._suggest_for_haystack(_normalize(project_name), project_type)

    async def suggest_for_text(self, text: str, project_type: str | None = None) -> list[PromptSuggestion]:
        """Match the complete request when resolving an architecture baseline."""
        return await self._suggest_for_haystack(_normalize(text), project_type)

    async def _suggest_for_haystack(
        self, haystack: str, project_type: str | None = None
    ) -> list[PromptSuggestion]:
        candidates = await self.repo.list(active_only=True)
        matched: list[PromptSuggestion] = []
        fallback: list[PromptSuggestion] = []
        for item in candidates:
            types = {str(value).lower() for value in item.project_types or []}
            if project_type and types and project_type.lower() not in types:
                continue
            if _any_keyword_matches(haystack, item.excluded_keywords) or not _all_keywords_match(haystack, item.required_keywords):
                continue
            if item.is_fallback:
                fallback.append(item)
            elif _any_keyword_matches(haystack, item.matching_keywords):
                matched.append(item)
        return (matched or fallback)[:3]
