from __future__ import annotations

from typing import TYPE_CHECKING, ClassVar

from textual.containers import Horizontal
from textual.screen import Screen
from textual.widgets import Footer, Header

from smartwallet.persistence import load_budget, save_budget
from smartwallet.ui.widgets.budget_tree import BudgetTree
from smartwallet.ui.widgets.detail_panel import DetailPanel

if TYPE_CHECKING:
    from textual.app import ComposeResult
    from textual.binding import BindingType

    from smartwallet.model.base import BudgetComponent
    from smartwallet.model.category import Category
    from smartwallet.ui.modals.add_transaction import TransactionData


class MainScreen(Screen):
    BINDINGS: ClassVar[list[BindingType]] = [
        ("a", "add_category", "Add Category"),
        ("t", "add_transaction", "Add Transaction"),
        ("e", "edit_node", "Edit"),
        ("d", "delete_node", "Delete"),
        ("tab", "next_node", "Next Node"),
        ("shift+tab", "prev_node", "Previous Node"),
        ("q", "quit", "Quit"),
    ]

    def __init__(self) -> None:
        super().__init__()
        self._root: Category = load_budget()

    def compose(self) -> ComposeResult:
        yield Header()
        with Horizontal(id="main-container"):
            yield BudgetTree(self._root)
            yield DetailPanel(id="detail-panel")
        yield Footer()

    def on_tree_node_highlighted(self, event: BudgetTree.NodeHighlighted) -> None:
        component: BudgetComponent | None = event.node.data
        panel = self.query_one(DetailPanel)
        panel.selected = component

    def _autosave(self) -> None:
        save_budget(self._root)
        tree = self.query_one(BudgetTree)
        tree.rebuild()
        panel = self.query_one(DetailPanel)
        panel.selected = None
        if tree.cursor_node is not None:
            panel.selected = tree.cursor_node.data

    def _get_selected_component(self) -> BudgetComponent | None:
        tree = self.query_one(BudgetTree)
        if tree.cursor_node is not None:
            return tree.cursor_node.data
        return None

    def _get_parent_category(self) -> Category | None:
        from smartwallet.model.category import Category as CategoryModel

        selected = self._get_selected_component()
        if selected is None:
            return self._root

        if isinstance(selected, CategoryModel):
            return selected

        tree = self.query_one(BudgetTree)
        if tree.cursor_node is not None and tree.cursor_node.parent is not None:
            parent_data = tree.cursor_node.parent.data
            if isinstance(parent_data, CategoryModel):
                return parent_data

        return self._root

    def action_add_category(self) -> None:
        from smartwallet.ui.modals.add_category import AddCategoryModal

        parent = self._get_parent_category()

        def _on_result(name: str | None) -> None:
            if name is None:
                return
            from smartwallet.factory import BudgetFactory

            new_cat = BudgetFactory.create_category(name)
            if parent is not None:
                parent.add(new_cat)
            self._autosave()
            self.notify(f'Category "{name}" added.')

        self.app.push_screen(AddCategoryModal(), _on_result)

    def action_add_transaction(self) -> None:
        from smartwallet.ui.modals.add_transaction import AddTransactionModal

        parent = self._get_parent_category()

        def _on_result(data: TransactionData | None) -> None:
            if data is None:
                return
            from smartwallet.factory import BudgetFactory

            try:
                txn = BudgetFactory.create_transaction(
                    name=data.name,
                    amount=data.amount,
                    trans_date=data.date or None,
                )
            except ValueError as exc:
                self.notify(str(exc), severity="error")
                return

            if parent is not None:
                parent.add(txn)
            self._autosave()
            self.notify(f'Transaction "{data.name}" added.')

        self.app.push_screen(AddTransactionModal(), _on_result)

    def action_edit_node(self) -> None:
        from smartwallet.model.category import Category as CategoryModel
        from smartwallet.model.transaction import Transaction

        selected = self._get_selected_component()
        if selected is None:
            self.notify("Nothing selected.", severity="warning")
            return

        if isinstance(selected, CategoryModel):
            self._edit_category(selected)
        elif isinstance(selected, Transaction):
            self._edit_transaction(selected)

    def _edit_category(self, category: Category) -> None:
        from smartwallet.ui.modals.edit_category import EditCategoryModal

        def _on_result(name: str | None) -> None:
            if name is None:
                return
            from smartwallet.factory import BudgetFactory

            try:
                BudgetFactory.update_category(category, name)
            except ValueError as exc:
                self.notify(str(exc), severity="error")
                return

            self._autosave()
            self.notify(f'Category renamed to "{name}".')

        self.app.push_screen(EditCategoryModal(category.name), _on_result)

    def _edit_transaction(self, transaction: BudgetComponent) -> None:
        from smartwallet.model.transaction import Transaction
        from smartwallet.ui.modals.edit_transaction import EditTransactionModal

        if not isinstance(transaction, Transaction):
            return

        def _on_result(data: TransactionData | None) -> None:
            if data is None:
                return
            from smartwallet.factory import BudgetFactory

            try:
                BudgetFactory.update_transaction(
                    transaction,
                    name=data.name,
                    amount=data.amount,
                    trans_date=data.date or None,
                )
            except ValueError as exc:
                self.notify(str(exc), severity="error")
                return

            self._autosave()
            self.notify(f'Transaction "{data.name}" updated.')

        self.app.push_screen(
            EditTransactionModal(
                current_name=transaction.name,
                current_amount=transaction.amount,
                current_date=transaction.transaction_date,
            ),
            _on_result,
        )

    def action_delete_node(self) -> None:
        from smartwallet.ui.modals.confirm_delete import ConfirmDeleteModal

        selected = self._get_selected_component()
        if selected is None:
            self.notify("Nothing selected.", severity="warning")
            return

        if selected is self._root:
            self.notify("Cannot delete the root category.", severity="warning")
            return

        def _on_result(confirmed: bool) -> None:
            if not confirmed:
                return
            self._remove_component(selected.id)
            self._autosave()
            panel = self.query_one(DetailPanel)
            panel.selected = None
            self.notify(f'"{selected.name}" deleted.')

        self.app.push_screen(
            ConfirmDeleteModal(selected.name),
            _on_result,
        )

    def _remove_component(self, component_id: str) -> None:
        self._remove_from(self._root, component_id)

    def _remove_from(self, category: Category, component_id: str) -> bool:
        from smartwallet.model.category import Category as CategoryModel

        for child in category.children:
            if child.id == component_id:
                category.remove(component_id)
                return True
            if isinstance(child, CategoryModel) and self._remove_from(
                child, component_id
            ):
                return True
        return False

    def action_next_node(self) -> None:
        self.query_one(BudgetTree).action_cursor_down()

    def action_prev_node(self) -> None:
        self.query_one(BudgetTree).action_cursor_up()

    def action_quit(self) -> None:
        self.app.exit()
