"""
Mode System for groknroll REPL

Provides unified mode management combining operational modes and agent modes.
All modes can be cycled through with Shift+Tab.

Operational Modes:
- FSD: Full Self-Driving - no permission checks
- Normal: Standard with permission prompts
- Safe: Read-only operations only
- Audit: Log everything, ask for all

Agent Modes:
- Build: Full coding agent
- Plan: Read-only planning agent
- Oracle: Codebase knowledge agent

Examples:
    # Create mode manager
    manager = ModeManager()

    # Cycle forward
    new_mode = manager.cycle()

    # Cycle reverse (Shift+Tab)
    new_mode = manager.cycle(reverse=True)

    # Check mode type
    if manager.is_operational():
        permissions = manager.get_permissions()
"""

from dataclasses import dataclass
from enum import Enum
from typing import Optional


class Mode(Enum):
    """All modes in single enum for unified cycling"""

    # Operational modes
    FSD = "fsd"
    NORMAL = "normal"
    SAFE = "safe"
    AUDIT = "audit"
    # Agent modes
    BUILD = "build"
    PLAN = "plan"
    ORACLE = "oracle"


# Mode display information
MODE_INFO: dict[Mode, dict[str, str]] = {
    Mode.FSD: {
        "icon": "🚀",
        "description": "Full Self-Driving - no permission checks",
        "short": "FSD",
    },
    Mode.NORMAL: {
        "icon": "🔵",
        "description": "Standard with permission prompts",
        "short": "Normal",
    },
    Mode.SAFE: {
        "icon": "🛡️",
        "description": "Read-only operations only",
        "short": "Safe",
    },
    Mode.AUDIT: {
        "icon": "📋",
        "description": "Log everything, ask for all",
        "short": "Audit",
    },
    Mode.BUILD: {
        "icon": "🔨",
        "description": "Full coding agent",
        "short": "Build",
    },
    Mode.PLAN: {
        "icon": "📝",
        "description": "Read-only planning agent",
        "short": "Plan",
    },
    Mode.ORACLE: {
        "icon": "🔮",
        "description": "Codebase knowledge agent",
        "short": "Oracle",
    },
}

# Operational modes (control permission behavior)
OPERATIONAL_MODES = frozenset({Mode.FSD, Mode.NORMAL, Mode.SAFE, Mode.AUDIT})

# Agent modes (control agent behavior)
AGENT_MODES = frozenset({Mode.BUILD, Mode.PLAN, Mode.ORACLE})


@dataclass
class ModePermissions:
    """Permission settings for a mode"""

    read: str  # "allow", "ask", "deny"
    write: str
    edit: str
    bash: str
    skip_all_checks: bool = False  # FSD mode


class ModeManager:
    """
    Manage all modes with unified cycling.

    Provides a single point of control for both operational modes
    (FSD/Normal/Safe/Audit) and agent modes (Build/Plan/Oracle).

    Attributes:
        current_mode: The currently active mode
        CYCLE_ORDER: The order in which modes cycle (7 total)
    """

    CYCLE_ORDER = [
        Mode.FSD,
        Mode.NORMAL,
        Mode.SAFE,
        Mode.AUDIT,
        Mode.BUILD,
        Mode.PLAN,
        Mode.ORACLE,
    ]

    def __init__(self, initial_mode: Mode = Mode.NORMAL):
        """
        Initialize mode manager.

        Args:
            initial_mode: The starting mode (default: Normal)
        """
        self.current_mode = initial_mode

    def cycle(self, reverse: bool = False) -> Mode:
        """
        Cycle to next mode in rotation.

        Tab cycles forward, Shift+Tab cycles reverse.

        Args:
            reverse: If True, cycle backwards (Shift+Tab)

        Returns:
            The new current mode
        """
        current_index = self.CYCLE_ORDER.index(self.current_mode)

        if reverse:
            new_index = (current_index - 1) % len(self.CYCLE_ORDER)
        else:
            new_index = (current_index + 1) % len(self.CYCLE_ORDER)

        self.current_mode = self.CYCLE_ORDER[new_index]
        return self.current_mode

    def set_mode(self, mode: Mode) -> None:
        """
        Set mode directly.

        Args:
            mode: The mode to set
        """
        self.current_mode = mode

    def set_mode_by_name(self, name: str) -> Optional[Mode]:
        """
        Set mode by string name.

        Args:
            name: Mode name (case-insensitive)

        Returns:
            The new mode, or None if name is invalid
        """
        name_lower = name.lower()
        for mode in Mode:
            if mode.value == name_lower:
                self.current_mode = mode
                return mode
        return None

    def is_operational(self) -> bool:
        """
        Check if current mode is an operational mode.

        Returns:
            True if FSD/Normal/Safe/Audit
        """
        return self.current_mode in OPERATIONAL_MODES

    def is_agent(self) -> bool:
        """
        Check if current mode is an agent mode.

        Returns:
            True if Build/Plan/Oracle
        """
        return self.current_mode in AGENT_MODES

    def get_permissions(self) -> ModePermissions:
        """
        Get permission settings for current mode.

        Returns:
            ModePermissions with appropriate settings
        """
        mode = self.current_mode

        if mode == Mode.FSD:
            # FSD: Skip all permission checks
            return ModePermissions(
                read="allow",
                write="allow",
                edit="allow",
                bash="allow",
                skip_all_checks=True,
            )
        elif mode == Mode.NORMAL:
            # Normal: Standard permission prompts
            return ModePermissions(
                read="allow",
                write="ask",
                edit="ask",
                bash="ask",
            )
        elif mode == Mode.SAFE:
            # Safe: Read-only operations only
            return ModePermissions(
                read="allow",
                write="deny",
                edit="deny",
                bash="deny",
            )
        elif mode == Mode.AUDIT:
            # Audit: Log everything, ask for all
            return ModePermissions(
                read="ask",
                write="ask",
                edit="ask",
                bash="ask",
            )
        elif mode == Mode.BUILD:
            # Build agent: Full coding access
            return ModePermissions(
                read="allow",
                write="ask",
                edit="ask",
                bash="ask",
            )
        elif mode == Mode.PLAN:
            # Plan agent: Read-only planning
            return ModePermissions(
                read="allow",
                write="deny",
                edit="deny",
                bash="deny",
            )
        elif mode == Mode.ORACLE:
            # Oracle agent: Read-only codebase knowledge
            return ModePermissions(
                read="allow",
                write="deny",
                edit="deny",
                bash="deny",
            )

        # Default fallback
        return ModePermissions(
            read="allow",
            write="ask",
            edit="ask",
            bash="ask",
        )

    def get_agent_name(self) -> str:
        """
        Get the agent name for the current mode.

        For operational modes, returns "build" as default.
        For agent modes, returns the agent name.

        Returns:
            Agent name string
        """
        if self.current_mode == Mode.BUILD:
            return "build"
        elif self.current_mode == Mode.PLAN:
            return "plan"
        elif self.current_mode == Mode.ORACLE:
            return "oracle"
        else:
            # Operational modes use build agent by default
            return "build"

    def get_display_info(self) -> dict[str, str]:
        """
        Get display information for current mode.

        Returns:
            Dict with icon, description, short name
        """
        return MODE_INFO.get(
            self.current_mode,
            {"icon": "?", "description": "Unknown mode", "short": "Unknown"},
        )

    def get_display_string(self) -> str:
        """
        Get formatted display string for current mode.

        Returns:
            String like "🚀 FSD" or "🔨 Build"
        """
        info = self.get_display_info()
        return f"{info['icon']} {info['short']}"

    def get_all_modes(self) -> list[dict]:
        """
        Get information about all available modes.

        Returns:
            List of mode info dicts
        """
        return [
            {
                "mode": mode,
                "name": mode.value,
                "type": "operational" if mode in OPERATIONAL_MODES else "agent",
                **MODE_INFO[mode],
            }
            for mode in self.CYCLE_ORDER
        ]


def create_mode_manager(initial_mode: str = "normal") -> ModeManager:
    """
    Create a ModeManager with optional initial mode.

    Args:
        initial_mode: Mode name (default: "normal")

    Returns:
        Configured ModeManager
    """
    manager = ModeManager()
    manager.set_mode_by_name(initial_mode)
    return manager
