"""Project manifest (.ievo/manifest.yaml) handling."""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING

import yaml

from ievo.core.config import IEVO_MANIFEST, ensure_project_dir

if TYPE_CHECKING:
    from ievo.core.registry import Registry


@dataclass
class ProjectManifest:
    """Represents .ievo/manifest.yaml."""

    project: str
    version: str = "0.1.0"
    created: str = ""
    agents: dict[str, str] = field(default_factory=dict)
    overrides: dict[str, dict] = field(default_factory=dict)
    cli_version: str = ""

    # ── I/O ────────────────────────────────────────────────────

    @classmethod
    def load(cls, root: Path) -> ProjectManifest:
        path = root / IEVO_MANIFEST
        data = yaml.safe_load(path.read_text())
        return cls(
            project=data.get("project", root.name),
            version=data.get("version", "0.1.0"),
            created=data.get("created", ""),
            agents=data.get("agents", {}),
            overrides=data.get("overrides", {}),
            cli_version=data.get("cli_version", "") or "",
        )

    def save(self, root: Path) -> None:
        ensure_project_dir(root)
        path = root / IEVO_MANIFEST
        data = {
            "project": self.project,
            "version": self.version,
            "created": self.created,
            "agents": self.agents,
            "cli_version": self.cli_version,
        }
        if self.overrides:
            data["overrides"] = self.overrides
        path.write_text(yaml.dump(data, default_flow_style=False, sort_keys=False))

    # ── Helpers ────────────────────────────────────────────────

    def has_agent(self, name: str) -> bool:
        return name in self.agents

    def add_agent(self, name: str, version: str) -> None:
        self.agents[name] = version

    def remove_agent(self, name: str) -> None:
        self.agents.pop(name, None)
        self.overrides.pop(name, None)


def compare_template_versions(
    manifest: ProjectManifest | None,
    cli_version: str,
) -> bool:
    """Return True when project templates may be outdated.

    Compares manifest.cli_version against the running CLI version.
    Returns False for None manifest or empty cli_version (old projects
    that predate this feature -- no nagging).
    """
    if manifest is None:
        return False
    if manifest.cli_version == "":
        return False
    return manifest.cli_version != cli_version


def compare_agent_versions(
    manifest: ProjectManifest | None,
    registry: Registry | None,
) -> dict[str, tuple[str, str]]:
    """Return outdated agents as {name: (installed_version, latest_version)}.

    An agent is outdated when its installed version (from manifest) differs
    from the latest version in the registry. String inequality is used —
    no SemVer parsing required.

    Agents in the manifest but absent from the registry are skipped.
    Returns an empty dict if either manifest or registry is None.
    """
    if manifest is None or registry is None:
        return {}

    result: dict[str, tuple[str, str]] = {}
    for name, installed_version in manifest.agents.items():
        entry = registry.get(name)
        if entry is None:
            continue
        if entry.version != installed_version:
            result[name] = (installed_version, entry.version)
    return result
