"""TUI helper functions — all real logic lives here.

This module contains every TUI operation as a standalone function. Each
function receives all of its dependencies as parameters (client, file paths,
etc.) and has no implicit state. This makes them independently testable and
callable from anywhere — the TUI app, haiv commands, tests — without pulling
in a dependency graph.

The Tui class (tui.py) is a thin convenience wrapper that holds pre-loaded
dependencies and delegates to these functions. No logic should live in the
Tui class itself. If you're adding a new TUI capability, put it here as a
function, then add a one-line passthrough in the Tui class.

Naming convention: noun_verb (e.g. sessions_refresh, hud_update) so that
related functions sort together.
"""

from __future__ import annotations

import sys
from pathlib import Path
from typing import TYPE_CHECKING

from haiv.helpers.sessions import (
    Session,
    get_most_recent_session_for_mind,
    load_sessions,
    resolve_session,
)
from haiv.helpers.tui.protocol import ModelClient
from haiv.helpers.tui.TuiModel import ActiveMindRaw, GitRaw, SessionEntry, SessionsRaw
from haiv.wrappers.git import BranchStats, Git

if TYPE_CHECKING:
    from haiv.helpers.tui.terminal import TerminalManager


# -- Model operations --


def sessions_refresh(
    client: ModelClient,
    sessions_file: Path,
    *,
    git: Git | None = None,
) -> None:
    """Read sessions from disk and push them into the TUI model.

    Loads sessions and pushes them as raw data. If a Git instance is
    provided, branch stats are gathered and pushed separately.

    Args:
        client: A TuiClient or TuiLocalClient.
        sessions_file: Path to the sessions.ig.toml file.
        git: Optional Git instance for computing branch stats.
    """
    # -- Gather --
    sessions = load_sessions(sessions_file)
    entries = [
        SessionEntry(
            id=s.id, mind=s.mind, task=s.task,
            short_id=s.short_id, status=s.status,
            description=s.description, parent_id=s.parent_id,
            branch=s.branch, base_branch=s.base_branch,
            bounce=s.bounce,
        )
        for s in sessions
    ]
    git_branches: dict[str, BranchStats] = {}
    if git is not None:
        for s in sessions:
            if s.branch:
                try:
                    git_branches[s.branch] = git.branch_stats(s.branch, s.base_branch)
                except Exception:
                    pass

    # -- Send --
    git_raw = GitRaw(branches=git_branches) if git_branches else None
    client.write_raw(sessions=SessionsRaw(entries=entries), git=git_raw)


def active_mind_set(client: ModelClient, mind_name: str) -> None:
    """Push the active mind into the TUI model.

    Args:
        client: A TuiClient or TuiLocalClient.
        mind_name: Name of the mind now active in the hud.
    """
    client.write_raw(active_mind=ActiveMindRaw(mind=mind_name))


# -- Workspace operations --


def workspace_start(terminal: TerminalManager) -> None:
    """Ensure the haiv workspace exists (window, hud tab, layout).

    Args:
        terminal: TerminalManager for pane operations.
    """
    terminal.ensure_workspace()


# -- Mind operations --


def mind_launch(
    terminal: TerminalManager,
    client: ModelClient,
    sessions_file: Path,
    mind_name: str,
    haiv_root: Path,
    *,
    task: str | None = None,
    parent_id: str = "",
    git: Git | None = None,
) -> Session:
    """Put a mind in the hud — switching, launching, or restarting as needed.

    If the mind is already active in the hud, this is a no-op (just refreshes
    the model). If the mind has a parked pane, switches to it. Otherwise,
    resolves a session and launches a new pane with Claude Code.

    Args:
        terminal: TerminalManager for pane operations.
        client: A TuiClient or TuiLocalClient for model updates.
        sessions_file: Path to sessions.ig.toml.
        mind_name: Name of the mind to launch.
        haiv_root: Project root path (for environment variables).
        task: Task description for new sessions. If None and no existing
              session, creates one with an empty task.
        parent_id: Parent session id (for delegation chains).
        git: Optional Git instance for computing branch stats.

    Returns:
        The session for this mind.
    """
    # Already showing — just refresh the model
    if terminal.is_mind_active(mind_name):
        session = get_most_recent_session_for_mind(sessions_file, mind_name)
        if session is not None:
            print(
                f"Mind '{mind_name}' is already active in the hud.\n"
                f"To relaunch here: hv start {mind_name} --here"
            )
            sessions_refresh(client, sessions_file, git=git)
            active_mind_set(client, mind_name)
            return session

    # Parked pane exists — switch to it
    if terminal.is_mind_parked(mind_name):
        session = get_most_recent_session_for_mind(sessions_file, mind_name)
        if session is not None:
            terminal.switch_to_mind(mind_name)
            sessions_refresh(client, sessions_file, git=git)
            active_mind_set(client, mind_name)
            return session

    # Need to launch a new pane — resolve or create session
    session = resolve_session(sessions_file, mind_name, task=task, parent_id=parent_id)

    sessions_refresh(client, sessions_file, git=git)
    active_mind_set(client, mind_name)

    # Build claude command and launch
    claude_cmd = build_claude_command(
        mind_name, session.claude_session_id,
        autonomous=session.autonomous, allowed_paths=session.allowed_paths,
    )
    env = build_env(mind_name, session.id, haiv_root)
    terminal.launch_in_mind_pane(mind_name, env, [claude_cmd])

    return session


def mind_try_send_text(
    terminal: TerminalManager, mind_name: str, text: str, *, submit: bool = False,
) -> bool:
    """Send text to a mind's pane, returning whether it was found.

    Args:
        terminal: TerminalManager for pane operations.
        mind_name: Name of the mind to send text to.
        text: Text to send.
        submit: If True, submit the text as if Enter was pressed.

    Returns:
        True if the mind's pane was found and text was sent.
    """
    return terminal.try_send_text_to_mind(mind_name, text, submit=submit)


def mind_send_text(
    terminal: TerminalManager, mind_name: str, text: str, *, submit: bool = False,
) -> None:
    """Send text to a mind's pane, raising if not found.

    Args:
        terminal: TerminalManager for pane operations.
        mind_name: Name of the mind to send text to.
        text: Text to send.
        submit: If True, submit the text as if Enter was pressed.

    Raises:
        CommandError: If no pane found for the mind.
    """
    terminal.send_text_to_mind(mind_name, text, submit=submit)


def mind_close_pane(terminal: TerminalManager, mind_name: str) -> None:
    """Close a parked mind's pane.

    Args:
        terminal: TerminalManager for pane operations.
        mind_name: Name of the mind whose parked pane to close.
    """
    terminal.close_parked_mind(mind_name)


# -- Claude launch helpers --
# These aren't really TUI concerns — they're about constructing the claude
# invocation. They live here for now because mind_launch is the primary
# consumer, but they should move if a better home emerges.


def build_claude_command(
    mind_name: str,
    claude_session_id: str,
    *,
    autonomous: bool = False,
    allowed_paths: list[str] | None = None,
) -> str:
    """Build the claude CLI command for launching a mind."""
    from haiv.util import shell_quote

    prompt = f"Run `hv become {mind_name}`"
    tools = [
        f"Bash(hv become {mind_name})",
        "Bash(cd *)",
        "Bash(hv chart *)",
        "Bash(hv pop *)",
        "Bash(git status)",
        "Bash(git diff *)",
        "Bash(git log *)",
    ]
    if autonomous:
        tools.extend([
            "Bash(git add *)",
            "Bash(git commit *)",
        ])
        for path in allowed_paths or []:
            tools.append(f"Edit(/{path}**)")
            tools.append(f"Write(/{path}**)")
    allowed = " ".join(tools)
    return (
        f"claude {shell_quote(prompt)} "
        f"--session-id {shell_quote(claude_session_id)} "
        f"--allowedTools {shell_quote(allowed)}"
    )


def build_env(mind_name: str, session_id: str, haiv_root: Path) -> dict[str, str]:
    """Build environment variables for a mind pane."""
    from haiv._infrastructure.env import HV_CLAUDE_HOOK_DISPATCH, HV_MIND, HV_ROOT, HV_SESSION
    from haiv._infrastructure.TuiServer._TuiIpc import pipe_address

    return {
        HV_MIND: mind_name,
        HV_SESSION: session_id,
        HV_ROOT: str(haiv_root),
        HV_CLAUDE_HOOK_DISPATCH: pipe_address(haiv_root.name),
    }


