"""The keep / archive / discard question when an interactive ``hermes -w`` session exits with a
dirty or unpushed worktree.

Asked once, in the foreground, after the UI released the terminal: by the classic CLI in
``_tui_shutdown`` (before ``_run_cleanup`` arms the exit watchdog) and by the TUI launcher after
the Ink child exits. The answer is applied right there, synchronously, before any exit watchdog
or teardown runs: a large archive is never cut short, and a failed one keeps the tree. Only a real interactive session
asks: ``-q``/``-z``/cron/gateway never reach these call sites, a missing TTY or a signal-driven
shutdown skips the question, and every skip means Keep (the sweep archives the tree later).
"""

from __future__ import annotations

import logging
import os
import sys
from typing import Optional

from agent.i18n import t

logger = logging.getLogger(__name__)

KEEP, ARCHIVE, DISCARD = "keep", "archive", "discard"


def _has_tty() -> bool:
    try:
        return sys.stdin.isatty() and sys.stdout.isatty()
    except (AttributeError, ValueError):
        return False


def worktree_state(info: dict) -> Optional[str]:
    """``"dirty"``, ``"unpushed"`` or None (clean and pushed / gone)."""
    from hermes_cli.worktree_ops import _worktree_has_unpushed_commits, _worktree_is_dirty
    path, repo_root = info.get("path", ""), info.get("repo_root", "")
    if not path or not os.path.isdir(path):
        return None
    if _worktree_is_dirty(path, repo_root, timeout=10):
        return "dirty"
    if _worktree_has_unpushed_commits(path, timeout=10):
        return "unpushed"
    return None


def _still_in_use(path: str, grace_s: float = 5.0) -> bool:
    """Whether a live process (other than this one) still works in *path* after a short grace:
    the TUI's own backend (``tui_gateway``) takes a moment to exit after the Ink UI closes."""
    import time

    from hermes_cli.worktree_sweep import live_process_pids
    deadline = time.monotonic() + grace_s
    while live_process_pids([path], exclude=(os.getpid(),)).get(path):
        if time.monotonic() >= deadline:
            return True
        time.sleep(0.25)
    return False


def ask_exit_choice(info: Optional[dict]) -> str:
    """Ask what to do with a dirty/unpushed tree; records the answer in ``info["exit_choice"]``."""
    if not info or not _has_tty():
        return KEEP
    state = worktree_state(info)
    if state is None:
        return KEEP
    if _still_in_use(info["path"]):
        return KEEP  # something still runs in there; never offer to delete it
    from hermes_cli.curses_ui import curses_radiolist
    title = t(f"cli.worktree.exit_question_{state}", path=info["path"])
    items = [t("cli.worktree.exit_choice_keep"), t("cli.worktree.exit_choice_archive"),
             t("cli.worktree.exit_choice_discard")]
    try:  # Esc / Ctrl-C / a curses failure all resolve to Keep (index 0)
        idx = curses_radiolist(title, items, selected=0, cancel_returns=0)
    except Exception:
        logger.debug("worktree exit question failed; keeping the tree", exc_info=True)
        idx = None
    choice = {1: ARCHIVE, 2: DISCARD}.get(idx, KEEP) if isinstance(idx, int) else KEEP
    info["exit_choice"] = choice
    return choice


def apply_exit_choice(info: dict, echo) -> bool:
    """Carry out an Archive/Discard answer once -> True when the tree was handled (removed or a
    message printed). Keep (or no answer) -> False: the caller's default keep path runs."""
    choice = info.pop("exit_choice", None)
    if choice not in (ARCHIVE, DISCARD):
        return False
    path, repo_root, branch = info["path"], info["repo_root"], info.get("branch")
    if choice == ARCHIVE:
        import subprocess

        from hermes_cli.worktree_archive import ArchiveError
        from hermes_cli.worktree_sweep import archive_then_remove, sweep_settings
        echo(t("cli.worktree.archiving", path=path))
        try:
            ref, _kept, removed, why = archive_then_remove(
                repo_root, path, reason="session exit", unlock=True, exclude_pids=(os.getpid(),),
                max_untracked_mb=sweep_settings()["archive_max_file_mb"])
        except (ArchiveError, OSError, subprocess.SubprocessError, KeyboardInterrupt) as exc:
            echo(f"\033[31m{t('cli.worktree.archive_failed', path=path, error=str(exc) or 'interrupted')}\033[0m")
            return True
        name = ref.rsplit("/", 1)[-1]
        if removed:
            echo(f"\033[32m{t('cli.worktree.archived', path=path, ref=ref)}\033[0m")
        else:
            echo(f"\033[33m{t('cli.worktree.archived_kept', path=path, ref=ref, reason=why or '?')}\033[0m")
        echo(f"  {t('cli.worktree.restore_hint', name=name)}")
        return True
    from hermes_cli._subprocess_compat import noninteractive_git_env
    from hermes_cli.worktree_ops import _git_quiet, release_lsp_clients
    release_lsp_clients(path)
    _git_quiet(["worktree", "unlock", path], repo_root, log="git worktree unlock failed (non-fatal)")
    _git_quiet(["worktree", "remove", path, "--force"], repo_root, timeout=60, log="Failed to remove worktree")
    if branch and not os.path.isdir(path):
        _git_quiet(["branch", "-D", branch], repo_root, log=f"Failed to delete branch {branch}",
                   env=noninteractive_git_env())
    echo(f"\033[33m{t('cli.worktree.discarded', path=path)}\033[0m")
    return True
