from __future__ import annotations

import webbrowser

from pathlib import Path
from typing  import TYPE_CHECKING, Any, Callable

from src import models
from src import theme_2
from src.module_loader import _require_warmed

from src.hot_reloader import HotReloader

if TYPE_CHECKING:
 from src.gui_2 import App

# Lazy command registry (startup_speedup_20260606 Phase 5A)
# --------------------------------------------------------------------------
# The @registry.register decorator runs at module import time, but we want
# to defer the actual CommandRegistry creation (and the underlying
# src.command_palette import, ~244ms) until the palette is actually used.
# The proxy below makes @registry.register a no-op that just queues the
# function; the real CommandRegistry is built lazily on first access to
# any other registry attribute (.all, .get, etc.) by gui_2.py or tests.
# --------------------------------------------------------------------------
_PENDING_REGISTRATIONS: list[Callable] = []
_real_registry: Any = None


class _LazyCommandRegistry:
 """Proxy that defers CommandRegistry instantiation.

 Behaves like a CommandRegistry from the caller's perspective:
  - @registry.register decorates functions by queuing them
  - .all, .get, etc. trigger real initialization on first access
 """

 def register(self, command_or_callable: Any) -> Any:
  _PENDING_REGISTRATIONS.append(command_or_callable)
  return command_or_callable

 def __getattr__(self, name: str) -> Any:
  return getattr(_get_real_registry(), name)


def _get_real_registry() -> Any:
 global _real_registry
 if _real_registry is None:
  command_palette = _require_warmed("src.command_palette")
  _real_registry = command_palette.CommandRegistry()
  for func in _PENDING_REGISTRATIONS:
   _real_registry.register(func)
 return _real_registry


registry = _LazyCommandRegistry()


# --------------------------------------------------------------------------
# Helpers
# --------------------------------------------------------------------------

def _toggle_window(app: "App", name: str) -> None:
 """Toggle a window in app.show_windows by name. Defensive no-op if missing."""
 if hasattr(app, "show_windows") and isinstance(app.show_windows, dict):
  app.show_windows[name] = not app.show_windows.get(name, False)


def _toggle_attr(app: "App", attr: str) -> None:
 """Toggle a boolean attribute on app. Defensive no-op if missing."""
 if hasattr(app, attr):
  current = getattr(app, attr)
  if isinstance(current, bool):
   setattr(app, attr, not current)


# --------------------------------------------------------------------------
# AI / Discussion
# --------------------------------------------------------------------------

@registry.register
def reset_session(app: "App") -> None:
 """Reset Session — Reset the AI session, clear comms and tool logs."""
 from src import ai_client
 ai_client.reset_session()
 if hasattr(app, "_handle_reset_session"): app._handle_reset_session()
 if hasattr(app, "_comms_log"):            app._comms_log.clear()
 if hasattr(app, "_tool_log"):             app._tool_log.clear()
 if hasattr(app, "ai_response"):           app.ai_response = ""


@registry.register
def clear_discussion(app: "App") -> None:
 """Clear Discussion — Clear all entries in the current discussion."""
 if hasattr(app, "discussion_history"):
  app.discussion_history = []


@registry.register
def add_all_files_to_context(app: "App") -> None:
 """Add All Files to Context — Add all tracked files to the context."""
 if hasattr(app, "_add_all_files_to_context"):
  app._add_all_files_to_context()


@registry.register
def generate_md_only(app: "App") -> None:
 """Generate MD Only — Run the AI to produce a markdown file without sending to the chat."""
 if hasattr(app, "_do_generate"):
  try:
   md, path, *_     = app._do_generate()
   app.last_md      = md
   app.last_md_path = path
   if hasattr(app, "ai_status"):
    app.ai_status = f"md written: {path.name}"
  except Exception as e:
   if hasattr(app, "ai_status"):
    app.ai_status = f"error: {e}"


# --------------------------------------------------------------------------
# Project
# --------------------------------------------------------------------------

@registry.register
def open_project(app: "App") -> None:
 """Open Project — Open a different project TOML."""
 if hasattr(app, "_show_project_picker"):
  app._show_project_picker()


@registry.register
def save_project(app: "App") -> None:
 """Save Project — Save the current project state to TOML."""
 if hasattr(app, "_save_project_state"):
  app._save_project_state()


@registry.register
def save_all(app: "App") -> None:
 """Save All — Flush to project, flush to config, save global config."""
 if hasattr(app, "_flush_to_project"): app._flush_to_project()
 if hasattr(app, "_flush_to_config"):  app._flush_to_config()
 if hasattr(app, "config"):
  try:
   app.save_config()
  except Exception as e:
   if hasattr(app, "ai_status"):
    app.ai_status = f"save error: {e}"


# --------------------------------------------------------------------------
# View — Window toggles (from show_windows dict)
# --------------------------------------------------------------------------

@registry.register
def toggle_text_viewer(app: "App") -> None:
 """Toggle Text Viewer — Open or close the standalone text/code viewer."""
 _toggle_window(app, "Text Viewer")


@registry.register
def toggle_diagnostics(app: "App") -> None:
 """Toggle Diagnostics — Show/hide the Diagnostics panel."""
 _toggle_window(app, "Diagnostics")


@registry.register
def toggle_usage_analytics(app: "App") -> None:
 """Toggle Usage Analytics — Show/hide the Usage Analytics panel."""
 _toggle_window(app, "Usage Analytics")


@registry.register
def toggle_context_preview(app: "App") -> None:
 """Toggle Context Preview — Show/hide the Context Preview panel."""
 _toggle_window(app, "Context Preview")


@registry.register
def toggle_tier1_strategy(app: "App") -> None:
 """Toggle Tier 1 Strategy — Show/hide the Tier 1 strategy stream."""
 _toggle_window(app, "Tier 1: Strategy")


@registry.register
def toggle_tier2_tech_lead(app: "App") -> None:
 """Toggle Tier 2 Tech Lead — Show/hide the Tier 2 tech-lead stream."""
 _toggle_window(app, "Tier 2: Tech Lead")


@registry.register
def toggle_tier3_workers(app: "App") -> None:
 """Toggle Tier 3 Workers — Show/hide the Tier 3 worker streams."""
 _toggle_window(app, "Tier 3: Workers")


@registry.register
def toggle_tier4_qa(app: "App") -> None:
 """Toggle Tier 4 QA — Show/hide the Tier 4 QA stream."""
 _toggle_window(app, "Tier 4: QA")


@registry.register
def toggle_external_tools(app: "App") -> None:
 """Toggle External Tools — Show/hide the External MCP tools panel."""
 _toggle_window(app, "External Tools")


@registry.register
def toggle_shader_editor(app: "App") -> None:
 """Toggle Shader Editor — Show/hide the live shader editor."""
 _toggle_window(app, "Shader Editor")


@registry.register
def toggle_undo_redo_history(app: "App") -> None:
 """Toggle Undo/Redo History — Show/hide the undo/redo history panel."""
 _toggle_window(app, "Undo/Redo History")


@registry.register
def toggle_command_palette(app: "App") -> None:
 """Toggle Command Palette — Open or close the command palette."""
 _toggle_attr(app, "show_command_palette")


@registry.register
def show_all_panels(app: "App") -> None:
 """Show All Panels — Open every toggleable window."""
 if hasattr(app, "show_windows") and isinstance(app.show_windows, dict):
  for name in app.show_windows.keys():
   app.show_windows[name] = True


@registry.register
def hide_all_panels(app: "App") -> None:
 """Hide All Panels — Close every toggleable window."""
 if hasattr(app, "show_windows") and isinstance(app.show_windows, dict):
  for name in app.show_windows.keys():
   app.show_windows[name] = False


@registry.register
def reset_layout(app: "App") -> None:
 """Reset Layout — Restore the default window layout and clear the stale dock-state cache.

 Useful when a previously-saved manualslop_layout.ini references
 window names that no longer exist (e.g. after a refactor renames
 windows) and the dock space appears empty / non-recoverable via
 the Windows menu. Sets every show_windows entry to True and
 deletes manualslop_layout.ini so hello_imgui regenerates a fresh
 dock layout on the next frame (and the next process shutdown saves
 the new layout in place of the stale one). The user will need to
 restart sloppy.py for the dock layout to fully take effect; the
 show_windows toggles take effect immediately.
 """
 if hasattr(app, "show_windows") and isinstance(app.show_windows, dict):
  for name in app.show_windows.keys():
   app.show_windows[name] = True
 try:
  import os
  layout_paths = [
   "manualslop_layout.ini",
   os.path.join("tests", "artifacts", "live_gui_workspace", "manualslop_layout.ini"),
  ]
  for p in layout_paths:
   if os.path.exists(p):
    os.remove(p)
    if hasattr(app, "ai_status"): app.ai_status = f"layout reset: removed {p}"
 except Exception as e:
  if hasattr(app, "ai_status"): app.ai_status = f"layout reset partial: {e}"


# --------------------------------------------------------------------------
# Layout — Workspace Profiles
# --------------------------------------------------------------------------

@registry.register
def save_workspace_profile(app: "App") -> None:
 """Save Workspace Profile — Open the save-profile dialog."""
 _toggle_attr(app, "_show_save_workspace_profile_modal")
 if hasattr(app, "_new_workspace_profile_name"):
  app._new_workspace_profile_name = ""


@registry.register
def show_workspace_manager(app: "App") -> None:
 """Show Workspace Manager — Open the workspace profile management UI."""
 if hasattr(app, "show_windows") and isinstance(app.show_windows, dict):
  app.show_windows["Workspace Manager"] = True


# --------------------------------------------------------------------------
# Tools
# --------------------------------------------------------------------------

@registry.register
def trigger_hot_reload(app: "App") -> None:
 """Hot Reload — Reload the GUI module to pick up code changes."""
 HotReloader.reload("src.gui_2", app)


@registry.register
def undo(app: "App") -> None:
 """Undo — Revert the most recent UI mutation."""
 if hasattr(app, "_handle_undo"):
  app._handle_undo()


@registry.register
def redo(app: "App") -> None:
 """Redo — Re-apply the most recently undone mutation."""
 if hasattr(app, "_handle_redo"):
  app._handle_redo()


# --------------------------------------------------------------------------
# Theme
# --------------------------------------------------------------------------

@registry.register
def switch_to_dark_theme(app: "App") -> None:
 """Switch to Dark Theme (10x Dark palette)."""
 theme_2.apply("10x Dark")


@registry.register
def switch_to_light_theme(app: "App") -> None:
 """Switch to Light Theme (ImGui Light palette)."""
 theme_2.apply("ImGui Light")


@registry.register
def switch_to_nerv_theme(app: "App") -> None:
 """Switch to NERV Theme (Tactical Console aesthetic)."""
 theme_2.apply("NERV")


@registry.register
def cycle_theme(app: "App") -> None:
 """Cycle Theme — Switch to the next theme in the cycle (Dark → Light → NERV → Dark)."""
 order = ["10x Dark", "ImGui Light", "NERV"]
 current = theme_2.get_current_palette()
 if current in order:
  next_idx = (order.index(current) + 1) % len(order)
 else:
  next_idx = 0
 theme_2.apply(order[next_idx])


# --------------------------------------------------------------------------
# Help
# --------------------------------------------------------------------------

@registry.register
def show_documentation(app: "App") -> None:
 """Show Documentation — Open the project URL in the browser."""
 webbrowser.open("https://git.cozyair.dev/ed/manual_slop/")


@registry.register
def show_command_palette_help(app: "App") -> None:
 """Show Command Palette Help — Open the docs/Readme.md in the Text Viewer."""
 if hasattr(app, "readme_text"):
  docs_readme = Path("docs/Readme.md")
  if docs_readme.exists():
   app.readme_text = docs_readme.read_text(encoding="utf-8")
   if hasattr(app, "show_windows") and isinstance(app.show_windows, dict):
    app.show_windows["Text Viewer"] = True
