"""TUI for browsing the SmartTools Registry using urwid.

Uses threading for non-blocking network operations.
"""

import os
import threading
from concurrent.futures import ThreadPoolExecutor
from typing import Optional, List, Dict, Any, Callable

import urwid

from .registry_client import (
    RegistryClient, RegistryError, ToolInfo,
    get_client, PaginatedResponse
)
from .resolver import install_from_registry


# Color palette - matching the main UI style
PALETTE = [
    ('body', 'white', 'dark blue'),
    ('header', 'white', 'dark red', 'bold'),
    ('footer', 'black', 'light gray'),
    ('button', 'black', 'light gray'),
    ('button_focus', 'white', 'dark red', 'bold'),
    ('edit', 'black', 'light gray'),
    ('edit_focus', 'black', 'yellow'),
    ('listbox', 'black', 'light gray'),
    ('listbox_focus', 'white', 'dark red'),
    ('dialog', 'black', 'light gray'),
    ('label', 'yellow', 'dark blue', 'bold'),
    ('error', 'white', 'dark red', 'bold'),
    ('success', 'light green', 'dark blue', 'bold'),
    ('info', 'light cyan', 'dark blue'),
    ('downloads', 'light green', 'light gray'),
    ('category', 'dark cyan', 'light gray'),
    ('version', 'brown', 'light gray'),
    ('loading', 'yellow', 'dark blue'),
]


class ToolListItem(urwid.WidgetWrap):
    """A selectable tool item in the browse list."""

    def __init__(self, tool_data: Dict[str, Any], on_select=None, on_install=None):
        self.tool_data = tool_data
        self.on_select = on_select
        self.on_install = on_install

        owner = tool_data.get("owner", "")
        name = tool_data.get("name", "")
        version = tool_data.get("version", "")
        description = tool_data.get("description", "")[:50]
        downloads = tool_data.get("downloads", 0)
        category = tool_data.get("category", "")

        # Format: owner/name v1.0.0                    [category] ↓123
        main_line = urwid.Text([
            ('listbox', f"  {owner}/"),
            ('listbox', f"{name} "),
            ('version', f"v{version}"),
        ])

        desc_line = urwid.Text([
            ('listbox', f"    {description}{'...' if len(tool_data.get('description', '')) > 50 else ''}"),
        ])

        meta_line = urwid.Text([
            ('category', f"    [{category}]" if category else ""),
            ('downloads', f" ↓{downloads}"),
        ])

        pile = urwid.Pile([main_line, desc_line, meta_line])
        self.attr_map = urwid.AttrMap(pile, 'listbox', 'listbox_focus')
        super().__init__(self.attr_map)

    def selectable(self):
        return True

    def keypress(self, size, key):
        if key == 'enter' and self.on_select:
            self.on_select(self.tool_data)
            return None
        if key == 'i' and self.on_install:
            self.on_install(self.tool_data)
            return None
        return key

    def mouse_event(self, size, event, button, col, row, focus):
        if event == 'mouse press' and button == 1 and self.on_select:
            self.on_select(self.tool_data)
            return True
        return False


class SearchEdit(urwid.Edit):
    """Search box that triggers callback on enter."""

    def __init__(self, on_search=None):
        self.on_search = on_search
        super().__init__(caption="Search: ", edit_text="")

    def keypress(self, size, key):
        if key == 'enter' and self.on_search:
            self.on_search(self.edit_text)
            return None
        return super().keypress(size, key)


class AsyncOperation:
    """Manages async operations with UI callbacks."""

    def __init__(self, executor: ThreadPoolExecutor):
        self.executor = executor
        self._write_fd: Optional[int] = None
        self._read_fd: Optional[int] = None
        self._pending_callbacks: List[Callable] = []
        self._lock = threading.Lock()

    def setup_pipe(self, loop: urwid.MainLoop):
        """Setup a pipe for thread-safe UI updates."""
        self._read_fd, self._write_fd = os.pipe()
        loop.watch_file(self._read_fd, self._handle_callback)

    def cleanup(self):
        """Cleanup pipe file descriptors."""
        if self._read_fd is not None:
            os.close(self._read_fd)
        if self._write_fd is not None:
            os.close(self._write_fd)

    def _handle_callback(self):
        """Handle pending callbacks from worker threads."""
        # Read and discard the notification byte
        os.read(self._read_fd, 1)

        # Process pending callbacks
        with self._lock:
            callbacks = self._pending_callbacks[:]
            self._pending_callbacks.clear()

        for callback in callbacks:
            callback()

    def _schedule_callback(self, callback: Callable):
        """Schedule a callback to run on the main thread."""
        with self._lock:
            self._pending_callbacks.append(callback)

        # Wake up the main loop
        if self._write_fd is not None:
            os.write(self._write_fd, b'x')

    def run_async(
        self,
        operation: Callable,
        on_success: Callable[[Any], None],
        on_error: Callable[[Exception], None]
    ):
        """Run an operation asynchronously."""
        def worker():
            try:
                result = operation()
                self._schedule_callback(lambda: on_success(result))
            except Exception as e:
                self._schedule_callback(lambda: on_error(e))

        self.executor.submit(worker)


class RegistryBrowser:
    """TUI browser for the SmartTools Registry."""

    def __init__(self):
        self.client = get_client()
        self.tools: List[Dict] = []
        self.categories: List[Dict] = []
        self.current_category: Optional[str] = None
        self.current_query: str = ""
        self.current_page: int = 1
        self.total_pages: int = 1
        self.status_message: str = ""
        self.loop: Optional[urwid.MainLoop] = None
        self.loading: bool = False

        # Thread pool for async operations
        self.executor = ThreadPoolExecutor(max_workers=2)
        self.async_ops = AsyncOperation(self.executor)

        # Build UI
        self._build_ui()

    def _build_ui(self):
        """Build the main UI layout."""
        # Header
        self.header = urwid.AttrMap(
            urwid.Text(" SmartTools Registry Browser ", align='center'),
            'header'
        )

        # Search box
        self.search_edit = SearchEdit(on_search=self._do_search)
        search_widget = urwid.AttrMap(self.search_edit, 'edit', 'edit_focus')

        # Category selector
        self.category_text = urwid.Text("Category: All")
        category_widget = urwid.AttrMap(self.category_text, 'info')

        # Top bar with search and category
        top_bar = urwid.Columns([
            ('weight', 2, search_widget),
            ('weight', 1, category_widget),
        ], dividechars=2)

        # Tools list
        self.list_walker = urwid.SimpleFocusListWalker([])
        self.listbox = urwid.ListBox(self.list_walker)
        list_frame = urwid.LineBox(self.listbox, title="Tools")

        # Detail panel (right side)
        self.detail_text = urwid.Text("Select a tool to view details\n\nPress 'i' to install")
        self.detail_box = urwid.LineBox(
            urwid.Filler(self.detail_text, valign='top'),
            title="Details"
        )

        # Main content area with list and details
        self.main_columns = urwid.Columns([
            ('weight', 2, list_frame),
            ('weight', 1, self.detail_box),
        ], dividechars=1)

        # Status bar
        self.status_text = urwid.Text(" Loading...")
        self.footer = urwid.AttrMap(
            urwid.Columns([
                self.status_text,
                urwid.Text("↑↓:Navigate  Enter:Details  i:Install  /:Search  c:Category  q:Quit", align='right'),
            ]),
            'footer'
        )

        # Main frame
        body = urwid.Pile([
            ('pack', urwid.AttrMap(top_bar, 'body')),
            ('pack', urwid.Divider()),
            self.main_columns,
        ])

        self.frame = urwid.Frame(
            urwid.AttrMap(body, 'body'),
            header=self.header,
            footer=self.footer
        )

    def _load_tools(self, query: str = "", category: str = None, page: int = 1):
        """Load tools from the registry asynchronously."""
        if self.loading:
            return

        self.loading = True
        self._set_status("Loading...", loading=True)

        def fetch():
            if query:
                return self.client.search_tools(
                    query=query,
                    category=category,
                    page=page,
                    per_page=20
                )
            else:
                return self.client.list_tools(
                    category=category,
                    page=page,
                    per_page=20
                )

        def on_success(result: PaginatedResponse):
            self.loading = False
            self.tools = result.data
            self.current_page = result.page
            self.total_pages = result.total_pages
            self._update_list()
            self._set_status(f"Found {result.total} tools (page {result.page}/{result.total_pages})")

        def on_error(e: Exception):
            self.loading = False
            if isinstance(e, RegistryError):
                self._set_status(f"Error: {e.message}")
            else:
                self._set_status(f"Error: {e}")

        self.async_ops.run_async(fetch, on_success, on_error)

    def _load_categories(self):
        """Load categories from the registry asynchronously."""
        def fetch():
            return self.client.get_categories()

        def on_success(categories):
            self.categories = categories

        def on_error(e):
            self.categories = []

        self.async_ops.run_async(fetch, on_success, on_error)

    def _update_list(self):
        """Update the tool list display."""
        self.list_walker.clear()

        if not self.tools:
            self.list_walker.append(urwid.Text("  No tools found"))
            return

        for tool in self.tools:
            item = ToolListItem(
                tool,
                on_select=self._show_detail,
                on_install=self._install_tool
            )
            self.list_walker.append(item)
            self.list_walker.append(urwid.Divider('─'))

    def _show_detail(self, tool_data: Dict):
        """Show tool details in the detail panel."""
        owner = tool_data.get("owner", "")
        name = tool_data.get("name", "")
        version = tool_data.get("version", "")
        description = tool_data.get("description", "No description")
        category = tool_data.get("category", "")
        tags = tool_data.get("tags", [])
        downloads = tool_data.get("downloads", 0)

        detail = f"""{owner}/{name}
Version: {version}
Category: {category}
Downloads: {downloads}

{description}

Tags: {', '.join(tags) if tags else 'None'}

Install command:
  smarttools registry install {owner}/{name}

Press 'i' to install this tool
"""
        self.detail_text.set_text(detail)

    def _install_tool(self, tool_data: Dict):
        """Install the selected tool asynchronously."""
        if self.loading:
            return

        owner = tool_data.get("owner", "")
        name = tool_data.get("name", "")

        self.loading = True
        self._set_status(f"Installing {owner}/{name}...", loading=True)

        def install():
            return install_from_registry(f"{owner}/{name}")

        def on_success(resolved):
            self.loading = False
            self._set_status(f"Installed: {resolved.full_name}@{resolved.version}")

        def on_error(e):
            self.loading = False
            self._set_status(f"Install failed: {e}")

        self.async_ops.run_async(install, on_success, on_error)

    def _do_search(self, query: str):
        """Perform search."""
        self.current_query = query
        self.current_page = 1
        self._load_tools(query=query, category=self.current_category)

    def _cycle_category(self):
        """Cycle through categories."""
        if not self.categories:
            self._load_categories()
            # Schedule the cycle after categories load
            return

        if not self.categories:
            return

        cat_names = [None] + [c.get("name") for c in self.categories]
        try:
            idx = cat_names.index(self.current_category)
            idx = (idx + 1) % len(cat_names)
        except ValueError:
            idx = 0

        self.current_category = cat_names[idx]
        cat_display = self.current_category or "All"
        self.category_text.set_text(f"Category: {cat_display}")
        self._load_tools(query=self.current_query, category=self.current_category)

    def _next_page(self):
        """Go to next page."""
        if self.current_page < self.total_pages:
            self.current_page += 1
            self._load_tools(
                query=self.current_query,
                category=self.current_category,
                page=self.current_page
            )

    def _prev_page(self):
        """Go to previous page."""
        if self.current_page > 1:
            self.current_page -= 1
            self._load_tools(
                query=self.current_query,
                category=self.current_category,
                page=self.current_page
            )

    def _set_status(self, message: str, loading: bool = False):
        """Update status bar message."""
        if loading:
            self.status_text.set_text(('loading', f" ⟳ {message}"))
        else:
            self.status_text.set_text(f" {message}")

    def _handle_input(self, key):
        """Handle global key input."""
        if key in ('q', 'Q'):
            raise urwid.ExitMainLoop()
        elif key == '/':
            # Focus search box
            self.frame.body.base_widget.set_focus(0)
            return None
        elif key == 'c':
            self._cycle_category()
            return None
        elif key == 'n':
            self._next_page()
            return None
        elif key == 'p':
            self._prev_page()
            return None
        elif key == 'r':
            # Refresh current view
            self._load_tools(
                query=self.current_query,
                category=self.current_category,
                page=self.current_page
            )
            return None
        return key

    def run(self):
        """Run the TUI browser."""
        # Create main loop
        self.loop = urwid.MainLoop(
            self.frame,
            palette=PALETTE,
            unhandled_input=self._handle_input,
            handle_mouse=True
        )

        # Setup async pipe for thread-safe callbacks
        self.async_ops.setup_pipe(self.loop)

        try:
            # Initial load (async)
            self._load_categories()
            self._load_tools()

            # Run main loop
            self.loop.run()
        finally:
            # Cleanup
            self.async_ops.cleanup()
            self.executor.shutdown(wait=False)


def run_registry_browser():
    """Entry point for the registry browser TUI."""
    try:
        browser = RegistryBrowser()
        browser.run()
    except RegistryError as e:
        print(f"Error connecting to registry: {e.message}")
        return 1
    except Exception as e:
        print(f"Error: {e}")
        return 1
    return 0
