from __future__ import annotations

from app.modules.agent.engine.orchestrator.models import (
    AttachmentRef,
    FileRef,
    OutputContract,
    OutputSection,
    RoutingMeta,
    Scenario,
    TaskConstraints,
    TaskSpec,
)


class TaskSpecBuilder:
    def build(
        self,
        *,
        task_id: str,
        dialog_session_id: str,
        rag_session_id: str,
        mode: str,
        message: str,
        route: RoutingMeta,
        attachments: list[dict],
        files: list[dict],
        rag_items: list[dict],
        rag_context: str,
        confluence_context: str,
        files_map: dict[str, dict],
    ) -> TaskSpec:
        scenario = self._detect_scenario(mode=mode, message=message, route=route)
        output_contract = self._output_contract(scenario)
        constraints = self._constraints_for(scenario)
        metadata = {
            "rag_items": rag_items,
            "rag_context": rag_context,
            "confluence_context": confluence_context,
            "files_map": files_map,
        }
        return TaskSpec(
            task_id=task_id,
            dialog_session_id=dialog_session_id,
            rag_session_id=rag_session_id,
            mode=mode,
            user_message=message,
            scenario=scenario,
            routing=route,
            attachments=self._map_attachments(attachments),
            files=self._map_files(files),
            constraints=constraints,
            output_contract=output_contract,
            metadata=metadata,
        )

    def _detect_scenario(self, *, mode: str, message: str, route: RoutingMeta) -> Scenario:
        mode_key = (mode or "").strip().lower()
        text = (message or "").strip().lower()

        if mode_key == "analytics_review":
            return Scenario.ANALYTICS_REVIEW
        if "gherkin" in text or "cucumber" in text:
            return Scenario.GHERKIN_MODEL
        if any(token in text for token in ("review analytics", "ревью аналитики", "проведи ревью")):
            return Scenario.ANALYTICS_REVIEW
        if any(token in text for token in ("сформируй документацию", "документацию из аналитики", "generate docs")):
            return Scenario.DOCS_FROM_ANALYTICS
        if any(token in text for token in ("точечн", "измени файл", "targeted edit", "patch file")):
            return Scenario.TARGETED_EDIT
        if route.domain_id == "project" and route.process_id == "edits":
            return Scenario.TARGETED_EDIT
        if route.domain_id == "docs" and route.process_id == "generation":
            return Scenario.DOCS_FROM_ANALYTICS
        if route.domain_id == "project" and route.process_id == "qa" and self._looks_like_explain_request(text):
            return Scenario.EXPLAIN_PART
        if route.domain_id == "project" and route.process_id == "qa" and "review" in text:
            return Scenario.ANALYTICS_REVIEW
        return Scenario.GENERAL_QA

    def _looks_like_explain_request(self, text: str) -> bool:
        markers = (
            "explain",
            "how it works",
            "sequence",
            "diagram",
            "obiasni",
            "kak rabotaet",
            "kak ustroeno",
            "объясни",
            "как работает",
            "как устроен",
            "диаграм",
        )
        return any(marker in text for marker in markers)

    def _map_attachments(self, attachments: list[dict]) -> list[AttachmentRef]:
        mapped: list[AttachmentRef] = []
        for item in attachments:
            value = str(item.get("url") or item.get("value") or "").strip()
            if not value:
                continue
            raw_type = str(item.get("type") or "http_url").strip().lower()
            attachment_type = raw_type if raw_type in {"confluence_url", "http_url", "file_ref"} else "http_url"
            mapped.append(AttachmentRef(type=attachment_type, value=value))
        return mapped

    def _map_files(self, files: list[dict]) -> list[FileRef]:
        mapped: list[FileRef] = []
        for item in files:
            path = str(item.get("path") or "").replace("\\", "/").strip()
            if not path:
                continue
            mapped.append(
                FileRef(
                    path=path,
                    content=str(item.get("content") or ""),
                    content_hash=str(item.get("content_hash") or ""),
                )
            )
        return mapped

    def _constraints_for(self, scenario: Scenario) -> TaskConstraints:
        if scenario in {Scenario.DOCS_FROM_ANALYTICS, Scenario.TARGETED_EDIT, Scenario.GHERKIN_MODEL}:
            return TaskConstraints(allow_writes=True, max_steps=16, max_retries_per_step=2, step_timeout_sec=120)
        return TaskConstraints(allow_writes=False, max_steps=12, max_retries_per_step=2, step_timeout_sec=90)

    def _output_contract(self, scenario: Scenario) -> OutputContract:
        if scenario == Scenario.EXPLAIN_PART:
            return OutputContract(result_type="answer", sections=[OutputSection(name="summary", format="markdown")])
        if scenario == Scenario.ANALYTICS_REVIEW:
            return OutputContract(
                result_type="review_report",
                sections=[
                    OutputSection(name="findings", format="markdown"),
                    OutputSection(name="recommendations", format="markdown"),
                ],
            )
        if scenario in {Scenario.DOCS_FROM_ANALYTICS, Scenario.TARGETED_EDIT}:
            return OutputContract(result_type="changeset", sections=[OutputSection(name="changeset", format="changeset")])
        if scenario == Scenario.GHERKIN_MODEL:
            return OutputContract(
                result_type="gherkin_bundle",
                sections=[OutputSection(name="gherkin_bundle", format="gherkin")],
            )
        return OutputContract(result_type="answer", sections=[OutputSection(name="summary", format="markdown")])
