"""Shared knowledge bank — preseedable persistent memory accessible to all agents."""

from __future__ import annotations

from dataclasses import dataclass

from desktop_env.desktop_env import DesktopEnv

from .tools import run_bash_on_vm


@dataclass
class KnowledgeEntry:
    category: str
    content: str
    source: str  # "preseed" for built-in, or the agent name that added it


class KnowledgeBank:
    """Shared memory that agents can read from and write to.

    Pre-seeded with OSWorld environment facts and domain-specific hints.
    Runtime entries (added by agents during a task) can be cleared between
    tasks while preserving the pre-seeded knowledge.
    """

    def __init__(self) -> None:
        self._entries: list[KnowledgeEntry] = []
        self._preseed()

    # -- Public API --------------------------------------------------------

    def add(self, category: str, content: str, source: str = "agent") -> None:
        for e in self._entries:
            if e.category == category and e.content == content:
                return
        self._entries.append(KnowledgeEntry(category=category, content=content, source=source))

    def query(self, category: str | None = None) -> list[KnowledgeEntry]:
        if category is None:
            return list(self._entries)
        return [e for e in self._entries if e.category == category]

    def format_for_prompt(self, categories: list[str] | None = None) -> str:
        entries = self._entries if categories is None else [e for e in self._entries if e.category in categories]
        if not entries:
            return ""
        grouped: dict[str, list[str]] = {}
        for e in entries:
            grouped.setdefault(e.category, []).append(e.content)
        lines: list[str] = ["<knowledge_bank>"]
        for cat, items in grouped.items():
            lines.append(f"[{cat}]")
            for item in items:
                lines.append(f"  - {item}")
        lines.append("</knowledge_bank>")
        return "\n".join(lines)

    def clear_runtime(self) -> None:
        """Remove all entries added by agents, keep pre-seeded ones."""
        self._entries = [e for e in self._entries if e.source == "preseed"]

    def get_app_versions(self, env: DesktopEnv) -> None:
        applications = {
            "chrome": "google-chrome --version",
            "gimp": "gimp --version",
            "libreoffice_calc": "libreoffice --version",
            "libreoffice_impress": "libreoffice --version",
            "libreoffice_writer": "libreoffice --version",
            "os": "lsb_release -a",
            "thunderbird": "thunderbird --version",
            "vlc": "vlc --version",
            "vs_code": "code --version",
        }

        for app, cmd in applications.items():
            result = run_bash_on_vm(env, cmd, timeout=30)
            output = result.output if hasattr(result, "output") else str(result)
            self.add("apps", "\n".join([cmd, output or ""]), "preseed")

    def _preseed(self) -> None:
        s = "preseed"

        # OSWorld VM environment
        self.add("environment", "OS: Ubuntu x86_64 with internet access.", s)
        self.add("environment", "Home directory: /home/user", s)
        self.add(
            "environment",
            "Display server: DISPLAY=:0. Start GUI apps with: (DISPLAY=:0 app_name &)",
            s,
        )
        self.add("environment", "Sudo password: osworld-public-evaluation", s)
        self.add("environment", "Browser: Google Chrome (click desktop icon to open).", s)
        self.add(
            "environment",
            "Chrome CDP debug port: localhost:9222 (when Chrome is running with --remote-debugging-port).",
            s,
        )

        # Application hints
        self.add(
            "apps",
            "LibreOffice: Use code agent for all spreadsheet calculations and data manipulation. Use UNO bridge for programmatic access.",
            s,
        )
        self.add(
            "apps",
            "GIMP: Images must be explicitly exported/saved to disk (File > Export As). GIMP Tasks should use the GUI instead.",
            s,
        )
        self.add(
            "apps",
            "VS Code: Settings changes must be persisted to settings.json. Just opening UI is not enough.",
            s,
        )
        self.add(
            "apps",
            "Thunderbird: Config changes must be written to prefs.js. Close the app or commit settings.",
            s,
        )

        # Evaluation hints
        self.add(
            "evaluation",
            "OSWorld evaluators check REAL system state (files on disk, CLI output, config values), NOT screenshots.",
            s,
        )
        self.add(
            "evaluation",
            "Files must be explicitly SAVED (Ctrl+S, File > Save, or via code). Unsaved changes score 0.",
            s,
        )
        self.add(
            "evaluation",
            "Browser state (bookmarks, tabs, cookies, history) is checked programmatically, not visually.",
            s,
        )
        self.add(
            "evaluation",
            "System settings are verified via gsettings/dconf/shell commands, not screenshots.",
            s,
        )

        # LibreOffice color palette (commonly needed)
        self.add(
            "colors",
            (
                "LibreOffice named colors: "
                "Black=#000000, White=#FFFFFF, Red=#FF0000, Green=#00A933, Blue=#0000FF, "
                "Yellow=#FFFF00, Cyan=#00FFFF, Magenta=#FF00FF, Orange=#FF8000"
                "Dark Red=#800000, Dark Green=#008000, Dark Blue=#000080, "
                "Orange=#FFA500, Light Grey=#C0C0C0, Dark Grey=#808080"
            ),
            s,
        )
