from datetime import datetime
from decimal import Decimal
from textual.reactive import reactive
from textual.types import NoSelection
from textual.message import Message

from textual.widgets import (
    DataTable,
)
from moneyterm.utils.ledger import Ledger, Transaction
from moneyterm.screens.quicklabelscreen import QuickLabelScreen
from moneyterm.screens.transactiondetailscreen import TransactionDetailScreen
from moneyterm.screens.transactionsplitscreen import TransactionSplitScreen


class TransactionTable(DataTable):
    class RowSent(Message):
        def __init__(self, row_key: str) -> None:
            super().__init__()
            self.account_number, self.txid = row_key.split(":")

    class QuickLabelChanged(Message):
        def __init__(self) -> None:
            super().__init__()

    class TransactionSplitModified(Message):
        """Message for when transaction splits are modified, added, or removed."""

        def __init__(self) -> None:
            """Initialize message.

            Args:
                None
            """
            super().__init__()

    account: reactive[str | NoSelection] = reactive(NoSelection)
    year: reactive[int | NoSelection] = reactive(NoSelection)
    month: reactive[int | NoSelection] = reactive(NoSelection)

    BINDINGS = [
        ("c", "quick_label", "Quick Label"),
        ("i", "transaction_details", "Transaction Details"),
        ("ctrl+l", "send_to_labeler", "Send to Labeler"),
        ("/", "split_transaction", "Split Transaction"),
    ]

    def __init__(self, ledger: Ledger) -> None:
        super().__init__()
        self.ledger = ledger
        self.zebra_stripes = True
        self.cursor_type = "row"
        self.id = "transactions_table"
        self.selected_row_key: str | None = None
        self.column_labels = ["Date", "Payee", "Type", "Amount", "Account", "Labels"]
        self.last_sort_label: str = ""
        self.reversed_sort: bool = False

    def add_columns_from_labels(self) -> None:
        """Add columns from column keys."""
        for label in self.column_labels:
            self.add_column(label, key=label)

    def update_data(self) -> None:
        """Update the datatable when month selection changed."""
        self.clear(columns=True)
        self.add_columns_from_labels()
        if (
            isinstance(self.account, NoSelection)
            or isinstance(self.year, NoSelection)
            or isinstance(self.month, NoSelection)
        ):
            self.selected_row_key = None
            self.add_row("No account/dates selected.", "", "", "", "", "")
            self.cursor_type = "none"
            return
        for tx in self.ledger.get_tx_by_month(self.account, self.year, self.month):
            self.add_transaction_row(tx)
        if self.selected_row_key:
            try:
                self.move_cursor(row=self.get_row_index(self.selected_row_key))
            except:
                pass

    def add_transaction_row(self, tx: Transaction) -> None:
        self.cursor_type = "row"
        all_labels = sorted(
            tx.auto_labels.bills
            + tx.auto_labels.expenses
            + tx.auto_labels.incomes
            + tx.manual_labels.bills
            + tx.manual_labels.expenses
            + tx.manual_labels.incomes
        )
        labels_with_splits = []
        for label in all_labels:
            if label in tx.splits and tx.splits[label] > 0:
                labels_with_splits.append(f"{label} (${tx.splits[label]:.2f})")
            else:
                labels_with_splits.append(label)
        self.add_row(
            tx.date.strftime("%Y-%m-%d"),
            tx.alias if tx.alias else tx.payee,
            tx.tx_type,
            tx.amount,
            tx.account.alias if tx.account.alias else tx.account.number,
            ",".join(labels_with_splits),
            key=f"{tx.account.number}:{tx.txid}",
        )

    def on_mount(self) -> None:
        """Mount the datatable."""
        self.add_columns_from_labels()

    def action_send_to_labeler(self) -> None:
        if self.selected_row_key:
            self.post_message(self.RowSent(self.selected_row_key))

    def action_transaction_details(self) -> None:
        if self.selected_row_key:
            self.log(f"Showing transaction details for {self.selected_row_key}")
            account_number, txid = self.selected_row_key.split(":")
            transaction = self.ledger.get_tx_by_txid(account_number, txid)
            self.app.push_screen(
                TransactionDetailScreen(self.ledger, transaction),
                self.label_removed,
            )

    def action_quick_label(self) -> None:
        if self.selected_row_key:
            account_number, txid = self.selected_row_key.split(":")
            transaction = self.ledger.get_tx_by_txid(account_number, txid)
            self.app.push_screen(QuickLabelScreen(self.ledger, transaction), self.quick_add_label)

    def action_split_transaction(self) -> None:
        if self.selected_row_key:
            account_number, txid = self.selected_row_key.split(":")
            transaction = self.ledger.get_tx_by_txid(account_number, txid)
            self.app.push_screen(
                TransactionSplitScreen(self.ledger, transaction),
                lambda split: self.splits_modified() if split else None,
            )

    def splits_modified(self) -> None:
        self.update_data()
        self.post_message(self.TransactionSplitModified())

    def label_removed(self, label_removed: bool) -> None:
        if label_removed:
            self.update_data()
            self.post_message(self.QuickLabelChanged())

    def quick_add_label(self, label_info: tuple[str, str]) -> None:
        if self.selected_row_key:
            label, label_type = label_info
            account_number, txid = self.selected_row_key.split(":")
            self.ledger.add_label_to_tx(account_number, txid, label, label_type, auto=False)
            self.ledger.save_ledger_pkl()
            self.update_data()
            self.post_message(self.QuickLabelChanged())

    def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
        self.selected_row_key = event.row_key.value

    def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
        self.selected_row_key = event.row_key.value

    def on_data_table_header_selected(self, event: DataTable.HeaderSelected) -> None:
        """
        Sorts the transaction table based on the selected header label.

        Args:
            event (DataTable.HeaderSelected): The event object containing the selected header label.

        Returns:
            None
        """
        if str(event.label) == self.last_sort_label:
            self.reversed_sort = not self.reversed_sort
        else:
            self.reversed_sort = False
        self.last_sort_label = str(event.label)
        if str(event.label) == "Labels":
            self.log("Sorting by labels")
            self.sort("Labels", key=lambda label: label.lower(), reverse=self.reversed_sort)
        elif str(event.label) == "Date":
            self.log("Sorting by date")
            self.sort("Date", key=lambda date: datetime.strptime(date, "%Y-%m-%d"), reverse=self.reversed_sort)
        elif str(event.label) == "Payee":
            self.log("Sorting by payee")
            self.sort("Payee", key=lambda payee: payee.lower(), reverse=self.reversed_sort)
        elif str(event.label) == "Type":
            self.log("Sorting by type")
            self.sort("Type", key=lambda tx_type: tx_type.lower(), reverse=self.reversed_sort)
        elif str(event.label) == "Amount":
            self.log("Sorting by amount")
            self.sort("Amount", key=lambda amount: Decimal(amount), reverse=self.reversed_sort)
        elif str(event.label) == "Account":
            self.log("Sorting by account")
            self.sort("Account", reverse=self.reversed_sort)

    def watch_account(self) -> None:
        """Watch for account selection changes."""
        self.update_data()

    def watch_year(self) -> None:
        """Watch for year selection changes."""
        self.update_data()

    def watch_month(self) -> None:
        """Watch for month selection changes."""
        self.update_data()
