import tkinter as tk
from collections import Counter
from collections.abc import Callable
from tkinter import messagebox, ttk

from ccma.domain.dates import format_date_for_display
from ccma.domain.models import (
    ASSET_STATUS_LABELS,
    MEMBERSHIP_STATUS_LABELS,
    Asset,
    HousekeeperFinding,
    Member,
)
from ccma.ui.labels import storage_key
from ccma.ui.sections import titled_frame

MEMBER_TABLE_COLUMNS = (
    ("number", "Nummer", 110),
    ("last_name", "Nachname", 180),
    ("first_name", "Vorname", 160),
    ("nickname", "Nickname", 160),
    ("email", "E-Mail-Adresse", 270),
    ("birth", "Geburtsdatum", 120),
    ("status", "Status", 170),
)

STATUS_FILTER_ALL = "Alle"
MEMBER_FINDING_COLORS = {
    "info": "#d9efff",
    "warning": "#fff3b0",
    "error": "#ffd0d0",
}
MEMBER_FINDING_PRIORITY = {"info": 1, "warning": 2, "error": 3}
ASSET_FILTER_ALL = "Alle"
ASSET_TABLE_COLUMNS = (
    ("label", "Bezeichnung", 260),
    ("category", "Kategorie", 140),
    ("inventory_number", "Inventarnummer", 140),
    ("status", "Status", 140),
    ("owner", "Eigentümer", 240),
    ("holder", "Gewahrsam", 240),
    ("location", "Standort", 180),
)


def _member_table_value(member: Member, column: str) -> str:
    if column == "number":
        return member.member_number
    if column == "first_name":
        return member.first_name
    if column == "last_name":
        return member.last_name
    if column == "nickname":
        return member.nickname
    if column == "email":
        return member.email
    if column == "birth":
        return member.birth_date
    if column == "status":
        return MEMBERSHIP_STATUS_LABELS.get(member.status, member.status)
    return ""


def _filter_members(members: list[Member], status_filter: str) -> list[Member]:
    if status_filter == "all":
        return list(members)
    return [member for member in members if member.status == status_filter]


def _sort_members(members: list[Member], column: str, descending: bool) -> list[Member]:
    return sorted(
        members,
        key=lambda member: _member_table_value(member, column).casefold(),
        reverse=descending,
    )


def _selected_status_filter(label: str) -> str:
    if label == STATUS_FILTER_ALL:
        return "all"
    return storage_key(MEMBERSHIP_STATUS_LABELS, label)


def _member_finding_severities(findings: list[HousekeeperFinding]) -> dict[str, str]:
    severities: dict[str, str] = {}
    for finding in findings:
        if not finding.member_id or finding.severity not in MEMBER_FINDING_PRIORITY:
            continue
        current = severities.get(finding.member_id)
        if current is None or MEMBER_FINDING_PRIORITY[finding.severity] > MEMBER_FINDING_PRIORITY[current]:
            severities[finding.member_id] = finding.severity
    return severities


def _filter_label_frame(parent: tk.Misc) -> ttk.LabelFrame:
    return titled_frame(parent, "// FILTER")


def _asset_table_value(asset: Asset, column: str, holder_label: str, owner_label: str = "") -> str:
    if column == "label":
        return asset.label
    if column == "category":
        return asset.category
    if column == "inventory_number":
        return asset.inventory_number
    if column == "status":
        return ASSET_STATUS_LABELS.get(asset.status, asset.status)
    if column == "holder":
        return holder_label
    if column == "owner":
        return owner_label
    if column == "location":
        return asset.location
    return ""


def _filter_assets(assets: list[Asset], status_filter: str) -> list[Asset]:
    if status_filter == "all":
        return list(assets)
    return [asset for asset in assets if asset.status == status_filter]


class DashboardTab(ttk.Frame):
    def __init__(
        self,
        master: tk.Misc,
        member_count: int,
        findings: list[HousekeeperFinding],
        on_housekeeper: Callable[[], None],
    ):
        super().__init__(master, padding=24)
        self.member_count = member_count
        self.findings = findings
        self.on_housekeeper = on_housekeeper
        self._build_ui()

    def _build_ui(self) -> None:
        self.columnconfigure(0, weight=1)
        ttk.Label(self, text="SYSTEM OVERVIEW", style="TabTitle.TLabel").grid(row=0, column=0, sticky="w")
        ttk.Label(self, text="Mitgliederverwaltung · lokaler File-Store", style="Mono.TLabel").grid(
            row=1, column=0, sticky="w", pady=(3, 22)
        )
        cards = ttk.Frame(self)
        cards.grid(row=2, column=0, sticky="ew")
        counts = Counter(finding.severity for finding in self.findings)
        values = [
            ("MITGLIEDER", str(self.member_count), ""),
            ("ACTION REQUIRED", str(counts["error"]), "CardError.TLabel"),
            ("DUE SOON", str(counts["warning"] + counts["info"]), "CardWarning.TLabel"),
            ("DATA INTEGRITY", "OK", ""),
        ]
        for column, (label, value, style) in enumerate(values):
            card = ttk.Frame(cards, style="Card.TFrame", padding=18)
            card.grid(row=0, column=column, sticky="nsew", padx=(0, 10))
            cards.columnconfigure(column, weight=1)
            ttk.Label(card, text=label, style="CardTitle.TLabel").pack(anchor="w")
            ttk.Label(card, text=value, style=style or "CardValue.TLabel").pack(anchor="w", pady=(8, 0))
        ttk.Button(self, text="Hausmeister öffnen", style="Accent.TButton", command=self.on_housekeeper).grid(
            row=3, column=0, sticky="w", pady=(24, 0)
        )

    def update_data(self, member_count: int, findings: list[HousekeeperFinding]) -> None:
        self.member_count = member_count
        self.findings = findings
        for child in self.winfo_children():
            child.destroy()
        self._build_ui()


class SearchResultsTab(ttk.Frame):
    def __init__(
        self,
        master: tk.Misc,
        query: str,
        members: list[Member],
        on_open: Callable[[str], None],
        on_close: Callable[[], None],
    ):
        super().__init__(master, padding=12)
        self.query = query
        self.members = members
        self.on_open = on_open
        self.on_close = on_close
        self._build_ui()

    def _build_ui(self) -> None:
        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        header = ttk.Frame(self)
        header.grid(row=0, column=0, sticky="ew", pady=(0, 10))
        header.columnconfigure(0, weight=1)
        ttk.Label(header, text=f'Suche: "{self.query}"', style="TabTitle.TLabel").grid(
            row=0, column=0, sticky="w"
        )
        ttk.Label(header, text=f"{len(self.members)} Treffer", style="Mono.TLabel").grid(
            row=1, column=0, sticky="w"
        )
        ttk.Button(header, text="Tab schließen", command=self.on_close).grid(row=0, column=3, rowspan=2)
        tree = ttk.Treeview(
            self,
            columns=("number", "first_name", "last_name", "nickname", "email", "birth", "status"),
            show="headings",
        )
        for key, title, width in (
            ("number", "Nummer", 90),
            ("first_name", "Vorname", 150),
            ("last_name", "Nachname", 170),
            ("nickname", "Nickname", 150),
            ("email", "E-Mail-Adresse", 260),
            ("birth", "Geburtsdatum", 110),
            ("status", "Status", 160),
        ):
            tree.heading(key, text=title)
            tree.column(key, width=width, anchor="w")
        tree.grid(row=1, column=0, sticky="nsew")
        for member in self.members:
            tree.insert(
                "",
                "end",
                iid=member.member_id,
                values=(
                    member.member_number,
                    member.first_name,
                    member.last_name,
                    member.nickname,
                    member.email,
                    format_date_for_display(member.birth_date),
                    MEMBERSHIP_STATUS_LABELS.get(member.status, member.status),
                ),
            )
        tree.bind("<Double-1>", lambda _event: self._open_selected(tree))
        tree.bind("<Return>", lambda _event: self._open_selected(tree))

    def _open_selected(self, tree: ttk.Treeview) -> None:
        selected = tree.selection()
        if selected:
            self.on_open(selected[0])


class MembersTab(ttk.Frame):
    def __init__(
        self,
        master: tk.Misc,
        members: list[Member],
        findings: list[HousekeeperFinding],
        on_open: Callable[[str], None],
        on_close: Callable[[], None],
    ):
        super().__init__(master, padding=12)
        self.members = members
        self.findings = findings
        self.on_open = on_open
        self.on_close = on_close
        self._build_ui()

    def _build_ui(self) -> None:
        self.columnconfigure(0, weight=1)
        self.rowconfigure(2, weight=1)
        header = ttk.Frame(self)
        header.grid(row=0, column=0, sticky="ew", pady=(0, 10))
        header.columnconfigure(0, weight=1)
        ttk.Label(header, text="MITGLIEDER", style="TabTitle.TLabel").grid(row=0, column=0, sticky="w")
        self.count_var = tk.StringVar()
        ttk.Label(header, textvariable=self.count_var, style="Mono.TLabel").grid(row=1, column=0, sticky="w")
        ttk.Button(header, text="Tab schließen", command=self.on_close).grid(row=0, column=1, rowspan=2)
        filters = _filter_label_frame(self)
        filters.grid(row=1, column=0, sticky="ew", pady=(0, 10))
        filter_row = ttk.Frame(filters)
        filter_row.grid(row=0, column=0, sticky="w")
        self.tree = ttk.Treeview(
            self,
            columns=tuple(column for column, _title, _width in MEMBER_TABLE_COLUMNS),
            show="headings",
        )
        self.sort_column = "last_name"
        self.sort_descending = False
        self.status_filter_var = tk.StringVar(value=MEMBERSHIP_STATUS_LABELS["active"])
        ttk.Label(filter_row, text="Status").grid(row=0, column=0, sticky="w", padx=(0, 8))
        self.status_filter = ttk.Combobox(
            filter_row,
            textvariable=self.status_filter_var,
            state="readonly",
            values=[STATUS_FILTER_ALL, *MEMBERSHIP_STATUS_LABELS.values()],
            width=28,
        )
        self.status_filter.grid(row=0, column=1, sticky="w")
        self.status_filter.bind("<<ComboboxSelected>>", lambda _event: self._render_members())
        for key, title, width in MEMBER_TABLE_COLUMNS:
            self.tree.heading(key, text=title, command=lambda column=key: self._toggle_sort(column))
            self.tree.column(key, width=width, anchor="w")
        for severity, color in MEMBER_FINDING_COLORS.items():
            self.tree.tag_configure(f"finding-{severity}", background=color, foreground="#202020")
        self.tree.grid(row=2, column=0, sticky="nsew")
        self.tree.bind("<Double-1>", lambda _event: self._open_selected())
        self.tree.bind("<Return>", lambda _event: self._open_selected())
        self.refresh(self.members, self.findings)

    def refresh(self, members: list[Member], findings: list[HousekeeperFinding]) -> None:
        self.members = members
        self.findings = findings
        self._render_members()

    def _render_members(self) -> None:
        self.tree.delete(*self.tree.get_children())
        status_filter = _selected_status_filter(self.status_filter_var.get())
        filtered_members = _filter_members(self.members, status_filter)
        sorted_members = _sort_members(filtered_members, self.sort_column, self.sort_descending)
        finding_severities = _member_finding_severities(self.findings)
        if len(filtered_members) == len(self.members):
            self.count_var.set(f"{len(filtered_members)} Mitglieder")
        else:
            self.count_var.set(f"{len(filtered_members)} / {len(self.members)} Mitglieder")
        self._update_tree_headings()
        for member in sorted_members:
            severity = finding_severities.get(member.member_id)
            self.tree.insert(
                "",
                "end",
                iid=member.member_id,
                tags=(f"finding-{severity}",) if severity else (),
                values=(
                    member.member_number,
                    member.last_name,
                    member.first_name,
                    member.nickname,
                    member.email,
                    format_date_for_display(member.birth_date),
                    MEMBERSHIP_STATUS_LABELS.get(member.status, member.status),
                ),
            )

    def _toggle_sort(self, column: str) -> None:
        if self.sort_column == column:
            self.sort_descending = not self.sort_descending
        else:
            self.sort_column = column
            self.sort_descending = False
        self._render_members()

    def _update_tree_headings(self) -> None:
        for key, title, _width in MEMBER_TABLE_COLUMNS:
            suffix = ""
            if key == self.sort_column:
                suffix = " v" if self.sort_descending else " ^"
            self.tree.heading(
                key,
                text=f"{title}{suffix}",
                command=lambda column=key: self._toggle_sort(column),
            )

    def _open_selected(self) -> None:
        selected = self.tree.selection()
        if selected:
            self.on_open(selected[0])


class AssetsTab(ttk.Frame):
    def __init__(
        self,
        master: tk.Misc,
        assets: list[Asset],
        resolve_holder_label: Callable[[str], str],
        on_new: Callable[[], None],
        on_open: Callable[[str], None],
        on_edit: Callable[[str], None],
        on_issue: Callable[[str], None],
        on_return: Callable[[str], None],
        on_close: Callable[[], None],
    ):
        super().__init__(master, padding=12)
        self.assets = assets
        self.resolve_holder_label = resolve_holder_label
        self.on_new = on_new
        self.on_open = on_open
        self.on_edit = on_edit
        self.on_issue = on_issue
        self.on_return = on_return
        self.on_close = on_close
        self.sort_column = "label"
        self.sort_descending = False
        self.status_filter_var = tk.StringVar(value=ASSET_FILTER_ALL)
        self._build_ui()

    def _build_ui(self) -> None:
        self.columnconfigure(0, weight=1)
        self.rowconfigure(2, weight=1)
        header = ttk.Frame(self)
        header.grid(row=0, column=0, sticky="ew", pady=(0, 10))
        header.columnconfigure(0, weight=1)
        ttk.Label(header, text="INVENTAR", style="TabTitle.TLabel").grid(row=0, column=0, sticky="w")
        self.count_var = tk.StringVar()
        ttk.Label(header, textvariable=self.count_var, style="Mono.TLabel").grid(row=1, column=0, sticky="w")
        button_row = ttk.Frame(header)
        button_row.grid(row=0, column=1, rowspan=2, sticky="e")
        ttk.Button(button_row, text="Neues Asset", command=self.on_new).pack(side="left", padx=(0, 8))
        ttk.Button(button_row, text="Tab schließen", command=self.on_close).pack(side="left")
        filters = _filter_label_frame(self)
        filters.grid(row=1, column=0, sticky="ew", pady=(0, 10))
        filter_row = ttk.Frame(filters)
        filter_row.grid(row=0, column=0, sticky="w")
        ttk.Label(filter_row, text="Status").grid(row=0, column=0, sticky="w", padx=(0, 8))
        self.status_filter = ttk.Combobox(
            filter_row,
            textvariable=self.status_filter_var,
            state="readonly",
            values=[ASSET_FILTER_ALL, *ASSET_STATUS_LABELS.values()],
            width=22,
        )
        self.status_filter.grid(row=0, column=1, sticky="w")
        self.status_filter.bind("<<ComboboxSelected>>", lambda _event: self._render_assets())
        self.tree = ttk.Treeview(
            self,
            columns=tuple(key for key, _title, _width in ASSET_TABLE_COLUMNS),
            show="headings",
        )
        for key, title, width in ASSET_TABLE_COLUMNS:
            self.tree.heading(key, text=title, command=lambda column=key: self._toggle_sort(column))
            self.tree.column(key, width=width, anchor="w")
        self.tree.grid(row=2, column=0, sticky="nsew")
        self.tree.bind("<Double-1>", self._open_selected)
        self.tree.bind("<<TreeviewSelect>>", lambda _event: self._update_actions())
        actions = ttk.Frame(self)
        actions.grid(row=3, column=0, sticky="e", pady=(10, 0))
        self.edit_button = ttk.Button(
            actions,
            text="Bearbeiten",
            command=self._edit_selected,
            state="disabled",
        )
        self.edit_button.pack(side="left", padx=(0, 8))
        self.open_button = ttk.Button(actions, text="Öffnen", command=self._open_selected, state="disabled")
        self.open_button.pack(side="left", padx=(0, 8))
        self.issue_button = ttk.Button(
            actions,
            text="Ausgeben",
            command=self._issue_selected,
            state="disabled",
        )
        self.issue_button.pack(side="left", padx=(0, 8))
        self.return_button = ttk.Button(
            actions,
            text="Zurücknehmen",
            command=self._return_selected,
            state="disabled",
        )
        self.return_button.pack(side="left")
        self.refresh(self.assets)

    def refresh(self, assets: list[Asset]) -> None:
        self.assets = assets
        self._render_assets()

    def _render_assets(self) -> None:
        self.tree.delete(*self.tree.get_children())
        status_filter = _selected_asset_filter(self.status_filter_var.get())
        filtered_assets = _filter_assets(self.assets, status_filter)
        sorted_assets = sorted(
            filtered_assets,
            key=lambda asset: _asset_table_value(
                asset,
                self.sort_column,
                self.resolve_holder_label(asset.current_holder_member_id),
                self._owner_label(asset),
            ).casefold(),
            reverse=self.sort_descending,
        )
        if len(filtered_assets) == len(self.assets):
            self.count_var.set(f"{len(filtered_assets)} Assets")
        else:
            self.count_var.set(f"{len(filtered_assets)} / {len(self.assets)} Assets")
        self._update_tree_headings()
        for asset in sorted_assets:
            self.tree.insert(
                "",
                "end",
                iid=asset.asset_id,
                values=(
                    asset.label,
                    asset.category,
                    asset.inventory_number,
                    ASSET_STATUS_LABELS.get(asset.status, asset.status),
                    self._owner_label(asset),
                    self.resolve_holder_label(asset.current_holder_member_id),
                    asset.location,
                ),
            )
        self._update_actions()

    def _owner_label(self, asset: Asset) -> str:
        if asset.owner_type == "club":
            return "Verein"
        if asset.owner_type == "member":
            return self.resolve_holder_label(asset.owner_member_id)
        return asset.owner_name or "Extern"

    def _toggle_sort(self, column: str) -> None:
        if self.sort_column == column:
            self.sort_descending = not self.sort_descending
        else:
            self.sort_column = column
            self.sort_descending = False
        self._render_assets()

    def _update_tree_headings(self) -> None:
        for key, title, _width in ASSET_TABLE_COLUMNS:
            suffix = ""
            if key == self.sort_column:
                suffix = " v" if self.sort_descending else " ^"
            self.tree.heading(
                key,
                text=f"{title}{suffix}",
                command=lambda column=key: self._toggle_sort(column),
            )

    def _selected_asset_id(self) -> str:
        selected = self.tree.selection()
        return selected[0] if selected else ""

    def _selected_asset(self) -> Asset | None:
        selected_asset_id = self._selected_asset_id()
        return next((asset for asset in self.assets if asset.asset_id == selected_asset_id), None)

    def _update_actions(self) -> None:
        asset = self._selected_asset()
        if asset is None:
            self.edit_button.configure(state="disabled")
            self.open_button.configure(state="disabled")
            self.issue_button.configure(state="disabled")
            self.return_button.configure(state="disabled")
            return
        self.edit_button.configure(state="normal")
        self.open_button.configure(state="normal")
        self.issue_button.configure(state="normal" if asset.status == "available" else "disabled")
        self.return_button.configure(state="normal" if asset.current_holder_member_id else "disabled")

    def _open_selected(self, _event: tk.Event | None = None) -> None:
        asset_id = self._selected_asset_id()
        if not asset_id:
            asset_id = self.tree.focus()
        if asset_id:
            self.on_open(asset_id)

    def _edit_selected(self, _event: tk.Event | None = None) -> None:
        asset_id = self._selected_asset_id()
        if not asset_id:
            asset_id = self.tree.focus()
        if asset_id:
            self.on_edit(asset_id)

    def _issue_selected(self, _event: tk.Event | None = None) -> None:
        asset_id = self._selected_asset_id()
        if not asset_id:
            asset_id = self.tree.focus()
        if asset_id:
            self.on_issue(asset_id)

    def _return_selected(self) -> None:
        asset_id = self._selected_asset_id()
        if asset_id:
            self.on_return(asset_id)


class HousekeeperTab(ttk.Frame):
    def __init__(
        self,
        master: tk.Misc,
        findings: list[HousekeeperFinding],
        on_open_target: Callable[[HousekeeperFinding], None],
        on_refresh: Callable[[], list[HousekeeperFinding]],
        on_delete: Callable[[str], list[HousekeeperFinding]],
        on_close: Callable[[], None],
    ):
        super().__init__(master, padding=12)
        self.findings = findings
        self.on_open_target = on_open_target
        self.on_refresh = on_refresh
        self.on_delete = on_delete
        self.on_close = on_close
        self._build_ui()

    def _build_ui(self) -> None:
        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        header = ttk.Frame(self)
        header.grid(row=0, column=0, sticky="ew", pady=(0, 10))
        header.columnconfigure(0, weight=1)
        self.title_var = tk.StringVar()
        ttk.Label(header, textvariable=self.title_var, style="TabTitle.TLabel").grid(
            row=0, column=0, sticky="w"
        )
        ttk.Label(
            header,
            text="Regeln prüfen Daten und führen idempotente Aktionen aus",
            style="Mono.TLabel",
        ).grid(row=1, column=0, sticky="w")
        ttk.Button(header, text="Neu prüfen", command=self.refresh).grid(
            row=0, column=1, rowspan=2, padx=(0, 8)
        )
        ttk.Button(header, text="Tab schließen", command=self.on_close).grid(row=0, column=2, rowspan=2)
        self.tree = ttk.Treeview(self, columns=("severity", "due", "title", "detail"), show="headings")
        for key, title, width in (
            ("severity", "Level", 90),
            ("due", "Fällig", 100),
            ("title", "Vorgang", 330),
            ("detail", "Details", 390),
        ):
            self.tree.heading(key, text=title)
            self.tree.column(key, width=width, anchor="w", stretch=key == "detail")
        self.tree.grid(row=1, column=0, sticky="nsew")
        self.tree.bind("<Double-1>", lambda _event: self._open_selected())
        self.tree.bind("<<TreeviewSelect>>", lambda _event: self._show_selected_details())
        details = ttk.LabelFrame(self, text="Details", padding=12)
        details.grid(row=2, column=0, sticky="ew", pady=(10, 0))
        details.columnconfigure(0, weight=1)
        self.detail_var = tk.StringVar(value="Eintrag auswählen, um Details anzuzeigen.")
        self.detail_label = ttk.Label(
            details,
            textvariable=self.detail_var,
            anchor="nw",
            justify="left",
            wraplength=800,
            style="Status.TLabel",
        )
        self.detail_label.grid(row=0, column=0, sticky="ew")
        self.delete_button = ttk.Button(
            details,
            text="Task löschen",
            command=self._delete_selected,
            state="disabled",
        )
        self.delete_button.grid(row=0, column=1, sticky="ne", padx=(12, 0))
        details.bind(
            "<Configure>",
            lambda event: self.detail_label.configure(wraplength=max(300, event.width - 32)),
        )
        self._render()

    def refresh(self) -> None:
        self.findings = self.on_refresh()
        self._render()

    def _render(self) -> None:
        self.tree.delete(*self.tree.get_children())
        self.detail_var.set("Eintrag auswählen, um Details anzuzeigen.")
        self.delete_button.configure(state="disabled")
        self.title_var.set(f"HAUSMEISTER · {len(self.findings)} Vorgänge")
        for index, finding in enumerate(self.findings):
            self.tree.insert(
                "",
                "end",
                iid=str(index),
                values=(finding.severity.upper(), finding.due_date or "", finding.title, finding.detail),
            )

    def _show_selected_details(self) -> None:
        selected = self.tree.selection()
        if not selected:
            self.detail_var.set("Eintrag auswählen, um Details anzuzeigen.")
            self.delete_button.configure(state="disabled")
            return
        index = int(selected[0])
        if index >= len(self.findings):
            return
        self.detail_var.set(_finding_details(self.findings[index]))
        self.delete_button.configure(state="normal" if self.findings[index].key else "disabled")

    def _delete_selected(self) -> None:
        selected = self.tree.selection()
        if not selected:
            return
        finding = self.findings[int(selected[0])]
        if not finding.key or not messagebox.askyesno(
            "Hausmeister-Task löschen",
            f"Task wirklich löschen?\n\n{finding.title}\n\n"
            "Falls die Ursache weiter besteht, wird er beim nächsten Lauf neu angelegt.",
            parent=self,
        ):
            return
        self.findings = self.on_delete(finding.key)
        self._render()

    def _open_selected(self) -> None:
        selected = self.tree.selection()
        if selected:
            self.on_open_target(self.findings[int(selected[0])])


def _finding_details(finding: HousekeeperFinding) -> str:
    lines = [f"{finding.severity.upper()} · {finding.code}", finding.title]
    if finding.target_type == "asset" and finding.asset_id:
        lines.append(f"Asset: {finding.asset_id}")
    elif finding.member_id:
        lines.append(f"Mitglied: {finding.member_id}")
    if finding.key:
        lines.append(f"Key: {finding.key}")
    if finding.due_date:
        lines.append(f"Fällig: {format_date_for_display(finding.due_date.isoformat())}")
    if finding.detail:
        lines.extend(("", finding.detail))
    return "\n".join(lines)


def _selected_asset_filter(label: str) -> str:
    if label == ASSET_FILTER_ALL:
        return "all"
    return storage_key(ASSET_STATUS_LABELS, label)
