from __future__ import annotations


class ProjectQaAnalyzer:
    def analyze_code(self, profile: dict, rag_items: list[dict], file_candidates: list[dict]) -> dict:
        terms = list(profile.get("terms", []) or [])
        intent = str(profile.get("intent") or "lookup")
        russian = bool(profile.get("russian"))
        findings: list[str] = []
        evidence: list[str] = []
        gaps: list[str] = []

        symbol_titles = [str(item.get("title", "") or "") for item in rag_items if str(item.get("layer", "")).startswith("C1")]
        symbol_set = set(symbol_titles)
        file_paths = [str(item.get("path", "") or item.get("source", "") or "") for item in rag_items]
        file_paths.extend(str(item.get("path", "") or "") for item in file_candidates)

        if "ConfigManager" in profile.get("entities", []) or "configmanager" in terms or "config_manager" in terms:
            alias_file = self.find_path(file_paths, "src/config_manager/__init__.py")
            if alias_file:
                findings.append(
                    "Публичный `ConfigManager` экспортируется из `src/config_manager/__init__.py` как alias на `ConfigManagerV2`."
                    if russian
                    else "Public `ConfigManager` is exported from `src/config_manager/__init__.py` as an alias to `ConfigManagerV2`."
                )
                evidence.append("src/config_manager/__init__.py")

        if "controlchannel" in {name.lower() for name in symbol_set}:
            findings.append(
                "Базовый контракт управления задает `ControlChannel`: он определяет команды `start` и `stop` для внешнего канала управления."
                if russian
                else "`ControlChannel` defines the base management contract with `start` and `stop` commands."
            )
            evidence.append("src/config_manager/v2/control/base.py")

        if "ControlChannelBridge" in symbol_set:
            findings.append(
                "`ControlChannelBridge` связывает внешний канал управления с lifecycle-методами менеджера: `on_start`, `on_stop`, `on_status`."
                if russian
                else "`ControlChannelBridge` connects the external control channel to manager lifecycle methods: `on_start`, `on_stop`, `on_status`."
            )
            evidence.append("src/config_manager/v2/core/control_bridge.py")

        implementation_files = self.find_management_implementations(file_candidates)
        if implementation_files:
            labels = ", ".join(f"`{path}`" for path in implementation_files)
            channel_names = self.implementation_names(implementation_files)
            findings.append(
                f"В коде найдены конкретные реализации каналов управления: {', '.join(channel_names)} ({labels})."
                if russian
                else f"Concrete management channel implementations were found in code: {', '.join(channel_names)} ({labels})."
            )
            evidence.extend(implementation_files)
        elif intent == "inventory":
            gaps.append(
                "В текущем контексте не удалось уверенно подтвердить конкретные файлы-реализации каналов, кроме базового контракта и bridge-слоя."
                if russian
                else "The current context does not yet confirm concrete channel implementation files beyond the base contract and bridge layer."
            )

        package_doc = self.find_management_doc(file_candidates)
        if package_doc:
            findings.append(
                f"Пакет управления прямо описывает внешние каналы через `{package_doc}`."
                if russian
                else f"The control package directly describes external channels in `{package_doc}`."
            )
            evidence.append(package_doc)

        subject = "management channels"
        if profile.get("entities"):
            subject = ", ".join(profile["entities"])
        return {
            "subject": subject,
            "findings": self.dedupe(findings),
            "evidence": self.dedupe(evidence),
            "gaps": gaps,
            "answer_mode": "inventory" if intent == "inventory" else "summary",
        }

    def analyze_docs(self, profile: dict, rag_items: list[dict]) -> dict:
        findings: list[str] = []
        evidence: list[str] = []
        for item in rag_items[:5]:
            title = str(item.get("title", "") or "")
            source = str(item.get("source", "") or "")
            content = str(item.get("content", "") or "").strip()
            if content:
                findings.append(content.splitlines()[0][:220])
            if source:
                evidence.append(source)
            elif title:
                evidence.append(title)
        return {
            "subject": "docs",
            "findings": self.dedupe(findings),
            "evidence": self.dedupe(evidence),
            "gaps": [] if findings else ["Недостаточно данных в документации." if profile.get("russian") else "Not enough data in documentation."],
            "answer_mode": "summary",
        }

    def find_management_implementations(self, file_candidates: list[dict]) -> list[str]:
        found: list[str] = []
        for item in file_candidates:
            path = str(item.get("path", "") or "")
            lowered = path.lower()
            if self.is_test_path(path):
                continue
            if any(token in lowered for token in ("http_channel.py", "telegram.py", "telegram_channel.py", "http.py")):
                found.append(path)
                continue
            content = str(item.get("content", "") or "").lower()
            if "controlchannel" in content and "class " in content:
                found.append(path)
                continue
            if ("channel" in lowered or "control" in lowered) and any(token in content for token in ("http", "telegram", "bot")):
                found.append(path)
        return self.dedupe(found)[:4]

    def implementation_names(self, paths: list[str]) -> list[str]:
        names: list[str] = []
        for path in paths:
            stem = path.rsplit("/", 1)[-1].rsplit(".", 1)[0]
            label = stem.replace("_", " ").strip()
            if label and label not in names:
                names.append(label)
        return names

    def find_management_doc(self, file_candidates: list[dict]) -> str | None:
        for item in file_candidates:
            path = str(item.get("path", "") or "")
            if self.is_test_path(path):
                continue
            content = str(item.get("content", "") or "").lower()
            if any(token in content for token in ("каналы внешнего управления", "external control channels", "http api", "telegram")):
                return path
        return None

    def find_path(self, paths: list[str], target: str) -> str | None:
        for path in paths:
            if path == target:
                return path
        return None

    def dedupe(self, items: list[str]) -> list[str]:
        seen: list[str] = []
        for item in items:
            if item and item not in seen:
                seen.append(item)
        return seen

    def is_test_path(self, path: str) -> bool:
        lowered = path.lower()
        return lowered.startswith("tests/") or "/tests/" in lowered or lowered.startswith("test_") or "/test_" in lowered
