"""Chat and classic chat flow. Depends on common, core_lifecycle, run (UI action helpers)."""

import json
import shlex
import subprocess
import sys
from typing import Any, Callable, Dict, Optional, Tuple

import questionary
from rich.align import Align
from rich.markdown import Markdown
from rich.panel import Panel
from rich.rule import Rule
from rich.text import Text

from armorer.config import ConfigManager
from armorer.messaging import InboxOutboxRelay
from armorer.messaging.bridge import (
    _sanitize_bridge_user_text,
    build_local_cli_task_metadata,
    format_queue_cleanup_note,
)
from armorer.cli import common as cli_common
from armorer.cli import run as cli_run


def parse_chat_response_result(result: Dict[str, Any]) -> Tuple[str, bool, Optional[Dict[str, Any]]]:
    raw_result = result.get("raw")
    if isinstance(raw_result, dict):
        raw_response = raw_result.get("response", "{}")
        resp_data: Any = None
        if isinstance(raw_response, dict):
            resp_data = raw_response
        else:
            try:
                resp_data = json.loads(raw_response)
            except Exception:
                resp_data = None
        if isinstance(resp_data, dict):
            raw_message = resp_data.get("message")
            if raw_message is None or (isinstance(raw_message, str) and not raw_message.strip()):
                raw_message = resp_data.get("error")
            if raw_message is None or (isinstance(raw_message, str) and not raw_message.strip()):
                raw_message = result.get("message") or "Input required."
            message = str(raw_message)
            requires_confirmation = bool(resp_data.get("requires_confirmation", False))
            ui_action = resp_data.get("ui_action") if isinstance(resp_data.get("ui_action"), dict) else None
            return message, requires_confirmation, ui_action
        if isinstance(resp_data, str):
            message = resp_data.strip()
            if message:
                return message, False, None
            return str(result.get("message") or "Input required."), False, None
        if isinstance(resp_data, (int, float, bool)):
            return str(resp_data), False, None
        if isinstance(resp_data, list):
            return json.dumps(resp_data), False, None
        raw_text = str(raw_response or "").strip()
        if raw_text in {"", "{}", "null"}:
            return str(result.get("message") or "Input required."), False, None
        return raw_text, False, None
    return str(result.get("message") or "Timed out waiting for Armorer Core response."), False, None


def wait_for_chat_result(
    relay: InboxOutboxRelay, task_id: str, busy_message: str = "[bold cyan]🛡️  Armorer is thinking..."
) -> Dict[str, Any]:
    with cli_common.console.status(busy_message) as status:
        return relay.wait_for_response(
            task_id,
            timeout=300,
            poll_interval=0.5,
            status_callback=lambda msg: status.update(_sanitize_bridge_user_text(str(msg or ""))[:240]),
        )


def print_classic_chat_cleanup_note(relay: InboxOutboxRelay, *, ui_width: int) -> None:
    note = format_queue_cleanup_note(getattr(relay, "last_submit_cleanup", []))
    if not note:
        return
    cli_common.console.print(Rule(style="dim black"), width=ui_width)
    cli_common.console.print(f"[dim]{note}[/dim]")


def run_classic_chat_interactive_command(command: str) -> str:
    try:
        args = shlex.split(command)
    except ValueError as exc:
        safe_detail = _sanitize_bridge_user_text(str(exc).strip() or exc.__class__.__name__)[:300]
        return f"Interactive command failed to parse: {safe_detail}"
    try:
        result = subprocess.run(args)
        rc = int(getattr(result, "returncode", 0))
        return f"Interactive command finished with exit code {rc}."
    except Exception as e:
        safe_detail = _sanitize_bridge_user_text(str(e).strip() or e.__class__.__name__)[:300]
        return f"Interactive command failed: {safe_detail}"


def prompt_for_classic_chat_ui_action(action: Dict[str, Any]) -> Optional[str]:
    atype = str(action.get("type", "")).strip().lower()
    message = _sanitize_bridge_user_text(cli_run.ui_action_prompt_message(action))

    if atype == "select":
        opts = [str(opt) for opt in action.get("options", []) if str(opt).strip()]
        if not opts:
            return None
        choices = [questionary.Choice(title=_sanitize_bridge_user_text(opt)[:120], value=opt) for opt in opts]
        choices.append(questionary.Choice("Cancel setup", value="__cancel__"))
        selected = questionary.select(message, choices=choices).ask()
        if selected is None or selected == "__cancel__":
            return None
        return f"UI selection: {selected}"

    if atype == "confirm":
        confirmed = questionary.confirm(message, default=bool(action.get("default", True))).ask()
        if confirmed is None:
            return "UI confirmation: no"
        return "UI confirmation: yes" if confirmed else "UI confirmation: no"

    if atype == "text":
        default = str(action.get("default", "") or "").strip()
        sensitive = cli_run.is_sensitive_text_ui_action(action)
        prompt_factory = questionary.password if sensitive else questionary.text
        prompt_lines = [message]
        if sensitive and default:
            prompt_lines.append("Stored value available; press Enter to keep it.")
        prompt_lines.append("Type /cancel to abort.")
        prompt_message = "\n".join(prompt_lines)
        prompt_default = "" if sensitive else default
        while True:
            value = prompt_factory(prompt_message, default=prompt_default).ask()
            if value is None:
                return None
            value = str(value).strip()
            if value.lower() == "/cancel":
                return None
            if not value:
                if default:
                    value = "skip" if sensitive else default
                else:
                    cli_common.console.print("[yellow]Input required.[/yellow]")
                    continue
            return f"UI input: {value}"

    if atype == "interactive_command":
        cmd = str(action.get("command", "")).strip()
        reason = _sanitize_bridge_user_text(str(action.get("reason", "")).strip())
        display_cmd = _sanitize_bridge_user_text(cmd)
        if not cmd:
            return "Interactive command was empty and could not be run."
        prompt_lines = [line for line in [message, reason] if line]
        prompt_lines.append(f"Command: {display_cmd}")
        run_now = questionary.confirm("\n\n".join(prompt_lines), default=True).ask()
        if not run_now:
            return "User canceled interactive command."
        return run_classic_chat_interactive_command(cmd)

    return None


def run_classic_chat_task(
    relay: InboxOutboxRelay,
    task_id: str,
    *,
    ui_width: int,
    metadata: Optional[Dict[str, Any]] = None,
) -> None:
    current_task_id = task_id
    try:
        while current_task_id:
            result = wait_for_chat_result(relay, current_task_id)
            message, requires_confirmation, ui_action = parse_chat_response_result(result)
            safe_message = _sanitize_bridge_user_text(str(message or ""))

            cli_common.console.print()
            cli_common.console.print(Panel(
                Markdown(safe_message),
                title="[bold cyan]🛡️  Armorer Core[/bold cyan]",
                border_style="cyan",
                padding=(1, 2),
            ))
            cli_common.console.print()

            if ui_action:
                followup = prompt_for_classic_chat_ui_action(ui_action)
                if followup is None:
                    followup = "cancel"
                if followup == "User canceled interactive command.":
                    cli_common.console.print(Rule(style="dim black"), width=ui_width)
                    cli_common.console.print("[yellow]Interactive command canceled.[/yellow]")
                    return
                if followup == "Interactive command was empty and could not be run.":
                    cli_common.console.print(Rule(style="dim black"), width=ui_width)
                    cli_common.console.print("[yellow]Interactive command could not be run because no command was provided.[/yellow]")
                    return
                if str(ui_action.get("type", "")).strip().lower() == "interactive_command":
                    safe_followup = _sanitize_bridge_user_text(str(followup or ""))[:300]
                    cli_common.console.print(Rule(style="dim black"), width=ui_width)
                    cli_common.console.print(f"[cyan]{safe_followup}[/cyan]")
                    return
                display_followup = cli_run.redact_ui_followup_for_display(ui_action, followup)
                safe_display_followup = _sanitize_bridge_user_text(str(display_followup or ""))
                cli_common.console.print(Rule(style="dim black"), width=ui_width)
                cli_common.console.print(f"👤 You: {safe_display_followup}")
                current_task_id = relay.submit_task(prompt=followup, source="cli", metadata=metadata)
                print_classic_chat_cleanup_note(relay, ui_width=ui_width)
                continue

            if requires_confirmation:
                confirm = questionary.select(
                    "Select action:",
                    choices=["Yes, proceed", "No, cancel"],
                ).ask()
                cli_common.console.print(Rule(style="dim black"), width=ui_width)
                if confirm and "Yes" in confirm:
                    cli_common.console.print("👤 You: Yes")
                    current_task_id = relay.submit_task(prompt="Yes", source="cli", metadata=metadata)
                    print_classic_chat_cleanup_note(relay, ui_width=ui_width)
                    continue
                cli_common.console.print("👤 You: No")
                current_task_id = relay.submit_task(prompt="No", source="cli", metadata=metadata)
                print_classic_chat_cleanup_note(relay, ui_width=ui_width)
                continue
            return
    except Exception as e:
        safe_detail = _sanitize_bridge_user_text(str(e).strip() or e.__class__.__name__)[:300]
        cli_common.console.print(f"[red]Failed to contact Armorer Core: {safe_detail}[/red]")


def chat_command_impl(
    *,
    ensure_armorer_core: Callable[[], None],
    check_notifications: Callable[[], None],
    print_logo: Callable[[], None],
    armorer_config_dir: Callable[[], Any],
) -> None:
    """Run the chat flow (Textual or classic fallback)."""
    from dotenv import load_dotenv
    load_dotenv(override=True)
    ensure_armorer_core()
    check_notifications()

    if sys.stdin.isatty():
        try:
            from armorer.ui.chat_textual import ArmorerChatApp
            ArmorerChatApp().run()
            return
        except Exception as e:
            safe_detail = _sanitize_bridge_user_text(str(e).strip() or e.__class__.__name__)[:300]
            cli_common.console.print(f"[dim]Textual chat unavailable, falling back to classic chat: {safe_detail}[/dim]")

    print_logo()
    terminal_width = cli_common.console.width or 80
    ui_width = min(terminal_width, 100)

    cli_common.console.print(Align.center(Rule("Armorer Secure Session", style="cyan", characters="─"), width=ui_width))
    cli_common.console.print()
    welcome_text = Text(
        "Welcome. I am the Armorer Core.\nYou are connected via the CLI Interface.\n\n",
        justify="center",
    )
    welcome_text.append("Type 'exit' to end session.", style="dim")
    cli_common.console.print(Align.center(
        Panel(
            welcome_text,
            border_style="cyan",
            title="🛡️ Armorer v0.1",
            width=min(ui_width, 80),
            padding=(1, 2),
        ),
        width=ui_width,
    ))
    cli_common.console.print()

    config_dir = armorer_config_dir()
    cm = ConfigManager()
    relay = InboxOutboxRelay(
        config_dir=config_dir,
        signing_key=cm.get_key("ARMORER_TASK_SIGNING_KEY"),
    )
    cli_metadata = build_local_cli_task_metadata()

    while True:
        try:
            cli_common.console.print(Rule(style="dim black"), width=ui_width)
            user_input = questionary.text("👤 You:").ask()
            if user_input is None or user_input.lower() in ["exit", "quit", "bye"]:
                break
            if not user_input.strip():
                continue
            task_id = relay.submit_task(prompt=user_input, source="cli", metadata=cli_metadata)
            print_classic_chat_cleanup_note(relay, ui_width=ui_width)
            run_classic_chat_task(relay, task_id, ui_width=ui_width, metadata=cli_metadata)
        except KeyboardInterrupt:
            break
        except Exception as e:
            safe_detail = _sanitize_bridge_user_text(str(e).strip() or e.__class__.__name__)[:300]
            cli_common.console.print(f"[red]Failed to contact Armorer Core: {safe_detail}[/red]")
