from __future__ import annotations

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

from ccma.domain.contributions import CONTRIBUTION_OVERRIDE_KIND_LABELS
from ccma.domain.dates import format_month_for_display, month_input_hint
from ccma.storage.repository import MemberRepository, RepositoryError
from ccma.ui.labels import storage_key


class _Dialog(tk.Toplevel):
    def __init__(self, master: tk.Misc, title: str, on_saved: Callable[[], None]):
        super().__init__(master)
        self.on_saved = on_saved
        self.title(title)
        self.transient(master.winfo_toplevel())
        self.resizable(False, False)
        self.frame = ttk.Frame(self, padding=18)
        self.frame.pack(fill="both", expand=True)
        self.bind("<Escape>", lambda _event: self.destroy())
        self.after_idle(self.grab_set)

    def _buttons(self, row: int, command: Callable[[], None]) -> None:
        buttons = ttk.Frame(self.frame)
        buttons.grid(row=row, column=0, columnspan=2, sticky="e", pady=(16, 0))
        ttk.Button(buttons, text="Abbrechen", command=self.destroy).pack(side="left", padx=(0, 8))
        ttk.Button(buttons, text="Speichern", style="Accent.TButton", command=command).pack(side="left")


class ContributionOverrideEditDialog(_Dialog):
    """Records an individually agreed membership fee for a member -- e.g. a reduced
    student rate -- for a month-granular date range, either as a fixed annual amount
    or as a percentage discount off whichever base rate is in effect at the time."""

    def __init__(
        self,
        master: tk.Misc,
        repository: MemberRepository,
        member_id: str,
        on_saved: Callable[[], None],
        override: dict | None = None,
    ):
        super().__init__(
            master,
            "Abweichenden Beitrag bearbeiten" if override else "Abweichenden Beitrag anlegen",
            on_saved,
        )
        self.repository = repository
        self.member_id = member_id
        self.override_id = str(override.get("override_id")) if override else None
        kind_label = CONTRIBUTION_OVERRIDE_KIND_LABELS.get(
            str(override.get("kind", "")) if override else "amount",
            CONTRIBUTION_OVERRIDE_KIND_LABELS["amount"],
        )
        self.variables = {
            "valid_from": tk.StringVar(
                value=format_month_for_display(str(override.get("valid_from", ""))) if override else ""
            ),
            "valid_until": tk.StringVar(
                value=format_month_for_display(str(override.get("valid_until", ""))) if override else ""
            ),
            "kind": tk.StringVar(value=kind_label),
            "value": tk.StringVar(value=str(override.get("value", "")) if override else ""),
        }

        self.frame.columnconfigure(1, weight=1)
        ttk.Label(self.frame, text=f"Ab ({month_input_hint()})").grid(
            row=0, column=0, sticky="w", pady=5, padx=(0, 12)
        )
        ttk.Entry(self.frame, textvariable=self.variables["valid_from"], width=42).grid(
            row=0, column=1, sticky="ew", pady=5
        )
        ttk.Label(self.frame, text=f"Bis ({month_input_hint()}, leer = unbefristet)").grid(
            row=1, column=0, sticky="w", pady=5, padx=(0, 12)
        )
        ttk.Entry(self.frame, textvariable=self.variables["valid_until"], width=42).grid(
            row=1, column=1, sticky="ew", pady=5
        )
        ttk.Label(self.frame, text="Art").grid(row=2, column=0, sticky="w", pady=5, padx=(0, 12))
        kind_combo = ttk.Combobox(
            self.frame,
            textvariable=self.variables["kind"],
            values=list(CONTRIBUTION_OVERRIDE_KIND_LABELS.values()),
            state="readonly",
            width=39,
        )
        kind_combo.grid(row=2, column=1, sticky="ew", pady=5)
        kind_combo.bind("<<ComboboxSelected>>", lambda _event: self._update_value_label())
        self.value_label_var = tk.StringVar()
        ttk.Label(self.frame, textvariable=self.value_label_var).grid(
            row=3, column=0, sticky="w", pady=5, padx=(0, 12)
        )
        ttk.Entry(self.frame, textvariable=self.variables["value"], width=42).grid(
            row=3, column=1, sticky="ew", pady=5
        )
        ttk.Label(self.frame, text="Begründung").grid(row=4, column=0, sticky="nw", pady=5, padx=(0, 12))
        self.reason_text = tk.Text(self.frame, width=42, height=4, wrap="word")
        self.reason_text.grid(row=4, column=1, sticky="ew", pady=5)
        if override:
            self.reason_text.insert("1.0", str(override.get("reason", "")))
        self._update_value_label()
        self._buttons(5, self._save)

    def _update_value_label(self) -> None:
        kind = storage_key(CONTRIBUTION_OVERRIDE_KIND_LABELS, self.variables["kind"].get())
        self.value_label_var.set("Jahresbetrag (EUR)" if kind == "amount" else "Ermäßigung (%)")

    def _save(self) -> None:
        kind = storage_key(CONTRIBUTION_OVERRIDE_KIND_LABELS, self.variables["kind"].get())
        reason = self.reason_text.get("1.0", "end-1c")
        try:
            if self.override_id:
                self.repository.update_contribution_override(
                    self.member_id,
                    self.override_id,
                    valid_from=self.variables["valid_from"].get(),
                    valid_until=self.variables["valid_until"].get(),
                    kind=kind,
                    value=self.variables["value"].get(),
                    reason=reason,
                )
            else:
                self.repository.record_contribution_override(
                    self.member_id,
                    valid_from=self.variables["valid_from"].get(),
                    valid_until=self.variables["valid_until"].get(),
                    kind=kind,
                    value=self.variables["value"].get(),
                    reason=reason,
                )
        except RepositoryError as exc:
            messagebox.showerror(
                "Abweichender Beitrag konnte nicht gespeichert werden", str(exc), parent=self
            )
            return
        self.destroy()
        self.on_saved()
