from __future__ import annotations

import json
from typing import Any

from app.core.config import settings
from app.generation.llm_client import LLMClientError
from app.llm import LLMRouter
from app.models.repo_resolution import RepoCandidate


class RepoClassifierResult:
    def __init__(
        self,
        *,
        selected_repo_id: str | None,
        confidence: float,
        clarification_message: str | None = None,
        candidate_repo_ids: list[str] | None = None,
    ):
        self.selected_repo_id = selected_repo_id
        self.confidence = confidence
        self.clarification_message = clarification_message
        self.candidate_repo_ids = candidate_repo_ids or []


class RepoClassifier:
    """LLM-based repo selection from indexed catalog + user question."""

    SYSTEM_PROMPT = """You identify which indexed repository a developer question refers to.
Return JSON with keys:
- selected_repo_id: string repo_id from the catalog, or null if unclear
- confidence: float 0-1
- clarification_message: user-facing question when unclear (optional)
- candidate_repo_ids: array of repo_id strings that might apply (optional)

Rules:
- selected_repo_id MUST be one of the provided repo_id values or null
- Use low confidence when the question does not imply a specific service/product
- Prefer null over guessing when multiple repos are equally plausible
"""

    def __init__(
        self,
        llm_client: LLMRouter | None = None,
        *,
        router: LLMRouter | None = None,
    ):
        self._llm = llm_client or router or LLMRouter()

    async def classify(
        self,
        question: str,
        candidates: list[RepoCandidate],
    ) -> RepoClassifierResult | None:
        if not candidates:
            return None

        catalog = [
            {
                "repo_id": c.repo_id,
                "label": c.label,
                "indexing_status": c.indexing_status,
            }
            for c in candidates
        ]
        user_content = json.dumps(
            {"question": question, "repos": catalog},
            separators=(",", ":"),
        )
        messages = [
            {"role": "system", "content": self.SYSTEM_PROMPT},
            {"role": "user", "content": user_content},
        ]
        try:
            data = await self._llm.complete_json(
                messages,
                model=settings.LLM_INTENT_MODEL,
            )
        except LLMClientError:
            return None
        return self._parse(data, candidates)

    def _parse(
        self,
        data: dict[str, Any],
        candidates: list[RepoCandidate],
    ) -> RepoClassifierResult | None:
        try:
            allowed = {c.repo_id for c in candidates}
            raw_id = data.get("selected_repo_id")
            selected_repo_id = str(raw_id).strip() if raw_id else None
            if selected_repo_id and selected_repo_id not in allowed:
                selected_repo_id = None
            confidence = float(data.get("confidence", 0.0))
            confidence = max(0.0, min(1.0, confidence))
            message = data.get("clarification_message")
            clarification_message = str(message).strip() if message else None
            raw_candidates = data.get("candidate_repo_ids", [])
            candidate_repo_ids: list[str] = []
            if isinstance(raw_candidates, list):
                candidate_repo_ids = [
                    str(item) for item in raw_candidates if str(item) in allowed
                ]
            return RepoClassifierResult(
                selected_repo_id=selected_repo_id,
                confidence=confidence,
                clarification_message=clarification_message,
                candidate_repo_ids=candidate_repo_ids,
            )
        except (TypeError, ValueError):
            return None
