from __future__ import annotations

import tkinter as tk
from collections.abc import Callable, Iterable
from dataclasses import dataclass
from tkinter import ttk

MessageType = str


@dataclass(frozen=True)
class MessageAction:
    label: str
    callback: Callable[[], None]


@dataclass(frozen=True)
class TabMessage:
    message_type: MessageType
    text: str
    action: MessageAction | None = None


_TYPE_STYLES = {
    "error": "Error",
    "notification": "Notification",
    "warning": "Warning",
    "info": "Info",
}


class MessageBannerList(ttk.Frame):
    def __init__(self, master: tk.Misc):
        super().__init__(master)
        self.columnconfigure(0, weight=1)
        self._messages: list[TabMessage] = []

    def set_messages(self, messages: Iterable[TabMessage]) -> None:
        self._messages = [message for message in messages if message.text.strip()]
        self._render()
        if self._messages:
            self.grid()
        else:
            self.grid_remove()

    def _render(self) -> None:
        for child in self.winfo_children():
            child.destroy()
        for row_index, message in enumerate(self._messages):
            style_name = _message_style_name(message.message_type)
            banner = ttk.Frame(self, style=f"Message{style_name}Border.TFrame", padding=1)
            banner.grid(row=row_index, column=0, sticky="ew", pady=(0, 6))
            banner.columnconfigure(0, weight=1)
            body = ttk.Frame(banner, style=f"Message{style_name}.TFrame", padding=(10, 8))
            body.grid(row=0, column=0, sticky="ew")
            body.columnconfigure(0, weight=1)
            label = ttk.Label(
                body,
                text=message.text,
                style=f"Message{style_name}.TLabel",
                wraplength=900,
                justify="left",
            )
            label.grid(row=0, column=0, sticky="ew")
            body.bind(
                "<Configure>",
                lambda event, target=label: target.configure(wraplength=max(240, event.width - 180)),
                add="+",
            )
            if message.action is not None:
                ttk.Button(
                    body,
                    text=message.action.label,
                    command=message.action.callback,
                ).grid(row=0, column=1, sticky="e", padx=(12, 0))


def _message_style_name(message_type: MessageType) -> str:
    return _TYPE_STYLES.get(message_type.casefold(), "Info")
