"""Switch the host session to another agent profile (model, endpoint, system prompt, tools)."""


TOOL_VERSION = "2026-05-22"

from __future__ import annotations

from pathlib import Path
from typing import Any

from harzoo.agent.components.paths import ConfigPaths
from harzoo.agent.components.util import load_yaml_front_matter_markdown
from harzoo.agent.kernel.tool import Context, Tool, ToolResult


def _profile_search_candidates(paths: ConfigPaths) -> list[Path]:
    seen: set[Path] = set()
    ordered: list[Path] = []
    for candidate in (
        *([paths.main_agent_path] if paths.main_agent_path else []),
        *sorted({p.resolve() for p in paths.agents_root.glob("*.md") if p.is_file()}),
        *paths.subagent_paths,
    ):
        resolved = candidate.resolve()
        if resolved not in seen:
            seen.add(resolved)
            ordered.append(resolved)
    return ordered


def _resolve_agent_profile_path(agent_name: str | None, paths: ConfigPaths) -> Path:
    if agent_name is None or not str(agent_name).strip():
        if paths.main_agent_path is None:
            raise FileNotFoundError(f"No main agent profile under {paths.agents_root}")
        return paths.main_agent_path.resolve()

    raw = str(agent_name).strip()
    expanded = Path(raw).expanduser()

    if expanded.is_absolute():
        resolved = expanded.resolve()
        if not resolved.is_file():
            raise FileNotFoundError(f"Profile not found: {resolved}")
        if resolved.suffix.lower() != ".md":
            raise ValueError(f"Profile must be a markdown file: {resolved}")
        return resolved

    under_agents = (paths.agents_root / expanded).resolve()
    if under_agents.is_file():
        return under_agents
    if under_agents.suffix.lower() != ".md":
        with_suffix = under_agents.with_suffix(".md")
        if with_suffix.is_file():
            return with_suffix

    stem = expanded.stem if expanded.suffix else raw
    direct = paths.agents_root / f"{stem}.md"
    if direct.is_file():
        return direct.resolve()

    for candidate in paths.subagent_paths:
        if candidate.stem == stem or candidate.name == f"{stem}.md":
            return candidate.resolve()

    normalized = raw.lower()
    for candidate in _profile_search_candidates(paths):
        try:
            meta, _ = load_yaml_front_matter_markdown(candidate)
        except (OSError, ValueError):
            continue
        name = str(meta.get("name") or "").strip().lower()
        if name == normalized:
            return candidate.resolve()

    raise FileNotFoundError(
        f"Agent profile {raw!r} not found under {paths.agents_root} (searched root profiles and subagents)"
    )


class SwitchProfileTool(Tool):
    name = "SwitchProfile"
    description = (
        "Switch the main agent to a different profile (markdown under the agents config directory). "
        "Updates model, API endpoint, system prompt, and the tool list exposed to the model for subsequent turns. "
        "Only available in the main engine session (not inside a nested subtask)."
    )
    parameters = {
        "type": "object",
        "properties": {
            "agent_name": {
                "type": "string",
                "description": (
                    "Profile file stem or path: e.g. `coder`, `coder.md`, or an absolute path to a profile markdown file."
                ),
            },
        },
        "required": ["agent_name"],
    }

    def execute(self, agent_name: str, *, ctx: Context | None = None, **_: Any) -> ToolResult:
        if ctx is None:
            return ToolResult.failure("SwitchProfile requires host Context", code="INVALID_CONTEXT")
        if ctx.emitter is None:
            return ToolResult.failure("SwitchProfile is not available in nested agent runs", code="INVALID_CONTEXT")

        raw = str(agent_name).strip()
        if not raw:
            return ToolResult.failure("agent_name is required", code="INVALID_ARGUMENTS")

        try:
            profile_path = _resolve_agent_profile_path(raw, ctx.config_paths)
            ctx.agent.rebind_profile(profile_path, subagent_paths=ctx.config_paths.subagent_paths)
        except Exception as exc:  # noqa: BLE001
            return ToolResult.failure(f"{type(exc).__name__}: {exc}", code="SWITCH_PROFILE_FAILED")

        if ctx.emitter is not None:
            cfg = ctx.agent.llm.llm_config
            ctx.emitter.emit_llm_ready(
                cfg.model_name,
                profile_path.stem,
                max_context_tokens=cfg.max_context_tokens,
            )

        return ToolResult.success(
            {
                "profile_path": str(profile_path),
                "model_name": ctx.agent.llm.llm_config.model_name,
                "max_context_tokens": ctx.agent.llm.llm_config.max_context_tokens,
            }
        )


TOOL = SwitchProfileTool
