"""Main window for the Dataview Editor.

Provides a file list sidebar and tabbed interface for editing DataViews.
"""

from __future__ import annotations

import tkinter as tk
from pathlib import Path
from tkinter import filedialog, messagebox, ttk
from typing import TYPE_CHECKING

from .dataview_panel import DataviewPanel
from .shared_dataview_data import SharedDataviewData

if TYPE_CHECKING:
    pass


class DataviewEditorWindow(tk.Toplevel):
    """Main window for the Dataview Editor.

    Features:
    - Left sidebar: List of CDV files in the project's DataView folder
    - Right panel: Notebook with tabs for open dataviews
    - Menu bar: File operations (New, Open, Save, Export)
    """

    def _setup_window(self) -> None:
        """Configure window properties."""
        base_title = "ClickNick Dataview Editor"
        if self.title_suffix:
            self.title(f"{base_title} - {self.title_suffix}")
        else:
            self.title(base_title)

        self.geometry("900x600")
        self.minsize(600, 400)

    def _refresh_file_list(self) -> None:
        """Refresh the file list from the dataview folder."""
        self.file_listbox.delete(0, tk.END)

        files = self.shared_data.get_cdv_files()
        for file_path in files:
            self.file_listbox.insert(tk.END, file_path.stem)

        # Store paths for lookup
        self._file_paths = files

    def _get_current_panel(self) -> DataviewPanel | None:
        """Get the currently active panel."""
        try:
            current = self.notebook.select()
            if current:
                return self.notebook.nametowidget(current)
        except tk.TclError:
            pass
        return None

    def _update_tab_title(self, panel: DataviewPanel) -> None:
        """Update the tab title for a panel."""
        for i in range(self.notebook.index("end")):
            if self.notebook.nametowidget(self.notebook.tabs()[i]) == panel:
                title = panel.name
                if panel.is_dirty:
                    title = f"*{title}"
                self.notebook.tab(i, text=title)
                break

    def _on_panel_modified(self) -> None:
        """Handle panel modification event."""
        panel = self._get_current_panel()
        if panel:
            self._update_tab_title(panel)

    def _open_dataview(self, file_path: Path) -> None:
        """Open a dataview file in a new tab.

        Args:
            file_path: Path to the CDV file
        """
        # Check if already open
        if file_path in self._open_panels:
            # Switch to existing tab
            panel = self._open_panels[file_path]
            for i in range(self.notebook.index("end")):
                if self.notebook.nametowidget(self.notebook.tabs()[i]) == panel:
                    self.notebook.select(i)
                    return
            return

        # Create new panel
        panel = DataviewPanel(
            self.notebook,
            file_path=file_path,
            on_modified=self._on_panel_modified,
            nickname_lookup=self.shared_data.lookup_nickname,
        )

        # Add tab
        self.notebook.add(panel, text=file_path.stem)
        self.notebook.select(panel)

        self._open_panels[file_path] = panel

    def _new_dataview(self) -> None:
        """Create a new unsaved dataview."""
        self._untitled_counter += 1
        name = f"Untitled{self._untitled_counter}"

        panel = DataviewPanel(
            self.notebook,
            file_path=None,
            on_modified=self._on_panel_modified,
            nickname_lookup=self.shared_data.lookup_nickname,
        )

        self.notebook.add(panel, text=name)
        self.notebook.select(panel)

        self._open_panels[None] = panel  # Track with None key

    def _open_file(self) -> None:
        """Open a CDV file via file dialog."""
        initial_dir = self.shared_data.dataview_folder or Path.cwd()

        file_path = filedialog.askopenfilename(
            parent=self,
            title="Open DataView",
            initialdir=initial_dir,
            filetypes=[("DataView files", "*.cdv"), ("All files", "*.*")],
        )

        if file_path:
            self._open_dataview(Path(file_path))

    def _save_as(self) -> None:
        """Save the current dataview with a new name."""
        panel = self._get_current_panel()
        if not panel:
            return

        initial_dir = self.shared_data.dataview_folder or Path.cwd()
        initial_name = panel.name if panel.name != "Untitled" else ""

        file_path = filedialog.asksaveasfilename(
            parent=self,
            title="Save DataView As",
            initialdir=initial_dir,
            initialfile=initial_name,
            defaultextension=".cdv",
            filetypes=[("DataView files", "*.cdv"), ("All files", "*.*")],
        )

        if file_path:
            new_path = Path(file_path)

            # Update tracking
            old_path = panel.file_path
            if old_path in self._open_panels:
                del self._open_panels[old_path]

            panel.save_as(new_path)
            self._open_panels[new_path] = panel
            self._update_tab_title(panel)

            # Refresh file list if saved to dataview folder
            if (
                self.shared_data.dataview_folder
                and new_path.parent == self.shared_data.dataview_folder
            ):
                self._refresh_file_list()

    def _save_current(self) -> None:
        """Save the current dataview."""
        panel = self._get_current_panel()
        if not panel:
            return

        if panel.file_path:
            panel.save()
            self._update_tab_title(panel)
        else:
            self._save_as()

    def _export(self) -> None:
        """Export the current dataview to a new location."""
        # Same as Save As for now
        self._save_as()

    def _close_current_tab(self) -> None:
        """Close the current tab."""
        panel = self._get_current_panel()
        if not panel:
            return

        # Check for unsaved changes
        if panel.is_dirty:
            result = messagebox.askyesnocancel(
                "Unsaved Changes",
                f"'{panel.name}' has unsaved changes. Save before closing?",
                parent=self,
            )
            if result is None:  # Cancel
                return
            if result:  # Yes - save
                if panel.file_path:
                    panel.save()
                else:
                    self._save_as()
                    if panel.is_dirty:  # User cancelled save dialog
                        return

        # Remove from tracking
        if panel.file_path in self._open_panels:
            del self._open_panels[panel.file_path]
        elif None in self._open_panels and self._open_panels[None] == panel:
            del self._open_panels[None]

        # Close tab
        self.notebook.forget(panel)
        panel.destroy()

    def _clear_selected_rows(self) -> None:
        """Clear selected rows in the current panel."""
        panel = self._get_current_panel()
        if not panel:
            return

        selected = panel.get_selected_rows()
        for row_idx in selected:
            panel.clear_row(row_idx)

    def _refresh_nicknames(self) -> None:
        """Refresh nicknames in all open panels."""
        for panel in self._open_panels.values():
            panel.refresh_nicknames()

    def _on_close(self) -> None:
        """Handle window close."""
        # Check for unsaved changes
        if self.has_unsaved_changes():
            result = messagebox.askyesnocancel(
                "Unsaved Changes",
                "You have unsaved changes. Save before closing?",
                parent=self,
            )
            if result is None:  # Cancel
                return
            if result:  # Yes - save
                self.save_all()

        # Unregister from shared data
        self.shared_data.unregister_window(self)

        # Destroy window
        self.destroy()

    def _create_menu(self) -> None:
        """Create the menu bar."""
        menubar = tk.Menu(self)
        self.config(menu=menubar)

        # File menu
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=file_menu)

        file_menu.add_command(
            label="New Dataview", command=self._new_dataview, accelerator="Ctrl+N"
        )
        file_menu.add_command(label="Open...", command=self._open_file, accelerator="Ctrl+O")
        file_menu.add_separator()
        file_menu.add_command(label="Save", command=self._save_current, accelerator="Ctrl+S")
        file_menu.add_command(label="Save As...", command=self._save_as)
        file_menu.add_command(label="Export...", command=self._export)
        file_menu.add_separator()
        file_menu.add_command(
            label="Close Tab", command=self._close_current_tab, accelerator="Ctrl+W"
        )
        file_menu.add_separator()
        file_menu.add_command(label="Close Window", command=self._on_close)

        # Bind keyboard shortcuts
        self.bind("<Control-n>", lambda e: self._new_dataview())
        self.bind("<Control-o>", lambda e: self._open_file())
        self.bind("<Control-s>", lambda e: self._save_current())
        self.bind("<Control-w>", lambda e: self._close_current_tab())

        # Edit menu
        edit_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Edit", menu=edit_menu)
        edit_menu.add_command(label="Clear Selected Rows", command=self._clear_selected_rows)
        edit_menu.add_separator()
        edit_menu.add_command(label="Refresh Nicknames", command=self._refresh_nicknames)

        # View menu
        view_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="View", menu=view_menu)
        view_menu.add_command(label="Refresh File List", command=self._refresh_file_list)

    def _open_selected(self) -> None:
        """Open the selected file from the list."""
        selection = self.file_listbox.curselection()
        if not selection:
            return

        idx = selection[0]
        if idx < len(self._file_paths):
            self._open_dataview(self._file_paths[idx])

    def _on_file_double_click(self, event) -> None:
        """Handle double-click on file list."""
        self._open_selected()

    def _on_tab_changed(self, event) -> None:
        """Handle tab change event."""
        pass  # Could update window title, etc.

    def _create_widgets(self) -> None:
        """Create the main UI widgets."""
        # Main paned window for sidebar + content
        self.paned = ttk.PanedWindow(self, orient=tk.HORIZONTAL)
        self.paned.pack(fill=tk.BOTH, expand=True)

        # Left sidebar: File list
        sidebar = ttk.Frame(self.paned)
        self.paned.add(sidebar, weight=0)

        ttk.Label(sidebar, text="DataViews", font=("TkDefaultFont", 10, "bold")).pack(
            pady=(5, 2), padx=5, anchor=tk.W
        )

        # File listbox with scrollbar
        list_frame = ttk.Frame(sidebar)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        self.file_listbox = tk.Listbox(list_frame, width=20)
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.file_listbox.yview)
        self.file_listbox.config(yscrollcommand=scrollbar.set)

        self.file_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # Double-click to open
        self.file_listbox.bind("<Double-1>", self._on_file_double_click)

        # Buttons below file list
        btn_frame = ttk.Frame(sidebar)
        btn_frame.pack(fill=tk.X, padx=5, pady=(0, 5))

        ttk.Button(btn_frame, text="New", command=self._new_dataview, width=8).pack(
            side=tk.LEFT, padx=2
        )
        ttk.Button(btn_frame, text="Open", command=self._open_selected, width=8).pack(
            side=tk.LEFT, padx=2
        )
        ttk.Button(btn_frame, text="Refresh", command=self._refresh_file_list, width=8).pack(
            side=tk.LEFT, padx=2
        )

        # Right panel: Notebook with tabs
        self.content = ttk.Frame(self.paned)
        self.paned.add(self.content, weight=1)

        self.notebook = ttk.Notebook(self.content)
        self.notebook.pack(fill=tk.BOTH, expand=True)

        # Bind tab change
        self.notebook.bind("<<NotebookTabChanged>>", self._on_tab_changed)

        # Initial sash position (sidebar width)
        self.after(100, lambda: self.paned.sashpos(0, 180))

    def __init__(
        self,
        parent: tk.Widget,
        shared_data: SharedDataviewData,
        title_suffix: str = "",
    ):
        """Initialize the Dataview Editor window.

        Args:
            parent: Parent widget (usually the main app window)
            shared_data: SharedDataviewData for managing dataviews
            title_suffix: Optional suffix for window title (e.g., project name)
        """
        super().__init__(parent)

        self.shared_data = shared_data
        self.title_suffix = title_suffix

        # Track open panels by file path
        self._open_panels: dict[Path | None, DataviewPanel] = {}
        self._untitled_counter = 0

        # Configure window
        self._setup_window()
        self._create_menu()
        self._create_widgets()

        # Register with shared data
        shared_data.register_window(self)

        # Refresh file list
        self._refresh_file_list()

        # Handle window close
        self.protocol("WM_DELETE_WINDOW", self._on_close)

    def has_unsaved_changes(self) -> bool:
        """Check if any open dataviews have unsaved changes."""
        return any(panel.is_dirty for panel in self._open_panels.values())

    def save_all(self) -> None:
        """Save all open dataviews that have file paths."""
        for panel in self._open_panels.values():
            if panel.file_path and panel.is_dirty:
                panel.save()
                self._update_tab_title(panel)

    def add_address_to_current(self, address: str) -> bool:
        """Add an address to the currently active dataview.

        Args:
            address: The address to add

        Returns:
            True if added successfully
        """
        panel = self._get_current_panel()
        if panel:
            return panel.add_address(address)
        return False
