from __future__ import annotations

from pathlib import Path
from typing import Any

from researchclaw.config import ResearchClawConfig
from researchclaw.fsm import TrialAborted
from researchclaw.fsm.states import State
from researchclaw.models import TrialMeta
from researchclaw.sandbox import SandboxManager


def _persist_autopilot(config: ResearchClawConfig, project_dir: Path) -> None:
    """Persist the current autopilot setting to the project config file."""
    project_config_path = (
        SandboxManager.sandbox_path(project_dir)
        / "project_settings"
        / "researchclaw.yaml"
    )
    if project_config_path.exists():
        saved = ResearchClawConfig.load_from_yaml(project_config_path)
    else:
        saved = ResearchClawConfig()
    saved.autopilot = config.autopilot
    saved.save_to_yaml(project_config_path)


# --- Decision options ---

DECISION_OPTIONS = """\
What would you like to do next?

  (1) New experiment  — start a new trial
  (2) View summary    — browse trial history
  (3) Settings        — view/edit configuration
  (4) Merge           — merge experiment code (not yet implemented)
  (5) Quit            — exit ResearchClaw\
"""

OPTION_MAP: dict[str, State | str] = {
    "1": State.EXPERIMENT_PLAN,
    "2": State.VIEW_SUMMARY,
    "3": State.SETTINGS,
    "4": State.MERGE_LOOP,
    "5": "quit",
}


def _build_trial_summary(trial_dir: Path, meta: TrialMeta) -> str:
    """Build a brief summary of the completed trial for display."""
    parts = [
        f"**Trial**: {trial_dir.name}",
        f"**Status**: {meta.status}",
    ]

    if meta.experiment_exit_code is not None:
        parts.append(f"**Experiment exit code**: {meta.experiment_exit_code}")
    if meta.eval_exit_code is not None:
        parts.append(f"**Eval exit code**: {meta.eval_exit_code}")
    if meta.decision:
        parts.append(f"**Previous decision**: {meta.decision}")

    # Try to show a snippet from REPORT.md if it exists
    report_path = trial_dir / "REPORT.md"
    if report_path.exists():
        report_text = report_path.read_text().strip()
        if report_text:
            # Show first 500 chars of report
            snippet = report_text[:500]
            if len(report_text) > 500:
                snippet += "\n[... truncated]"
            parts.append(f"\n**Report preview**:\n{snippet}")

    return "\n".join(parts)


def _parse_user_choice(text: str) -> str | None:
    """Parse user input to extract a valid option number.

    Returns the option key ('1'-'5') or None if invalid.
    """
    stripped = text.strip()
    # Direct number input
    if stripped in OPTION_MAP:
        return stripped

    # Try to extract first digit
    for ch in stripped:
        if ch in OPTION_MAP:
            return ch

    return None


def handle_decide(
    trial_dir: Path,
    meta: TrialMeta,
    config: ResearchClawConfig,
    chat_interface: Any,
) -> State:
    """Handle the DECIDE state.

    Shows trial summary, presents decision options, and returns the chosen
    next state. In autopilot mode, automatically selects new experiment.

    Agent has read-only access in this state — no write or execute.

    Args:
        trial_dir: Current trial directory.
        meta: Current trial metadata.
        config: ResearchClaw configuration.
        chat_interface: Chat interface for user interaction.

    Returns:
        The next FSM state based on user choice.
    """
    # Show trial summary and autopilot status
    if chat_interface is not None:
        summary = _build_trial_summary(trial_dir, meta)
        autopilot_status = "ON" if config.autopilot else "OFF"
        chat_interface.send(
            f"[DECIDE] Trial complete. (Autopilot: {autopilot_status})\n\n{summary}"
        )

    # Autopilot mode: auto-select new experiment
    if config.autopilot:
        meta.decision = "new_experiment"
        meta.decision_reasoning = "Autopilot mode: automatically starting new experiment."
        if chat_interface is not None:
            chat_interface.send(
                "[Autopilot] Automatically starting new experiment."
            )
        return State.EXPERIMENT_PLAN

    # Interactive mode: present options
    if chat_interface is not None:
        chat_interface.send(DECISION_OPTIONS)

    while True:
        if chat_interface is None:
            # No chat interface — default to new experiment
            meta.decision = "new_experiment"
            return State.EXPERIMENT_PLAN

        user_input = chat_interface.receive()

        # Handle slash commands
        from researchclaw.repl import SlashCommand, UserMessage

        if isinstance(user_input, SlashCommand):
            if user_input.name == "/quit":
                raise SystemExit("User quit via /quit")
            if user_input.name == "/abort":
                raise TrialAborted("User aborted trial at DECIDE")
            if user_input.name == "/status":
                if chat_interface is not None:
                    from researchclaw.status import render_status_string
                    project_dir = trial_dir.parent.parent.parent
                    status_output = render_status_string(project_dir)
                    chat_interface.send(status_output)
                continue
            if user_input.name == "/autopilot":
                # Confirmation prompt
                if chat_interface is not None:
                    chat_interface.send(
                        "Enable autopilot? The system will run experiments "
                        "unattended. Type 'yes' to confirm."
                    )
                confirm = chat_interface.receive()
                confirm_text = ""
                if isinstance(confirm, UserMessage):
                    confirm_text = confirm.text.strip().lower()
                elif isinstance(confirm, SlashCommand):
                    confirm_text = confirm.name
                if confirm_text not in ("yes", "y"):
                    if chat_interface is not None:
                        chat_interface.send("Autopilot not enabled.")
                    continue
                config.autopilot = True
                project_dir = trial_dir.parent.parent.parent
                _persist_autopilot(config, project_dir)
                meta.decision = "new_experiment"
                meta.decision_reasoning = "Autopilot enabled at DECIDE: starting new experiment."
                if chat_interface is not None:
                    chat_interface.send("Autopilot enabled. Starting new experiment.")
                return State.EXPERIMENT_PLAN
            if user_input.name == "/autopilot-stop":
                config.autopilot = False
                project_dir = trial_dir.parent.parent.parent
                _persist_autopilot(config, project_dir)
                if chat_interface is not None:
                    chat_interface.send("Autopilot disabled.")
                continue
            # Other slash commands — inform user
            if chat_interface is not None:
                chat_interface.send(f"Command {user_input.name} not available in DECIDE state. Please choose an option (1-5).")
            continue

        # Parse user message as option choice
        text = user_input.text if isinstance(user_input, UserMessage) else str(user_input)
        choice = _parse_user_choice(text)

        if choice is None:
            if chat_interface is not None:
                chat_interface.send("Please enter a number (1-5).")
            continue

        result = OPTION_MAP[choice]

        if result == "quit":
            meta.decision = "quit"
            raise SystemExit("User chose to quit at DECIDE")

        # Valid state transition
        assert isinstance(result, State)
        meta.decision = choice
        meta.decision_reasoning = f"User chose option {choice}"
        return result
