import sys
import os
from pathlib import Path
from textual import events
from textual.app import App, ComposeResult
from textual.containers import Horizontal, Vertical, VerticalScroll, Center
from textual.widgets import Header, Footer, DataTable, Input, Button, Label, Static, Checkbox, DirectoryTree, Select
from textual.screen import ModalScreen, Screen
from textual.binding import Binding
from textual.message import Message

from .models import BibManager
from .config import load_config, save_config



class BibFileTree(DirectoryTree):
    """A DirectoryTree filtered to highlight .bib files."""
    def filter_paths(self, paths: list[Path]) -> list[Path]:
        return [path for path in paths if not path.name.startswith(".")]

class FilePickerScreen(Screen[str]):
    """The startup screen to select or enter a .bib file path."""
    
    def compose(self) -> ComposeResult:
        yield Vertical(
            Label("Welcome to BibManager", id="picker-title"),
            Label("Select a .bib file from the list or enter a manual path below:"),
            BibFileTree("./", id="file-tree"),
            Horizontal(
                Input(placeholder="Manual path to .bib file...", id="manual-path"),
                Button("Load File", variant="success", id="btn-load-manual"),
            ),
            id="picker-container"
        )

    def on_directory_tree_file_selected(self, event: DirectoryTree.FileSelected) -> None:
        if event.path.suffix == ".bib":
            self.dismiss(str(event.path))
        else:
            self.notify("Please select a .bib file", severity="error")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn-load-manual":
            path = self.query_one("#manual-path", Input).value
            if os.path.exists(path) and path.endswith(".bib"):
                self.dismiss(path)
            else:
                self.notify("Invalid file path or not a .bib file", variant="error")


class EditEntryModal(ModalScreen[dict]):
    """Modal with form inputs to edit an entry's fields."""
    def __init__(self, entry_data: dict):
        super().__init__()
        self.entry_data = entry_data.copy()
        
        # Prepopulate missing fields
        entry_lower = {k.lower(): v for k, v in self.entry_data.items()}
        entry_type = entry_lower.get('entrytype', '').lower()
        
        if 'title' not in entry_lower:
            self.entry_data['title'] = ""
        
        if 'year' not in entry_lower and 'date' not in entry_lower:
            self.entry_data['year'] = ""
                
        if entry_type in ['article', 'inproceedings', 'incollection', 'book']:
            if 'author' not in entry_lower and 'editor' not in entry_lower:
                self.entry_data['author'] = ""
                
        if entry_type == 'article' and 'journal' not in entry_lower and 'journaltitle' not in entry_lower:
            self.entry_data['journal'] = ""
        elif entry_type in ['inproceedings', 'incollection'] and 'booktitle' not in entry_lower:
            self.entry_data['booktitle'] = ""

        self.inputs = {}

    def compose(self) -> ComposeResult:
        widgets = []
        for k, v in self.entry_data.items():
            if k != 'ID':
                widgets.append(Label(k.capitalize()))
                widgets.append(Input(value=str(v), id=f"edit-{k}"))
                
        yield Vertical(
            Label(f"Editing: {self.entry_data.get('ID')}", id="modal-title"),
            VerticalScroll(
                *widgets,
                id="modal-content"
            ),
            Horizontal(
                Input(placeholder="New field name", id="new-field-name"),
                Input(placeholder="New field value", id="new-field-value"),
                Button("Add", variant="primary", id="btn-add-field"),
                id="add-field-container"
            ),
            Horizontal(
                Button("Save Changes", variant="success", id="btn-save"),
                Button("Cancel", variant="primary", id="btn-cancel"),
                id="modal-buttons"
            ),
            id="modal-dialog"
        )

    def on_mount(self) -> None:
        for k in self.entry_data:
            if k != 'ID':
                self.inputs[k] = self.query_one(f"#edit-{k}", Input)

    async def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn-add-field":
            name_input = self.query_one("#new-field-name", Input)
            val_input = self.query_one("#new-field-value", Input)
            k = name_input.value.strip().lower()
            v = val_input.value.strip()
            
            if not k:
                self.notify("Field name cannot be empty", severity="error")
                return
            if k == 'id':
                self.notify("Cannot add 'id' field", severity="error")
                return
            if k in self.inputs:
                self.notify("Field already exists", severity="error")
                return
                
            new_input = Input(value=v, id=f"edit-{k}")
            self.inputs[k] = new_input
            
            scroll_container = self.query_one("#modal-content")
            await scroll_container.mount(Label(k.capitalize()))
            await scroll_container.mount(new_input)
            
            name_input.value = ""
            val_input.value = ""
            self.notify(f"Added field '{k}'")
            
        elif event.button.id == "btn-save":
            new_data = {k: inp.value for k, inp in self.inputs.items()}
            self.dismiss({"action": "save_edit", "data": new_data})
        elif event.button.id == "btn-cancel":
            self.dismiss({"action": "cancel"})


class DetailModal(ModalScreen[dict]):
    """Single entry view modal, now with an Edit option."""
    def __init__(self, entry_data: dict):
        super().__init__()
        self.entry_data = entry_data

    def compose(self) -> ComposeResult:
        content = "\n".join([f"{k}: {v}" for k, v in self.entry_data.items() if k != 'ID'])
        
        yield Vertical(
            Label(f"Entry ID: {self.entry_data.get('ID')}", id="modal-title"),
            Label(f"Type: {self.entry_data.get('ENTRYTYPE', 'N/A')}", classes="detail-type"),
            Static(content, id="modal-content"),
            Horizontal(
                Button("Edit Entry", variant="warning", id="btn-edit"),
                Button("Delete Entry", variant="error", id="btn-delete"),
                Button("Close", variant="primary", id="btn-cancel"),
                id="modal-buttons"
            ),
            id="modal-dialog"
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn-edit":
            self.dismiss({"action": "open_edit"})
        elif event.button.id == "btn-delete":
            self.dismiss({"action": "delete"})
        else:
            self.dismiss({"action": "cancel"})


class DuplicateGroupModal(ModalScreen[dict]):
    """Displays a whole group of duplicates and allows multi-selection for deletion."""
    def __init__(self, group: list):
        super().__init__()
        self.group = group
        self.checkboxes = {}

    def compose(self) -> ComposeResult:
        content = []
        for e in self.group:
            details_lines = []
            for k, v in e.items():
                if k not in ('ID', 'ENTRYTYPE') and str(v).strip():
                    details_lines.append(f"  {k}: {v}")
            details_str = "\n".join(details_lines)
            
            content.append(Vertical(
                Checkbox(f"{e.get('ID')} - {e.get('title', '')}", id=f"chk_{e.get('ID')}"),
                Label(f"Type: {e.get('ENTRYTYPE', 'N/A')}", classes="duplicate-type"),
                Label(details_str, classes="duplicate-details"),
                classes="duplicate-item"
            ))

        yield Vertical(
            Label("Manage Duplicate Group", id="modal-title"),
            VerticalScroll(
                *content,
                id="modal-content"
            ),
            Horizontal(
                Button("Delete Selected", variant="error", id="btn-delete-selected"),
                Button("Cancel", variant="primary", id="btn-cancel"),
                id="modal-buttons"
            ),
            id="modal-dialog"
        )

    def on_mount(self) -> None:
        for e in self.group:
            self.checkboxes[e.get('ID')] = self.query_one(f"#chk_{e.get('ID')}", Checkbox)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn-delete-selected":
            to_delete = [e_id for e_id, cb in self.checkboxes.items() if cb.value]
            self.dismiss({"action": "delete_multiple", "ids": to_delete})
        else:
            self.dismiss({"action": "cancel"})

class DiffModal(ModalScreen[None]):
    """Displays a text diff of the changes."""
    def __init__(self, diff_text: str):
        super().__init__()
        self.diff_text = diff_text

    def compose(self) -> ComposeResult:
        yield Vertical(
            Label("Changes Diff", id="modal-title"),
            VerticalScroll(
                Static(self.diff_text),
                id="modal-content"
            ),
            Button("Close", variant="primary", id="btn-close-diff"),
            id="modal-dialog"
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self.dismiss()

class SaveModal(ModalScreen[dict]):
    """Confirmation modal before saving changes to disk."""
    def __init__(self, summary_text: str, default_filepath: str):
        super().__init__()
        self.summary_text = summary_text
        self.default_filepath = default_filepath

    def compose(self) -> ComposeResult:
        yield Vertical(
            Label("Save Changes", id="modal-title"),
            VerticalScroll(
                Label("Summary of changes:", classes="section-label"),
                Static(self.summary_text, classes="summary-box"),
                Label("Target File:", classes="section-label"),
                Input(value=self.default_filepath, id="input-filepath"),
                Checkbox("Create Backup (.BKP)", value=True, id="chk-backup"),
                id="modal-content"
            ),
            Horizontal(
                Button("See Diff", variant="warning", id="btn-see-diff"),
                Button("Proceed", variant="success", id="btn-proceed"),
                Button("Cancel", variant="primary", id="btn-cancel"),
                id="modal-buttons"
            ),
            id="modal-dialog"
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn-see-diff":
            self.dismiss({"action": "see_diff"})
        elif event.button.id == "btn-proceed":
            filepath = self.query_one("#input-filepath", Input).value
            backup = self.query_one("#chk-backup", Checkbox).value
            self.dismiss({"action": "save_confirm", "filepath": filepath, "backup": backup})
        elif event.button.id == "btn-cancel":
            self.dismiss({"action": "cancel"})

class SettingsModal(ModalScreen[dict]):
    """Modal for configuring app settings."""
    def __init__(self, current_config: dict):
        super().__init__()
        self.config = current_config.copy()

    def compose(self) -> ComposeResult:
        yield Vertical(
            Label("Settings", id="modal-title"),
            VerticalScroll(
                Label("Theme Settings", classes="section-label"),
                Horizontal(
                    Label("Theme:", classes="inline-label"),
                    Select(
                        options=[("Dark", "dark"), ("Light", "light")],
                        value="dark" if self.config.get("dark_mode") is not False else "light",
                        id="select-theme",
                        allow_blank=False
                    )
                ),
                Label("Column Widths", classes="section-label"),
                *[
                    Horizontal(
                        Label(f"{col}:", classes="inline-label"),
                        Input(value=str(width), id=f"col-{col}")
                    ) for col, width in self.config.get("columns", {}).items()
                ],
                id="modal-content"
            ),
            Horizontal(
                Button("Save Settings", variant="success", id="btn-save-settings"),
                Button("Reset Defaults", variant="warning", id="btn-reset-settings"),
                Button("Cancel", variant="primary", id="btn-cancel-settings"),
                id="modal-buttons"
            ),
            id="modal-dialog"
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn-save-settings":
            new_config = {
                "dark_mode": self.query_one("#select-theme", Select).value == "dark",
                "columns": {}
            }
            for col in self.config.get("columns", {}):
                try:
                    val = int(self.query_one(f"#col-{col}", Input).value)
                    new_config["columns"][col] = val
                except ValueError:
                    new_config["columns"][col] = self.config["columns"][col]
            self.dismiss({"action": "save", "config": new_config})
            
        elif event.button.id == "btn-reset-settings":
            from .config import DEFAULT_CONFIG
            self.dismiss({"action": "save", "config": DEFAULT_CONFIG.copy()})
            
        elif event.button.id == "btn-cancel-settings":
            self.dismiss({"action": "cancel"})


class BibApp(App):
    CSS = """
    Screen {
        layout: vertical;
    }
    
    #search-bar {
        margin: 1;
        width: 100%;
    }
    
    #action-bar {
        height: 3;
        margin-bottom: 1;
        align: center middle;
    }
    
    DataTable {
        height: 1fr;
    }
    
    DetailModal {
        align: center middle;
    }

    #threshold-container {
        width: auto;
        height: auto;
        align: center middle;
        margin-left: 1;
    }

    #threshold-label {
        padding: 1;
    }

    #input-threshold {
        width: 10;
    }

    #modal-dialog {
        padding: 1 2;
        width: 80%;
        height: 80%;
        border: thick $background 80%;
        background: $surface;
    }
    
    #modal-title {
        text-align: center;
        text-style: bold;
        padding-bottom: 1;
    }
    
    #modal-content {
        height: 1fr;
        padding: 1;
        border: solid $primary;
        overflow-y: auto;
    }
    
    #modal-buttons {
        height: auto;
        margin-top: 1;
        align: center middle;
    }
    
    .section-label {
        margin-top: 1;
        text-style: bold;
    }
    
    .summary-box {
        margin-top: 1;
        padding: 1;
        border: solid $accent;
        color: $text-muted;
    }

    .inline-label {
        width: 15;
        content-align: right middle;
        padding-right: 1;
    }

    #add-field-container {
        height: auto;
        margin-top: 1;
        align: center middle;
    }
    
    #new-field-name {
        width: 1fr;
    }
    
    #new-field-value {
        width: 2fr;
    }

    .duplicate-item {
        height: auto;
        border: solid $accent;
        margin-bottom: 1;
        padding: 1;
    }
    
    .duplicate-details {
        margin-left: 4;
        color: $text-muted;
    }

    #picker-container {
        padding: 2 4;
        background: $surface;
    }

    #picker-title {
        color: $accent;
        margin-bottom: 1;
    }

    #file-tree {
        height: 15;
        border: tall $primary;
        margin-bottom: 1;
    }
    """

    BINDINGS = [
        ("q", "quit", "Quit"),
        ("a", "apply", "Apply Changes"),
        ("u", "undo", "Undo"),
        ("r", "redo", "Redo"),
        ("space", "toggle_selection", "Select"),
    ]

    def __init__(self):
        super().__init__()
        self.manager = None
        self.bib_path = ""
        self.current_filter = "all"
        self.search_term = ""
        self.sort_column = "ID"
        self.sort_reverse = False
        self.sim_threshold = 85.0
        self.duplicate_groups = []
        self.active_entry_id = None
        self.selected_ids = set() # Track selected entry IDs for bulk actions
        self.app_config = load_config()
    
    def compose(self) -> ComposeResult:
        yield Header()
        yield Input(placeholder="Search by ID, Title, or Author...", id="search-bar")
        yield Horizontal(
            Button("Show All", id="filter-all", variant="primary"),
            Button("Show Duplicates", id="filter-duplicates", variant="primary"),
            Horizontal(
                Label("Threshold:", id="threshold-label"),
                Input(value="85", id="input-threshold"),
                id="threshold-container"
            ),
            Button("Show Incomplete", id="filter-incomplete", variant="warning"),
            Button("Remove Exact Matches", id="btn-purge", variant="error"),
            Button("Delete Selected", id="btn-delete-selected", variant="error"),
            Button("Undo", id="btn-undo", variant="default"),
            Button("Redo", id="btn-redo", variant="default"),
            Button("Settings", id="btn-settings", variant="primary"),
            Button("Apply", id="btn-apply", variant="success"),
            id="action-bar"
        )
        yield DataTable(id="bib-table")
        yield Footer()

    def on_mount(self) -> None:
        is_dark = self.app_config.get("dark_mode", True)
        self.theme = "textual-dark" if is_dark else "textual-light"
        self.dark = is_dark
        # Just set up the table and show the picker
        table = self.query_one(DataTable)
        table.cursor_type = "row"
        
        # Show the picker immediately
        self.push_screen(FilePickerScreen(), self.load_bib_file)

    def load_bib_file(self, path: str) -> None:
        if path:
            self.bib_path = path
            self.manager = BibManager(path)
            self.refresh_table()
            self.notify(f"Loaded: {os.path.basename(path)}")

    def refresh_table(self) -> None:
        if not self.manager:
            return

        table = self.query_one(DataTable)
        
        # Save cursor position before clearing
        saved_cursor = table.cursor_coordinate
        
        table.clear(columns=True)
        
        # Show/Hide Purge and Delete Selected buttons
        self.query_one("#btn-purge", Button).display = (self.current_filter == "duplicates")
        self.query_one("#btn-delete-selected", Button).display = len(self.selected_ids) > 0
        
        # Enable/Disable Undo/Redo
        self.query_one("#btn-undo", Button).disabled = not (self.manager and self.manager.history_index > 0)
        self.query_one("#btn-redo", Button).disabled = not (self.manager and self.manager.history_index < len(self.manager.history) - 1)
        
        # Determine columns
        cols = self.app_config.get("columns", {})
        table.add_column("Sel", width=5)
        table.add_column("ID", width=cols.get("ID", 25))
        table.add_column("Type", width=cols.get("Type", 12))
        table.add_column("Title", width=cols.get("Title", 50))
        table.add_column("Author", width=cols.get("Author", 30))
        table.add_column("Journal", width=cols.get("Journal", 30))
        table.add_column("Year", width=cols.get("Year", 10))
        if self.current_filter != "all":
            table.add_column("Reason", width=40)
        
        entries = []
        self.duplicate_reasons = {}
        self.duplicate_groups = []
        
        if self.current_filter == "all":
            entries = list(self.manager.get_entries())
        elif self.current_filter == "incomplete":
            entries = list(self.manager.find_incomplete())
        elif self.current_filter == "duplicates":
            # Pass our dynamically tracked threshold to the models.py method
            self.duplicate_groups, reasons = self.manager.find_duplicates(threshold=self.sim_threshold)
            self.duplicate_reasons = reasons 
            
            # For duplicates, we don't sort the whole list as it breaks the group structure
            for g in self.duplicate_groups:
                entries.extend(g)
                entries.append({"ID": "---", "ENTRYTYPE": "", "title": ""})
        
        # Apply search filter
        if self.search_term and self.current_filter != "duplicates":
            filtered = []
            for e in entries:
                text = (e.get("ID", "") + e.get("title", "") + e.get("author", "")).lower()
                if self.search_term in text:
                    filtered.append(e)
            entries = filtered
            
        # Apply sorting (except for duplicates where structure matters)
        if self.current_filter != "duplicates":
            key_map = {
                "ID": "ID",
                "Type": "ENTRYTYPE",
                "Title": "title",
                "Author": "author",
                "Journal": "journal",
                "Year": "year",
                "Reason": "reason_incomplete" # Only relevant for incomplete
            }
            sort_key = key_map.get(self.sort_column, "ID")
            
            # Sort with a small helper to handle missing keys gracefully
            def get_sort_val(x, key):
                v = x.get(key, "")
                if key == "journal" and not v:
                    v = x.get("journaltitle", "")
                return str(v).lower()

            entries.sort(
                key=lambda x: get_sort_val(x, sort_key), 
                reverse=self.sort_reverse
            )

        seen_keys = set()
        for e in entries:
            base_id = e.get('ID', '')
            row_key = base_id
            
            # Handle unique row keys for duplicate display
            counter = 1
            while row_key in seen_keys:
                row_key = f"{base_id}__dup{counter}"
                counter += 1
            seen_keys.add(row_key)
            
            # DETERMINE THE REASON STRING
            reason_str = ""
            if self.current_filter == "duplicates":
                reason_str = self.duplicate_reasons.get(base_id, "") if base_id != "---" else ""
            elif self.current_filter == "incomplete":
                reason_str = e.get('reason_incomplete', "")

            row_data = [
                e.get('ID', ''), 
                e.get('ENTRYTYPE', ''),
                e.get('title', '')[:50] + ('...' if len(e.get('title', '')) > 50 else ''),
                e.get('author', '')[:30] + ('...' if len(e.get('author', '')) > 30 else ''),
                e.get('journal', '') if e.get('journal') else e.get('journaltitle', '')[:30],
                e.get('year', '') if e.get('year') else e.get('date', '')
            ]
            if self.current_filter != "all":
                row_data.append(reason_str)
 
            # Selection marker
            if base_id == "---":
                row_data.insert(0, "")
            else:
                is_selected = base_id in self.selected_ids
                sel_marker = "[X]" if is_selected else "[ ]"
                row_data.insert(0, sel_marker)

            table.add_row(*row_data, key=row_key)
            
        # Restore cursor position
        if saved_cursor:
            # Clamp to current bounds
            max_row = len(table.rows) - 1
            max_col = len(table.columns) - 1
            if max_row >= 0:
                target_row = min(saved_cursor.row, max_row)
                target_col = min(saved_cursor.column, max_col)
                table.move_cursor(row=target_row, column=target_col)

    def on_data_table_header_selected(self, event: DataTable.HeaderSelected) -> None:
        # Use simple label from event (handles str or Text object)
        col_label = str(event.label)
        
        if self.sort_column == col_label:
            self.sort_reverse = not self.sort_reverse
        else:
            self.sort_column = col_label
            self.sort_reverse = False
        
        self.refresh_table()

    # Remove the redundant/incomplete on_data_table_row_selected at line 581
    # and the cell selected handler here to merge them smarter below.
    
    def on_click(self, event: events.Click) -> None:
        # Use on_click and coordinate lookup if CellClicked is missing
        table = self.query_one(DataTable)
        try:
            # This helps distinguish mouse clicks on the selection column
            coordinate = table.get_coordinate_at(event.x, event.y)
            if coordinate.column == 0:
                row_key = table.coordinate_to_cell_key(coordinate).row_key.value
                entry_id = row_key.split("__dup")[0]
                if entry_id != "---":
                    self.toggle_selection(entry_id)
        except Exception:
            # Click might have been outside table cells (header, padding)
            pass

    def toggle_selection(self, entry_id: str) -> None:
        if entry_id in self.selected_ids:
            self.selected_ids.remove(entry_id)
        else:
            self.selected_ids.add(entry_id)
        self.refresh_table()

    def action_toggle_selection(self) -> None:
        table = self.query_one(DataTable)
        if table.cursor_coordinate:
            cell_key = table.coordinate_to_cell_key(table.cursor_coordinate)
            row_key = cell_key.row_key.value
            entry_id = row_key.split("__dup")[0]
            if entry_id != "---":
                self.toggle_selection(entry_id)

    def on_input_changed(self, event: Input.Changed) -> None:
        if event.input.id == "search-bar":
            self.search_term = event.value.lower()
            self.refresh_table()
        elif event.input.id == "input-threshold":
            # If the user clears the input, don't crash, just wait for a number
            if not event.value:
                return
            try:
                val = float(event.value)
                self.sim_threshold = val
                if self.current_filter == "duplicates":
                    self.refresh_table()
            except ValueError:
                pass

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "filter-all":
            self.current_filter = "all"
            self.refresh_table()
        elif event.button.id == "filter-duplicates":
            self.current_filter = "duplicates"
            self.refresh_table()
        elif event.button.id == "filter-incomplete":
            self.current_filter = "incomplete"
            self.refresh_table()
        elif event.button.id == "btn-purge":
            count = self.manager.auto_purge_duplicates()
            if count > 0:
                self.selected_ids.clear() # Clear potential dead selections
                self.notify(f"Automatically purged {count} exact duplicates.")
                self.refresh_table()
            else:
                self.notify("No exact duplicates found to purge.")
        elif event.button.id == "btn-delete-selected":
            self.action_delete_selected()
        elif event.button.id == "btn-undo":
            self.action_undo()
        elif event.button.id == "btn-redo":
            self.action_redo()
        elif event.button.id == "btn-settings":
            self.push_screen(SettingsModal(self.app_config), self.handle_settings_result)
        elif event.button.id == "btn-apply":
            self.action_apply()

    def handle_settings_result(self, result: dict) -> None:
        if not result or result.get("action") == "cancel":
            return
            
        if result.get("action") == "save":
            self.app_config = result.get("config")
            save_config(self.app_config)
            # Apply theme change explicitly and aggressively
            is_dark = self.app_config.get("dark_mode", True)
            self.theme = "textual-dark" if is_dark else "textual-light"
            self.dark = is_dark
            self.refresh_table()
            self.notify(f"Settings saved. (Theme: {'Dark' if is_dark else 'Light'})", severity="information")

    def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
        # Avoid opening details if the user just clicked the selection column
        table = self.query_one(DataTable)
        if table.cursor_coordinate and table.cursor_coordinate.column == 0:
             return
             
        row_key = event.row_key.value
        entry_id = row_key.split("__dup")[0]
        if entry_id == "---":
            return
            
        self.active_entry_id = entry_id

        if self.current_filter == "duplicates":
            target_group = next((g for g in self.duplicate_groups if any(e.get('ID') == entry_id for e in g)), None)
            if target_group:
                self.push_screen(DuplicateGroupModal(target_group), self.handle_modal_result)
                return

        # Otherwise, open the single-entry Detail Modal
        entry_data = next((e for e in self.manager.get_entries() if e.get("ID") == entry_id), None)
        if entry_data:
            self.push_screen(DetailModal(entry_data), self.handle_modal_result)

    def handle_modal_result(self, result: dict) -> None:
        # Changed this to expect a dictionary instead of a raw string
        if not result or result.get("action") == "cancel":
            return

        action = result.get("action")
        
        if action == "delete":
            self.manager.delete_entry(self.active_entry_id)
            self.notify(f"Deleted entry {self.active_entry_id}")
            self.refresh_table()
            
        elif action == "delete_multiple":
            ids_to_delete = result.get("ids", [])
            for d_id in ids_to_delete:
                self.manager.delete_entry(d_id)
            self.notify(f"Deleted {len(ids_to_delete)} entries.")
            self.refresh_table()
            
        elif action == "open_edit":
            # Pass the data to the Edit modal
            entry_data = next((e for e in self.manager.get_entries() if e.get("ID") == self.active_entry_id), None)
            if entry_data:
                self.push_screen(EditEntryModal(entry_data), self.handle_modal_result)
                
        elif action == "save_edit":
            new_data = result.get("data", {})
            self.manager.update_entry(self.active_entry_id, new_data)
            self.notify("Entry updated.")
            self.refresh_table()

    def action_undo(self) -> None:
        if self.manager and self.manager.undo():
            self.notify("Undo successful.")
            self.refresh_table()

    def action_redo(self) -> None:
        if self.manager and self.manager.redo():
            self.notify("Redo successful.")
            self.refresh_table()

    def handle_save_modal_result(self, result: dict) -> None:
        if not result or result.get("action") == "cancel":
            return
            
        action = result.get("action")
        if action == "see_diff":
            # Show diff then immediately return to save modal after diff closes
            diff_text = self.manager.get_diff()
            self.push_screen(DiffModal(diff_text), lambda _: self.action_apply())
        elif action == "save_confirm":
            filepath = result.get("filepath", self.bib_path)
            backup = result.get("backup", True)
            self.manager.save(filepath=filepath, create_backup=backup)
            
            # If the filepath was changed, update our internal reference
            if filepath != self.bib_path:
                self.bib_path = filepath
                self.manager.filepath = filepath
                
            self.notify(f"Bibliography saved to {os.path.basename(filepath)}.")

    def action_apply(self) -> None:
        if not self.manager:
            return
        summary = self.manager.get_summary()
        self.push_screen(SaveModal(str(summary), self.bib_path), self.handle_save_modal_result)

    def action_delete_selected(self) -> None:
        if not self.selected_ids:
            return
            
        count = len(self.selected_ids)
        self.manager._save_state() # Bulk action, save once
        for eid in list(self.selected_ids):
             # We need to be careful with ID matching if they are duplicates
             self.manager.db.entries = [e for e in self.manager.db.entries if e.get('ID') != eid]
        
        self.selected_ids.clear()
        self.notify(f"Deleted {count} selected entries.")
        self.refresh_table()

    def on_key(self, event: events.Key) -> None:
        # Textual Bindings handle 'space' via action_toggle_selection automatically
        if event.key == "enter":
            # Allow enter to open detail if not on selection column
            table = self.query_one(DataTable)
            if table.cursor_coordinate and table.cursor_coordinate.column != 0:
                 row_index = table.cursor_coordinate.row
                 row_key = table.row_keys[row_index].value
                 entry_id = row_key.split("__dup")[0]
                 if entry_id != "---":
                     entry_data = next((e for e in self.manager.get_entries() if e.get("ID") == entry_id), None)
                     if entry_data:
                         self.push_screen(DetailModal(entry_data), self.handle_modal_result)
                         self.active_entry_id = entry_id

if __name__ == "__main__":
    app = BibApp() 
    app.run()
