"""App orchestrator: view stack, tab bar, leader key routing."""

import curses
import os
import time
from pathlib import Path

from muva import theme
from muva.keybinds import LeaderKeyHandler
from muva.views.explorer import ExplorerView
from muva.views.editor import EditorView
from muva.views.terminal import TerminalView


class App:
    """Main application orchestrator."""

    def __init__(self, stdscr):
        self.stdscr = stdscr
        self._setup_curses()

        self.leader = LeaderKeyHandler()
        self.explorer = ExplorerView()
        self.view_stack = [self.explorer]  # Explorer is always the base
        self.terminals = []  # list of TerminalView instances
        self._next_term_id = 1

    def _setup_curses(self):
        curses.curs_set(0)
        curses.start_color()
        curses.use_default_colors()
        theme.init_colors()
        self.stdscr.timeout(50)  # 50ms timeout for non-blocking reads (terminal needs this)
        try:
            curses.set_escdelay(25)
        except Exception:
            pass

    @property
    def active_view(self):
        return self.view_stack[-1] if self.view_stack else self.explorer

    def push_view(self, view):
        self.active_view.on_leave()
        self.view_stack.append(view)
        view.on_enter()

    def pop_view(self):
        if len(self.view_stack) <= 1:
            return  # Don't pop the explorer
        view = self.view_stack.pop()
        view.on_leave()
        # If it's a terminal, don't stop it (keep it running in background)
        self.active_view.on_enter()

    def _get_all_tabs(self):
        """Return list of (label, is_active, view) for tab bar."""
        tabs = []
        for view in self.view_stack:
            is_active = view is self.active_view
            tabs.append((view.title, is_active, view))
        # Also show background terminals not in stack
        for term in self.terminals:
            if term not in self.view_stack:
                tabs.append((term.title, False, term))
        return tabs

    def draw_tab_bar(self):
        """Draw the tab bar at row 0."""
        h, w = self.stdscr.getmaxyx()
        tabs = self._get_all_tabs()

        # Fill tab bar background
        try:
            self.stdscr.addnstr(0, 0, " " * w, w, theme.cp("tab_bg"))
        except curses.error:
            pass

        x = 1
        for label, is_active, view in tabs:
            if is_active:
                attr = theme.cp("tab_active") | curses.A_BOLD
            else:
                attr = theme.cp("tab_inactive")

            tab_text = f" {label} "
            if x + len(tab_text) + 1 >= w:
                break
            try:
                self.stdscr.addnstr(0, x, tab_text, len(tab_text), attr)
            except curses.error:
                pass
            x += len(tab_text)

            # Separator
            if x < w - 1:
                try:
                    self.stdscr.addstr(0, x, "\u2502", theme.cp("tab_inactive"))
                except curses.error:
                    pass
                x += 1

    def handle_leader_action(self, action):
        """Process a leader key action string."""
        if action == "toggle_terminal":
            # If active view is a terminal, pop it
            if isinstance(self.active_view, TerminalView):
                self.pop_view()
            else:
                # Push the most recent terminal, or create one
                if self.terminals:
                    term = self.terminals[-1]
                    if term not in self.view_stack:
                        self.push_view(term)
                    else:
                        self.pop_view()
                else:
                    self._new_terminal()

        elif action == "close_pane":
            if len(self.view_stack) > 1:
                view = self.active_view
                self.pop_view()
                # If it's a terminal, stop it
                if isinstance(view, TerminalView):
                    view.stop()
                    if view in self.terminals:
                        self.terminals.remove(view)

        elif action == "toggle_root_lock":
            self.explorer.root_locked = not self.explorer.root_locked
            if self.explorer.root_locked:
                self.explorer.root_path = Path(self.explorer.path)
                self.explorer.show_msg(f"Root locked: {self.explorer.path}")
            else:
                self.explorer.show_msg("Root unlocked — free roaming")

        elif action == "new_terminal":
            self._new_terminal()

        elif action == "jump_explorer":
            # Pop everything back to explorer
            while len(self.view_stack) > 1:
                self.view_stack[-1].on_leave()
                self.view_stack.pop()
            self.explorer.on_enter()

        elif action == "run_python":
            self._run_python_file()

        elif action.startswith("switch_tab:"):
            idx = int(action.split(":")[1]) - 1
            tabs = self._get_all_tabs()
            if 0 <= idx < len(tabs):
                _, _, target_view = tabs[idx]
                if target_view is not self.active_view:
                    # If target is in stack, pop to it; otherwise push it
                    if target_view in self.view_stack:
                        while self.view_stack[-1] is not target_view:
                            self.view_stack[-1].on_leave()
                            self.view_stack.pop()
                        self.active_view.on_enter()
                    else:
                        self.push_view(target_view)

    def _new_terminal(self):
        cwd = str(self.explorer.path) if self.explorer.root_locked else str(self.explorer.path)
        term = TerminalView(cwd=cwd, tab_id=self._next_term_id)
        self._next_term_id += 1
        self.terminals.append(term)
        self.push_view(term)

    def _run_python_file(self):
        """Run the current Python file in a new terminal."""
        # Find the current editor's file
        for view in reversed(self.view_stack):
            if isinstance(view, EditorView) and view.filename:
                if view.filename.endswith(".py"):
                    cwd = str(os.path.dirname(os.path.abspath(view.filename)))
                    term = TerminalView(cwd=cwd, tab_id=self._next_term_id)
                    self._next_term_id += 1
                    self.terminals.append(term)
                    self.push_view(term)
                    # Send the run command after terminal starts
                    term.on_enter()
                    import time
                    time.sleep(0.1)
                    cmd = f"python3 {view.filename}\n"
                    if term.master_fd:
                        try:
                            os.write(term.master_fd, cmd.encode())
                        except OSError:
                            pass
                    return
        # No Python file found in stack

    def run(self):
        """Main event loop."""
        self.explorer.on_enter()

        while True:
            h, w = self.stdscr.getmaxyx()
            if h < 3 or w < 10:
                continue

            self.stdscr.erase()

            # Draw tab bar (row 0)
            self.draw_tab_bar()

            # Draw active view (rows 1 to h-1)
            view_h = h - 1
            try:
                self.active_view.draw(self.stdscr, view_h, w, 1)
            except curses.error:
                pass

            self.stdscr.refresh()

            # Get key
            key = self.stdscr.getch()
            if key == -1:
                # Timeout (no key pressed) - useful for terminal refresh
                continue

            if key == curses.KEY_RESIZE:
                continue

            # Check leader key first
            action, consumed = self.leader.handle_key(key)
            if action == "replay_leader":
                # Leader didn't match — send Space to the view, then this key
                from config import LEADER_KEY
                result = self.active_view.handle_key(LEADER_KEY)
                if result:
                    self._handle_view_result(result)
                    continue
                if not consumed:
                    # Also forward the second key that didn't match
                    result = self.active_view.handle_key(key)
                    if result:
                        self._handle_view_result(result)
                    continue
                continue
            elif action:
                self.handle_leader_action(action)
                continue
            if consumed:
                # Leader key pressed, waiting for next key
                continue

            # Route to active view
            result = self.active_view.handle_key(key)

            if result:
                self._handle_view_result(result)

    def _handle_view_result(self, result):
        """Process action strings returned by views."""
        if result == "quit":
            # Clean up terminals
            for term in self.terminals:
                term.stop()
            raise SystemExit(0)

        elif result == "back":
            self.pop_view()

        elif result.startswith("open:"):
            filepath = result[5:]
            editor = EditorView(filename=filepath)
            self.push_view(editor)
