from tkinter import *
from tkinter import ttk, messagebox
import database
import circulation
import books
import incidents
import email_service
import notifications
import ui_utils
from datetime import datetime, timedelta


def open_admin_dashboard(user_id):

    with database.connect() as conn:
        cursor = conn.cursor()
        cursor.execute("SELECT username FROM users WHERE id=?", (user_id,))
        username = cursor.fetchone()[0]

    root = Tk()
    root.title("Library Admin Dashboard")
    ui_utils.maximize_window(root)
    ui_utils.setup_app_style(root)

    sidebar = ui_utils.make_sidebar(root, "ADMIN PANEL")
    main_area = Frame(root, bg=ui_utils.BG_COLOR)
    main_area.pack(side=RIGHT, expand=True, fill=BOTH)

    def show_welcome_page():
        clear_main()
        database.setup_database()
        stats = circulation.get_library_stats()
        unreturned = circulation.get_unreturned_books()
        overdue_fines = circulation.get_all_fines()
        open_reports = incidents.get_open_incidents()

        ui_utils.make_page_header(
            page_area, f"Welcome back, {username}",
            "Live library counts — click ↻ Refresh to update",
        )

        stats_row = Frame(page_area, bg=ui_utils.BG_COLOR)
        stats_row.pack(fill=X, padx=28, pady=10)

        stat_items = [
            ("Available Books", stats["available_copies"], "#0ea5e9"),
            ("Borrowed Books", stats["borrowed_copies"], "#d97706"),
            ("Pending Requests", stats["pending_requests"], "#16a34a"),
            ("Overdue Books", stats["overdue_loans"], "#dc2626"),
        ]
        for title, value, color in stat_items:
            ui_utils.make_stat_card(stats_row, title, value, color).pack(side=LEFT, padx=8, pady=4)

        summary = Frame(page_area, bg=ui_utils.BG_COLOR)
        summary.pack(fill=X, padx=28, pady=(4, 8))
        ui_utils.make_label(
            summary,
            f"Catalog: {stats['total_titles']} titles  •  {stats['total_copies']} total copies  •  "
            f"{stats['total_students']} students  •  {len(overdue_fines)} unpaid overdue fine(s)  •  "
            f"{len(open_reports)} lost/damage report(s)",
            variant="small", bg=ui_utils.BG_COLOR,
        ).pack(anchor="w")

        alerts = Frame(page_area, bg=ui_utils.BG_COLOR)
        alerts.pack(fill=X, padx=28, pady=(0, 8))
        if stats["pending_requests"] > 0:
            ui_utils.make_label(
                alerts,
                f"→ {stats['pending_requests']} pending borrow request(s) — open Approve Requests",
                variant="small", bg=ui_utils.BG_COLOR, fg=ui_utils.WARNING,
            ).pack(anchor="w")
        if stats["overdue_loans"] > 0:
            ui_utils.make_label(
                alerts,
                f"→ {stats['overdue_loans']} overdue book(s) — open Fines to mark payments",
                variant="small", bg=ui_utils.BG_COLOR, fg=ui_utils.DANGER,
            ).pack(anchor="w")
        if open_reports:
            ui_utils.make_label(
                alerts,
                f"→ {len(open_reports)} student report(s) — open Lost & Damaged to review",
                variant="small", bg=ui_utils.BG_COLOR, fg=ui_utils.DANGER,
            ).pack(anchor="w")

        ui_utils.make_page_header(
            page_area, "Books Not Yet Returned",
            f"{len(unreturned)} active loan(s)",
        )

        if not unreturned:
            card = ui_utils.make_card(page_area)
            ui_utils.make_label(
                card, "All borrowed books have been returned.",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w", pady=8)
        else:
            columns = ("Student", "Student #", "Book", "Author", "Borrowed", "Due Date", "Status")
            tree = ui_utils.make_treeview(page_area, columns, height=min(len(unreturned), 10))
            for row in unreturned:
                uname, student_num, email, title, author, borrowed, due_date, loan_id, loan_status = row
                tree.insert("", "end", iid=str(loan_id), values=(
                    uname, student_num or "-", title, author,
                    borrowed or "-", due_date or "-", loan_status,
                ))

    def show_add_book():
        clear_main()
        ui_utils.make_page_header(page_area, "Add New Book", "Register a new title in the catalog")

        card = ui_utils.make_card(page_area)
        form = Frame(card, bg=ui_utils.CARD_COLOR)
        form.pack()

        title_entry = ui_utils.make_form_field(form, "Book Title")
        author_entry = ui_utils.make_form_field(form, "Author")
        shelf_entry = ui_utils.make_form_field(form, "Shelf Number")
        description_entry = ui_utils.make_form_field(form, "Description")
        quantity_entry = ui_utils.make_form_field(form, "Quantity")
        quantity_entry.insert(0, "1")

        message = ui_utils.make_label(card, "", variant="small", bg=ui_utils.CARD_COLOR)
        message.pack(pady=8)

        def add_book_action():
            qty_text = quantity_entry.get().strip() or "1"
            ok, text, _book_id = books.add_book(
                title_entry.get(),
                author_entry.get(),
                shelf_entry.get(),
                description_entry.get(),
                qty_text,
            )
            message.config(text=text, fg=ui_utils.SUCCESS if ok else ui_utils.DANGER)
            if ok:
                for entry in (title_entry, author_entry, shelf_entry, description_entry, quantity_entry):
                    entry.delete(0, END)
                quantity_entry.insert(0, "1")

        ttk.Button(card, text="Add Book", style="Primary.TButton", command=add_book_action).pack(pady=8)
        ttk.Button(
            card, text="View Catalog Now", style="Success.TButton",
            command=lambda: open_page(show_view_books),
        ).pack(pady=4)

    def show_requests():
        clear_main()
        pending = circulation.get_pending_requests()
        borrowed = circulation.get_active_borrowed_loans()

        ui_utils.make_page_header(
            page_area, "Approve Book Requests",
            f"{len(pending)} pending request(s) • {len(borrowed)} approved book(s) currently out",
        )

        if not pending and not borrowed:
            card = ui_utils.make_card(page_area)
            ui_utils.make_label(
                card,
                "No borrow requests or active loans right now.",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w", pady=(0, 8))
            ui_utils.make_label(
                card,
                "A request only appears here after a student:\n"
                "1. Signs in as student\n"
                "2. Opens Search Books\n"
                "3. Clicks a book → Request Borrow\n"
                "4. Sees the green \"Request Sent\" popup\n\n"
                "Place Hold does NOT appear here — that goes to My Holds only.\n"
                "After a student borrows, click ↻ Refresh on this page.",
                variant="small", bg=ui_utils.CARD_COLOR, justify=LEFT,
            ).pack(anchor="w")
            return

        body = Frame(page_area, bg=ui_utils.BG_COLOR)
        body.pack(fill=BOTH, expand=True, padx=28, pady=8)
        body.grid_columnconfigure(0, weight=1)

        default_due = (datetime.now() + timedelta(days=14)).strftime("%Y-%m-%d")
        current_row = 0

        if pending:
            Label(
                body,
                text=f"Pending Requests ({len(pending)}) — click a row, then Approve",
                font=ui_utils.FONT_NORMAL,
                fg=ui_utils.TEXT_PRIMARY,
                bg="#fffbeb",
                padx=12,
                pady=8,
                anchor="w",
            ).grid(row=current_row, column=0, sticky="ew", pady=(0, 8))
            current_row += 1

            pending_frame = Frame(
                body, bg=ui_utils.CARD_COLOR,
                highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
            )
            pending_frame.grid(row=current_row, column=0, sticky="ew", pady=(0, 8))
            current_row += 1

            pending_columns = ("Book", "Author", "Student", "Student #", "Status")
            pending_tree = ttk.Treeview(
                pending_frame, columns=pending_columns, show="headings",
                style="Treeview", height=min(len(pending), 8) or 3,
            )
            pending_scroll = ttk.Scrollbar(pending_frame, orient="vertical", command=pending_tree.yview)
            pending_tree.configure(yscrollcommand=pending_scroll.set)
            for col in pending_columns:
                pending_tree.heading(col, text=col)
                pending_tree.column(col, minwidth=90, width=130, anchor="center")
            pending_tree.pack(side=LEFT, fill=BOTH, expand=True)
            pending_scroll.pack(side=RIGHT, fill=Y)

            pending_data = {}
            for req in pending:
                loan_id, title, author, status, _book_id, username, _borrow_date, student_num = req
                pending_tree.insert("", "end", iid=str(loan_id), values=(
                    title, author, username, student_num or "-", "Awaiting approval",
                ))
                pending_data[str(loan_id)] = req

            pending_panel = Frame(
                body, bg=ui_utils.CARD_COLOR,
                highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                padx=20, pady=16,
            )
            pending_panel.grid(row=current_row, column=0, sticky="ew", pady=(0, 16))
            current_row += 1

            pending_title = ui_utils.make_label(
                pending_panel, "Select a pending request above to approve",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            )
            pending_title.pack(anchor="w", pady=(0, 8))
            pending_body = Frame(pending_panel, bg=ui_utils.CARD_COLOR)
            pending_body.pack(fill=X)

            def on_pending_select(_event=None):
                selected = pending_tree.selection()
                if not selected:
                    return
                loan_id = selected[0]
                req = pending_data.get(loan_id)
                if not req:
                    return

                title, author, username = req[1], req[2], req[5]
                pending_title.config(text=f"{title} by {author} — Student: {username}")

                for widget in pending_body.winfo_children():
                    widget.destroy()

                ui_utils.make_label(
                    pending_body,
                    "Due date (YYYY-MM-DD):",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(side=LEFT, padx=(0, 8))
                entry = ui_utils.make_entry(pending_body, width=14)
                entry.pack(side=LEFT, padx=4)
                entry.insert(0, default_due)

                def approve_selected(lid=int(loan_id), due_entry=entry):
                    due_date = due_entry.get().strip()
                    if not due_date:
                        messagebox.showerror("Missing Date", "Please enter a due date (YYYY-MM-DD).")
                        return
                    ok, msg = circulation.approve_request(lid, due_date)
                    if ok:
                        messagebox.showinfo("Approved", f"Book approved.\nDue date: {due_date}")
                        show_requests()
                    else:
                        messagebox.showerror("Error", msg)

                ttk.Button(
                    pending_body, text="Approve Borrow Request",
                    style="Success.TButton", command=approve_selected,
                ).pack(side=LEFT, padx=12)

            pending_tree.bind("<<TreeviewSelect>>", on_pending_select)
            pending_tree.bind("<Double-1>", on_pending_select)

            for item in pending_tree.get_children():
                pending_tree.selection_set(item)
                pending_tree.focus(item)
                on_pending_select()
                break

        if borrowed:
            Label(
                body,
                text=(
                    f"Currently Borrowed Books ({len(borrowed)}) — "
                    "mark returned when the student brings the book back (any time, even before due date)"
                ),
                font=ui_utils.FONT_NORMAL,
                fg=ui_utils.TEXT_PRIMARY,
                bg="#ecfdf5",
                padx=12,
                pady=8,
                anchor="w",
            ).grid(row=current_row, column=0, sticky="ew", pady=(8, 8))
            current_row += 1

            borrowed_frame = Frame(
                body, bg=ui_utils.CARD_COLOR,
                highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
            )
            borrowed_frame.grid(row=current_row, column=0, sticky="ew", pady=(0, 8))
            current_row += 1

            borrowed_columns = ("Book", "Author", "Student", "Student #", "Borrowed", "Due Date", "Status")
            borrowed_tree = ttk.Treeview(
                borrowed_frame, columns=borrowed_columns, show="headings",
                style="Treeview", height=min(len(borrowed), 10) or 4,
            )
            borrowed_scroll = ttk.Scrollbar(borrowed_frame, orient="vertical", command=borrowed_tree.yview)
            borrowed_tree.configure(yscrollcommand=borrowed_scroll.set)
            for col in borrowed_columns:
                borrowed_tree.heading(col, text=col)
                borrowed_tree.column(col, minwidth=90, width=120, anchor="center")
            borrowed_tree.pack(side=LEFT, fill=BOTH, expand=True)
            borrowed_scroll.pack(side=RIGHT, fill=Y)

            borrowed_data = {}
            for row in borrowed:
                loan_id, title, author, username, student_num, borrow_date, due_date, loan_status = row
                borrowed_tree.insert("", "end", iid=str(loan_id), values=(
                    title, author, username, student_num or "-",
                    borrow_date or "-", due_date or "-", loan_status,
                ))
                borrowed_data[str(loan_id)] = row

            borrowed_panel = Frame(
                body, bg=ui_utils.CARD_COLOR,
                highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                padx=20, pady=16,
            )
            borrowed_panel.grid(row=current_row, column=0, sticky="ew", pady=(0, 8))

            borrowed_title = ui_utils.make_label(
                borrowed_panel,
                "Select a borrowed book above, then click Mark Returned",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            )
            borrowed_title.pack(anchor="w", pady=(0, 8))
            borrowed_body = Frame(borrowed_panel, bg=ui_utils.CARD_COLOR)
            borrowed_body.pack(fill=X)

            def on_borrowed_select(_event=None):
                selected = borrowed_tree.selection()
                if not selected:
                    return
                loan_id = selected[0]
                row = borrowed_data.get(loan_id)
                if not row:
                    return

                title, author, username, _student_num, borrow_date, due_date, loan_status = row[1:]
                days_late, fine_amount = circulation.calculate_fine(due_date)
                borrowed_title.config(
                    text=f"{title} by {author} — {username} — due {due_date or 'not set'} ({loan_status})",
                )

                for widget in borrowed_body.winfo_children():
                    widget.destroy()

                detail_lines = [
                    f"Borrowed: {borrow_date or '-'}  •  Due: {due_date or '-'}  •  "
                    "Returning early or on time both add 1 copy back to Available.",
                ]
                if loan_status == "Overdue" and fine_amount > 0:
                    detail_lines.append(
                        f"Overdue fine: ₱{fine_amount:.0f} ({days_late} day(s) late) — "
                        "student must pay, then mark fine paid in Admin → Fines."
                    )
                ui_utils.make_label(
                    borrowed_body,
                    "\n".join(detail_lines),
                    variant="small", bg=ui_utils.CARD_COLOR, justify=LEFT,
                ).pack(side=LEFT, padx=(0, 8))

                def return_selected(lid=int(loan_id)):
                    ok, msg = circulation.return_book(lid)
                    if ok:
                        messagebox.showinfo("Returned", msg)
                        show_requests()
                    else:
                        messagebox.showerror("Error", msg)

                ttk.Button(
                    borrowed_body, text="Mark Returned",
                    style="Warning.TButton", command=return_selected,
                ).pack(side=LEFT, padx=12)

            borrowed_tree.bind("<<TreeviewSelect>>", on_borrowed_select)
            borrowed_tree.bind("<Double-1>", on_borrowed_select)

            if borrowed_tree.get_children():
                borrowed_tree.selection_set(borrowed_tree.get_children()[0])
                borrowed_tree.focus(borrowed_tree.get_children()[0])
                on_borrowed_select()

        page_area.update_idletasks()

    def show_view_books():
        clear_main()
        ui_utils.make_page_header(
            page_area, "Book Catalog",
            f"{books.count_books()} book(s) in database — select a row to edit or delete",
        )

        body = Frame(page_area, bg=ui_utils.BG_COLOR)
        body.pack(fill=BOTH, expand=True, padx=28, pady=8)
        body.grid_columnconfigure(0, weight=1)
        body.grid_rowconfigure(0, weight=0)
        body.grid_rowconfigure(1, weight=1)
        body.grid_rowconfigure(2, weight=0)

        search_row = Frame(body, bg=ui_utils.BG_COLOR)
        search_row.grid(row=0, column=0, sticky="ew", pady=(0, 8))
        ui_utils.make_label(search_row, "Filter:", variant="small", bg=ui_utils.BG_COLOR).pack(side=LEFT, padx=(0, 8))
        filter_entry = ui_utils.make_entry(search_row, width=40)
        filter_entry.pack(side=LEFT)

        tree_frame = Frame(
            body, bg=ui_utils.CARD_COLOR,
            highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
        )
        tree_frame.grid(row=1, column=0, sticky="nsew")

        columns = ("Title", "Author", "Shelf", "Available", "Total")
        tree = ttk.Treeview(tree_frame, columns=columns, show="headings", style="Treeview", height=12)
        scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=tree.yview)
        tree.configure(yscrollcommand=scrollbar.set)
        for col in columns:
            tree.heading(col, text=col)
            tree.column(col, minwidth=100, width=140, anchor="center")
        tree.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbar.pack(side=RIGHT, fill=Y)

        action_panel = Frame(
            body, bg=ui_utils.CARD_COLOR,
            highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
            padx=16, pady=12,
        )
        action_panel.grid(row=2, column=0, sticky="ew", pady=(12, 0))

        action_label = ui_utils.make_label(
            action_panel, "Select a book above to edit or delete",
            variant="small", bg=ui_utils.CARD_COLOR,
        )
        action_label.pack(anchor="w", pady=(0, 8))

        action_buttons = Frame(action_panel, bg=ui_utils.CARD_COLOR)
        action_buttons.pack(anchor="w")

        def load_catalog(term=""):
            for item in tree.get_children():
                tree.delete(item)
            catalog = books.get_all_books(term)
            for book in catalog:
                tree.insert("", "end", iid=str(book[0]), values=(
                    book[1], book[2], book[3] or "-",
                    book[6], book[5],
                ))
            if not catalog:
                action_label.config(text="No books match your filter." if term else "No books in the catalog yet.")

        def get_selected_book():
            selected = tree.selection()
            if not selected:
                messagebox.showinfo("Select Book", "Click a book row first.")
                return None
            book_data = books.get_book_by_id(int(selected[0]))
            if not book_data:
                messagebox.showerror("Error", "Book not found. Refreshing catalog.")
                load_catalog(filter_entry.get())
                return None
            return book_data

        def open_book_editor(book_data):
            popup = Toplevel(root)
            popup.title(book_data[1])
            popup.geometry("480x560")
            popup.configure(bg=ui_utils.CARD_COLOR)
            popup.resizable(False, False)
            popup.transient(root)
            popup.grab_set()

            ui_utils.make_label(
                popup, "Edit Book Details", variant="title", bg=ui_utils.CARD_COLOR,
            ).pack(pady=(20, 12), padx=24, anchor="w")

            form = Frame(popup, bg=ui_utils.CARD_COLOR)
            form.pack(fill=X, padx=24)

            title_e = ui_utils.make_form_field(form, "Title")
            title_e.insert(0, book_data[1])
            author_e = ui_utils.make_form_field(form, "Author")
            author_e.insert(0, book_data[2])
            shelf_e = ui_utils.make_form_field(form, "Shelf")
            shelf_e.insert(0, book_data[3] or "")
            desc_e = ui_utils.make_form_field(form, "Description")
            desc_e.insert(0, book_data[4] or "")
            qty_e = ui_utils.make_form_field(form, "Total Quantity")
            qty_e.insert(0, str(book_data[5]))

            borrowed = book_data[5] - book_data[6]
            ui_utils.make_label(
                popup, f"Currently borrowed: {borrowed}  •  Available: {book_data[6]}",
                variant="small", bg=ui_utils.CARD_COLOR,
            ).pack(padx=24, anchor="w", pady=(4, 0))

            msg = ui_utils.make_label(popup, "", variant="small", bg=ui_utils.CARD_COLOR)
            msg.pack(padx=24, anchor="w", pady=8)

            btn_row = Frame(popup, bg=ui_utils.CARD_COLOR)
            btn_row.pack(fill=X, padx=24, pady=20)

            def save_book():
                ok, text = circulation.update_book(
                    book_data[0],
                    title_e.get(), author_e.get(), shelf_e.get(),
                    desc_e.get(), qty_e.get(),
                )
                msg.config(text=text, fg=ui_utils.SUCCESS if ok else ui_utils.DANGER)
                if ok:
                    load_catalog(filter_entry.get())
                    action_label.config(text=f"Saved changes to '{title_e.get().strip()}'")
                    popup.after(500, popup.destroy)

            def delete_from_editor():
                if not messagebox.askyesno(
                    "Confirm Delete",
                    f"Delete '{book_data[1]}' from the catalog?\n\nThis cannot be undone.",
                    parent=popup,
                ):
                    return
                ok, text = books.delete_book(book_data[0])
                if ok:
                    popup.destroy()
                    load_catalog(filter_entry.get())
                    action_label.config(text=text)
                    messagebox.showinfo("Deleted", text)
                else:
                    msg.config(text=text, fg=ui_utils.DANGER)

            ttk.Button(btn_row, text="Save Changes", style="Primary.TButton", command=save_book).pack(side=LEFT)
            ttk.Button(btn_row, text="Delete Book", style="Danger.TButton", command=delete_from_editor).pack(side=RIGHT)

        def edit_selected():
            book_data = get_selected_book()
            if book_data:
                open_book_editor(book_data)

        def delete_selected():
            book_data = get_selected_book()
            if not book_data:
                return
            if not messagebox.askyesno(
                "Confirm Delete",
                f"Delete '{book_data[1]}' from the catalog?\n\nThis cannot be undone.",
            ):
                return
            ok, text = books.delete_book(book_data[0])
            if ok:
                load_catalog(filter_entry.get())
                action_label.config(text=text)
                messagebox.showinfo("Deleted", text)
            else:
                messagebox.showerror("Cannot Delete", text)

        def on_select(_event=None):
            selected = tree.selection()
            if not selected:
                return
            values = tree.item(selected[0], "values")
            if values:
                action_label.config(text=f"Selected: {values[0]} by {values[1]}")

        ttk.Button(
            action_buttons, text="Edit Selected Book",
            style="Primary.TButton", command=edit_selected,
        ).pack(side=LEFT, padx=(0, 8))
        ttk.Button(
            action_buttons, text="Delete Selected Book",
            style="Danger.TButton", command=delete_selected,
        ).pack(side=LEFT)

        filter_entry.bind("<KeyRelease>", lambda e: load_catalog(filter_entry.get()))
        tree.bind("<<TreeviewSelect>>", on_select)
        tree.bind("<Double-1>", lambda e: edit_selected())
        load_catalog()

    def show_reports():
        clear_main()
        ui_utils.make_page_header(
            page_area, "Library Reports",
            "Overdue students, popular books, and low stock",
        )

        card = ui_utils.make_card(page_area)

        ui_utils.make_label(card, "Overdue Students", variant="subtitle", bg=ui_utils.CARD_COLOR).pack(anchor="w", pady=(0, 8))
        overdue = circulation.get_overdue_students()
        if overdue:
            for row in overdue:
                username_s, student_num, title, due_date, _ = row
                ui_utils.make_label(
                    card,
                    f"• {username_s} ({student_num or '-'}) — {title} — due {due_date}",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(anchor="w", pady=2)
        else:
            ui_utils.make_label(card, "No overdue students.", variant="small", bg=ui_utils.CARD_COLOR).pack(anchor="w")

        Frame(card, bg=ui_utils.BORDER_COLOR, height=1).pack(fill=X, pady=16)

        ui_utils.make_label(card, "Most Borrowed Books", variant="subtitle", bg=ui_utils.CARD_COLOR).pack(anchor="w", pady=(0, 8))
        popular = circulation.get_most_borrowed_books()
        if popular:
            for title, author, count in popular:
                ui_utils.make_label(
                    card, f"• {title} by {author} — {count} loan(s)",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(anchor="w", pady=2)
        else:
            ui_utils.make_label(card, "No borrowing history yet.", variant="small", bg=ui_utils.CARD_COLOR).pack(anchor="w")

        Frame(card, bg=ui_utils.BORDER_COLOR, height=1).pack(fill=X, pady=16)

        ui_utils.make_label(
            card, f"Low Stock (≤ {database.LOW_STOCK_THRESHOLD} available)",
            variant="subtitle", bg=ui_utils.CARD_COLOR,
        ).pack(anchor="w", pady=(0, 8))
        low_stock = circulation.get_low_stock_books()
        if low_stock:
            for title, author, shelf, quantity, available in low_stock:
                ui_utils.make_label(
                    card,
                    f"• {title} by {author} — Shelf {shelf} — {available}/{quantity} left",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(anchor="w", pady=2)
        else:
            ui_utils.make_label(card, "All books are well stocked.", variant="small", bg=ui_utils.CARD_COLOR).pack(anchor="w")

    def show_fines():
        clear_main()
        database.setup_database()
        fines = circulation.get_all_fines()
        incident_penalties = incidents.get_all_incident_penalties()
        total_overdue = sum(f["amount"] for f in fines if not f["paid"])
        total_incident = sum(p["amount"] for p in incident_penalties)

        ui_utils.make_page_header(
            page_area, "Fines & Payments",
            f"₱{database.FINE_PER_DAY}/day overdue  •  "
            f"{len(fines)} overdue fine(s)  •  {len(incident_penalties)} lost/damage payment(s)",
        )

        if not fines and not incident_penalties:
            card = ui_utils.make_card(page_area)
            ui_utils.make_label(
                card, "No unpaid fines or lost/damage payments right now.",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(pady=20)
            ui_utils.make_label(
                card,
                "When a student has an overdue book, it will appear here.\n"
                "Lost/damage payments appear after you set a payment amount in Lost & Damaged.",
                variant="small", bg=ui_utils.CARD_COLOR, justify=LEFT,
            ).pack(anchor="w", pady=8)
            return

        list_frame = Frame(page_area, bg=ui_utils.BG_COLOR)
        list_frame.pack(fill=BOTH, expand=True, padx=28, pady=8)

        summary = Frame(list_frame, bg="#fef2f2", padx=16, pady=12,
                        highlightbackground="#dc2626", highlightthickness=1)
        summary.pack(fill=X, pady=(0, 12))
        ui_utils.make_label(
            summary,
            f"Total unpaid: ₱{total_overdue + total_incident:.0f}  "
            f"(Overdue: ₱{total_overdue:.0f}  •  Lost/Damage: ₱{total_incident:.0f})",
            variant="subtitle", bg="#fef2f2", fg=ui_utils.DANGER,
        ).pack(anchor="w")

        def mark_paid(loan_id):
            ok, msg = circulation.mark_fine_paid(loan_id)
            if ok:
                messagebox.showinfo("Paid", msg)
            else:
                messagebox.showerror("Error", msg)
            show_fines()

        if fines:
            ui_utils.make_label(
                list_frame, "Overdue Book Fines",
                variant="subtitle", bg=ui_utils.BG_COLOR,
            ).pack(anchor="w", pady=(0, 6))

        for fine in fines:
            frame = Frame(
                list_frame, bg=ui_utils.CARD_COLOR,
                highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                padx=16, pady=12,
            )
            frame.pack(fill=X, pady=6)

            returned_note = "Returned" if fine.get("returned") else "Still borrowed"
            ui_utils.make_label(
                frame,
                f"{fine['username']} ({fine['student_number']}) — {fine['title']}",
                variant="normal", bg=ui_utils.CARD_COLOR,
                font=("Helvetica Neue", 12, "bold"),
            ).pack(anchor="w")
            ui_utils.make_label(
                frame,
                f"Due: {fine['due_date']}  •  {fine['days_late']} day(s) late  •  "
                f"₱{fine['amount']:.0f}  •  {returned_note}  •  Unpaid",
                variant="small", bg=ui_utils.CARD_COLOR, fg=ui_utils.DANGER,
            ).pack(anchor="w", pady=(2, 8))

            ttk.Button(
                frame, text="Mark Overdue Fine Paid",
                style="Success.TButton",
                command=lambda lid=fine["loan_id"]: mark_paid(lid),
            ).pack(anchor="e")

        if incident_penalties:
            ui_utils.make_label(
                list_frame, "Lost & Damage Payments",
                variant="subtitle", bg=ui_utils.BG_COLOR,
            ).pack(anchor="w", pady=(16, 6))

        for penalty in incident_penalties:
            frame = Frame(
                list_frame, bg=ui_utils.CARD_COLOR,
                highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                padx=16, pady=12,
            )
            frame.pack(fill=X, pady=6)
            method = "Donate replacement" if penalty["settlement_method"] == "donation" else "Pay cash"
            ui_utils.make_label(
                frame,
                f"{penalty['username']} ({penalty['student_number']}) — {penalty['title']}",
                variant="normal", bg=ui_utils.CARD_COLOR,
                font=("Helvetica Neue", 12, "bold"),
            ).pack(anchor="w")
            ui_utils.make_label(
                frame,
                f"{penalty['incident_type'].title()}  •  ₱{penalty['amount']:.0f}  •  "
                f"{method}  •  Status: {penalty['status']}",
                variant="small", bg=ui_utils.CARD_COLOR, fg=ui_utils.DANGER,
            ).pack(anchor="w", pady=(2, 8))
            ui_utils.make_label(
                frame, "Settle this in Lost & Damaged → Mark Cash Paid or Verify Donation",
                variant="small", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w")

    def show_book_logs(book_id=None):
        clear_main()
        ui_utils.make_page_header(page_area, "Book Logs", "Complete borrowing history")

        columns = ("Book", "Student", "Student #", "Borrowed", "Due Date", "Status", "Returned")
        tree = ui_utils.make_treeview(page_area, columns)

        with database.connect() as conn:
            cursor = conn.cursor()
            query = """
                SELECT books.title, users.username, users.student_number,
                       loans.borrow_date, loans.due_date, loans.status, loans.returned
                FROM loans
                JOIN books ON loans.book_id = books.id
                JOIN users ON loans.user_id = users.id
            """
            params = ()
            if book_id:
                query += " WHERE books.id=?"
                params = (book_id,)
            query += " ORDER BY loans.borrow_date DESC"
            cursor.execute(query, params)
            logs = cursor.fetchall()

        for log in logs:
            returned_text = "Yes" if log[6] == 1 else "No"
            tree.insert("", "end", values=(
                log[0], log[1], log[2] or "-",
                log[3] or "-", log[4] or "-", log[5].title(), returned_text,
            ))

    def show_register_student():
        clear_main()
        ui_utils.make_page_header(page_area, "Register Student", "Create a new student account")

        card = ui_utils.make_card(page_area)
        form = Frame(card, bg=ui_utils.CARD_COLOR)
        form.pack()

        username_entry = ui_utils.make_form_field(form, "Username")
        password_entry = ui_utils.make_form_field(form, "Password", show="*")
        email_entry = ui_utils.make_form_field(form, "Email")
        student_number_entry = ui_utils.make_form_field(form, "Student Number")

        message = ui_utils.make_label(card, "", variant="small", bg=ui_utils.CARD_COLOR)
        message.pack(pady=8)

        def create_student():
            ok, text = database.register_student(
                username_entry.get(), password_entry.get(),
                student_number_entry.get(), email_entry.get(),
            )
            message.config(text=text, fg=ui_utils.SUCCESS if ok else ui_utils.DANGER)
            if ok:
                for entry in (username_entry, password_entry, email_entry, student_number_entry):
                    entry.delete(0, END)

        ttk.Button(card, text="Register Student", style="Success.TButton", command=create_student).pack(pady=12)

    def show_register_admin():
        clear_main()
        ui_utils.make_page_header(
            page_area, "Register Admin",
            "Add another librarian or admin account",
        )

        card = ui_utils.make_card(page_area)
        form = Frame(card, bg=ui_utils.CARD_COLOR)
        form.pack()

        username_entry = ui_utils.make_form_field(form, "Username")
        password_entry = ui_utils.make_form_field(form, "Password", show="*")

        message = ui_utils.make_label(card, "", variant="small", bg=ui_utils.CARD_COLOR)
        message.pack(pady=8)

        def create_admin():
            ok, text = database.register_admin(username_entry.get(), password_entry.get())
            message.config(text=text, fg=ui_utils.SUCCESS if ok else ui_utils.DANGER)
            if ok:
                username_entry.delete(0, END)
                password_entry.delete(0, END)

        ttk.Button(card, text="Register Admin", style="Primary.TButton", command=create_admin).pack(pady=12)

    def show_all_users():
        clear_main()
        ui_utils.make_page_header(
            page_area, "All Accounts",
            "View, block, or delete student accounts",
        )

        body = Frame(page_area, bg=ui_utils.BG_COLOR)
        body.pack(fill=BOTH, expand=True, padx=28, pady=8)
        body.grid_columnconfigure(0, weight=1)
        body.grid_rowconfigure(0, weight=1)

        columns = ("Username", "Role", "Student #", "Email", "Blocked")
        tree_frame = Frame(
            body, bg=ui_utils.CARD_COLOR,
            highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
        )
        tree_frame.grid(row=0, column=0, sticky="nsew")

        tree = ttk.Treeview(tree_frame, columns=columns, show="headings", style="Treeview", height=12)
        scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=tree.yview)
        tree.configure(yscrollcommand=scrollbar.set)
        for col in columns:
            tree.heading(col, text=col)
            tree.column(col, minwidth=90, width=120, anchor="center")
        tree.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbar.pack(side=RIGHT, fill=Y)

        user_map = {}

        def load_accounts():
            for item in tree.get_children():
                tree.delete(item)
            user_map.clear()
            for uid, uname, role, student_num, email, is_suspended in database.get_all_users():
                tree.insert("", "end", iid=str(uid), values=(
                    uname, role.title(), student_num or "-",
                    email or "-", "Yes" if is_suspended else "No",
                ))
                user_map[str(uid)] = (uname, role, bool(is_suspended))

        load_accounts()

        action_panel = Frame(
            body, bg=ui_utils.CARD_COLOR,
            highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
            padx=16, pady=12,
        )
        action_panel.grid(row=1, column=0, sticky="ew", pady=(12, 0))

        action_label = ui_utils.make_label(
            action_panel, "Select a student account above to block, unblock, or delete",
            variant="small", bg=ui_utils.CARD_COLOR,
        )
        action_label.pack(anchor="w", pady=(0, 8))

        btn_row = Frame(action_panel, bg=ui_utils.CARD_COLOR)
        btn_row.pack(anchor="w")

        def get_selected_student():
            sel = tree.selection()
            if not sel:
                messagebox.showinfo("Select Account", "Click a student row first.")
                return None, None, None
            uid = sel[0]
            uname, role, is_blocked = user_map.get(uid, ("", "", False))
            if role != "student":
                messagebox.showerror("Students Only", "Only student accounts can be managed here.")
                return None, None, None
            return uid, uname, is_blocked

        def block_selected():
            uid, uname, _ = get_selected_student()
            if not uid:
                return
            popup = Toplevel(root)
            popup.title("Block Account")
            popup.geometry("420x220")
            popup.configure(bg=ui_utils.CARD_COLOR)
            popup.transient(root)
            popup.grab_set()
            ui_utils.make_label(
                popup, f"Block student: {uname}",
                variant="title", bg=ui_utils.CARD_COLOR,
            ).pack(pady=(16, 8), padx=20, anchor="w")
            ui_utils.make_label(
                popup, "Reason (e.g. unpaid lost/damage book):",
                variant="small", bg=ui_utils.CARD_COLOR,
            ).pack(padx=20, anchor="w")
            reason_entry = ui_utils.make_entry(popup, width=40)
            reason_entry.pack(padx=20, pady=8, anchor="w")
            reason_entry.insert(0, "Unpaid lost or damaged book replacement.")

            def submit():
                ok, msg = database.block_student_account(int(uid), reason_entry.get())
                if ok:
                    messagebox.showinfo("Blocked", msg)
                    popup.destroy()
                    load_accounts()
                else:
                    messagebox.showerror("Error", msg)

            ttk.Button(popup, text="Block Account", style="Danger.TButton", command=submit).pack(padx=20, anchor="w")

        def unblock_selected():
            uid, uname, is_blocked = get_selected_student()
            if not uid:
                return
            if not is_blocked:
                messagebox.showinfo("Not Blocked", f"'{uname}' is not blocked.")
                return
            if not messagebox.askyesno("Unblock Account", f"Unblock student '{uname}'?"):
                return
            ok, msg = database.unblock_student_account(int(uid))
            if ok:
                messagebox.showinfo("Unblocked", msg)
                load_accounts()
            else:
                messagebox.showerror("Error", msg)

        def delete_selected():
            uid, uname, _ = get_selected_student()
            if not uid:
                return
            if not messagebox.askyesno(
                "Delete Student Account",
                f"Permanently delete student '{uname}'?\n\nThis cannot be undone.",
            ):
                return
            ok, msg = database.delete_student_account(int(uid))
            if ok:
                messagebox.showinfo("Deleted", msg)
                load_accounts()
            else:
                messagebox.showerror("Cannot Delete", msg)

        ttk.Button(btn_row, text="Block Account", style="Danger.TButton", command=block_selected).pack(side=LEFT, padx=(0, 8))
        ttk.Button(btn_row, text="Unblock Account", style="Success.TButton", command=unblock_selected).pack(side=LEFT, padx=(0, 8))
        ttk.Button(btn_row, text="Delete Account", style="Danger.TButton", command=delete_selected).pack(side=LEFT)

    def show_lost_damaged():
        clear_main()
        database.setup_database()
        open_list = incidents.get_open_incidents()

        ui_utils.make_page_header(
            page_area, "Student Reports — Lost & Damaged",
            f"{len(open_list)} report(s) waiting for review",
        )

        body = Frame(page_area, bg=ui_utils.BG_COLOR)
        body.pack(fill=BOTH, expand=True, padx=28, pady=8)
        body.grid_columnconfigure(0, weight=1)
        body.grid_rowconfigure(1, weight=1)

        ui_utils.make_label(
            body,
            "Select a student report below. After checking the book, fill in the review form "
            "and submit — the student will see your decision on their dashboard.",
            variant="small", bg=ui_utils.BG_COLOR, justify=LEFT,
        ).grid(row=0, column=0, sticky="ew", pady=(0, 10))

        if not open_list:
            card = Frame(
                body, bg=ui_utils.CARD_COLOR,
                highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                padx=20, pady=20,
            )
            card.grid(row=1, column=0, sticky="nsew")
            ui_utils.make_label(
                card, "No student reports waiting for review.",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w")
            return

        tree_frame = Frame(
            body, bg=ui_utils.CARD_COLOR,
            highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
        )
        tree_frame.grid(row=1, column=0, sticky="nsew")

        columns = ("Student", "Student #", "Book", "Type", "Status", "Reported")
        tree = ttk.Treeview(
            tree_frame, columns=columns, show="headings",
            style="Treeview", height=min(len(open_list), 8) or 4,
        )
        scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=tree.yview)
        tree.configure(yscrollcommand=scrollbar.set)
        for col in columns:
            tree.heading(col, text=col)
            tree.column(col, minwidth=90, width=130, anchor="center")
        tree.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbar.pack(side=RIGHT, fill=Y)

        report_map = {}
        for inc in open_list:
            inc_id, username, student_num, title, itype, _dmg, status, _penalty, reported_at, _desc, _uid = inc
            status_label = {
                "open": "Awaiting review",
                "payment_pending": "Payment required",
                "donation_pending": "Donation required",
            }.get(status, status.replace("_", " ").title())
            tree.insert("", "end", iid=str(inc_id), values=(
                username, student_num or "-", title, itype.title(),
                status_label, reported_at or "-",
            ))
            report_map[str(inc_id)] = inc_id

        review_panel = Frame(
            body, bg=ui_utils.CARD_COLOR,
            highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
            padx=20, pady=16,
        )
        review_panel.grid(row=2, column=0, sticky="ew", pady=(12, 0))

        review_title = ui_utils.make_label(
            review_panel, "Select a report above to review",
            variant="subtitle", bg=ui_utils.CARD_COLOR,
        )
        review_title.pack(anchor="w", pady=(0, 8))

        review_body = Frame(review_panel, bg=ui_utils.CARD_COLOR)
        review_body.pack(fill=X)

        def clear_review_body():
            for widget in review_body.winfo_children():
                widget.destroy()

        def show_review_form(incident_id):
            clear_review_body()
            inc = incidents.get_incident_by_id(incident_id)
            if not inc:
                review_title.config(text="Report not found — click Refresh")
                return

            (
                _id, username, student_num, title, itype, dmg_class, status,
                penalty, reported_at, description, _uid, admin_notes, settlement_method,
            ) = inc

            review_title.config(
                text=f"Review: {title} — {username} ({student_num or '-'}) — {itype.title()}",
            )

            details = Frame(review_body, bg=ui_utils.CARD_COLOR)
            details.pack(fill=X, pady=(0, 10))

            if description and description.strip() not in ("Book reported lost by student", ""):
                ui_utils.make_label(
                    details, f"Student report: {description.strip()}",
                    variant="small", bg=ui_utils.CARD_COLOR, justify=LEFT, wraplength=800,
                ).pack(anchor="w", pady=2)

            ui_utils.make_label(
                details,
                f"Reported: {reported_at or '-'}  •  Status: {status.replace('_', ' ').title()}  •  "
                f"Penalty: ₱{(penalty or 0):.0f}",
                variant="small", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w", pady=2)

            if admin_notes:
                ui_utils.make_label(
                    details, f"Previous notes: {admin_notes}",
                    variant="small", bg=ui_utils.CARD_COLOR, justify=LEFT, wraplength=800,
                ).pack(anchor="w", pady=2)

            msg_label = ui_utils.make_label(review_body, "", variant="small", bg=ui_utils.CARD_COLOR)
            msg_label.pack(anchor="w", pady=(4, 0))

            if status == "open":
                ui_utils.make_label(
                    review_body,
                    "After checking the book, choose the consequence and tell the student what to do:",
                    variant="normal", bg=ui_utils.CARD_COLOR,
                    font=("Helvetica Neue", 11, "bold"),
                ).pack(anchor="w", pady=(4, 8))

                form = Frame(review_body, bg=ui_utils.CARD_COLOR)
                form.pack(fill=X)

                consequence_var = StringVar(value="")
                amount_default = str(database.DEFAULT_REPLACEMENT_COST + database.PROCESSING_FEE)

                if itype == "damaged":
                    options = Frame(form, bg=ui_utils.CARD_COLOR)
                    options.pack(anchor="w", pady=(0, 8))
                    ttk.Radiobutton(
                        options, text=f"Minor damage — written warning + ₱{database.MINOR_DAMAGE_FEE:.0f} fee",
                        variable=consequence_var, value="minor_damage",
                    ).pack(anchor="w", pady=2)
                    ttk.Radiobutton(
                        options, text=f"Major damage — student must pay ₱{amount_default}",
                        variable=consequence_var, value="major_damage_pay",
                    ).pack(anchor="w", pady=2)
                    ttk.Radiobutton(
                        options, text="Major damage — student must donate replacement copy",
                        variable=consequence_var, value="major_damage_donate",
                    ).pack(anchor="w", pady=2)
                    consequence_var.set("minor_damage")
                else:
                    options = Frame(form, bg=ui_utils.CARD_COLOR)
                    options.pack(anchor="w", pady=(0, 8))
                    ttk.Radiobutton(
                        options, text=f"Lost book — student must pay ₱{amount_default}",
                        variable=consequence_var, value="lost_pay",
                    ).pack(anchor="w", pady=2)
                    ttk.Radiobutton(
                        options, text="Lost book — student must donate replacement copy",
                        variable=consequence_var, value="lost_donate",
                    ).pack(anchor="w", pady=2)
                    consequence_var.set("lost_pay")

                amount_row = Frame(form, bg=ui_utils.CARD_COLOR)
                amount_row.pack(anchor="w", pady=(0, 8))
                ui_utils.make_label(
                    amount_row, "Payment amount (₱) if student must pay:",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(side=LEFT, padx=(0, 8))
                amount_entry = ui_utils.make_entry(amount_row, width=12)
                amount_entry.pack(side=LEFT)
                amount_entry.insert(0, amount_default)

                ui_utils.make_label(
                    form, "Instructions for the student (what they need to do):",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(anchor="w")
                notes_box = Text(form, height=4, width=70, font=ui_utils.FONT_NORMAL, wrap=WORD)
                notes_box.pack(anchor="w", pady=(4, 8))
                if itype == "damaged":
                    notes_box.insert(
                        END,
                        "Please bring the damaged book to the library. "
                        "The librarian has checked the condition and set your penalty below.",
                    )
                else:
                    notes_box.insert(
                        END,
                        "Please visit the library office to settle this lost book case.",
                    )

                def submit_review(iid=incident_id):
                    action = consequence_var.get()
                    if not action:
                        messagebox.showerror("Missing Choice", "Select a consequence for the student.")
                        return
                    notes = notes_box.get("1.0", END).strip()
                    amount = amount_entry.get().strip() if action in ("lost_pay",) else None
                    ok, text = incidents.submit_admin_review(iid, action, amount, notes)
                    if ok:
                        messagebox.showinfo("Review Submitted", f"{text}\n\nThe student was notified on their dashboard.")
                        show_lost_damaged()
                    else:
                        msg_label.config(text=text, fg=ui_utils.DANGER)

                ttk.Button(
                    form, text="Submit Review & Notify Student",
                    style="Primary.TButton", command=submit_review,
                ).pack(anchor="w", pady=4)

            else:
                settlement = Frame(review_body, bg=ui_utils.CARD_COLOR)
                settlement.pack(fill=X, pady=(4, 0))
                ui_utils.make_label(
                    settlement,
                    "This case was already reviewed. Mark payment or donation when the student completes it:",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(anchor="w", pady=(0, 8))

                btn_row = Frame(settlement, bg=ui_utils.CARD_COLOR)
                btn_row.pack(anchor="w")

                if status in ("open", "payment_pending"):
                    ttk.Button(
                        btn_row, text="Mark Cash Paid",
                        style="Success.TButton",
                        command=lambda i=incident_id: (
                            incidents.settle_incident(i, "cash"),
                            show_lost_damaged(),
                        ),
                    ).pack(side=LEFT, padx=4)

                if status in ("open", "payment_pending", "donation_pending"):
                    ttk.Button(
                        btn_row, text="Require Donation Instead",
                        command=lambda i=incident_id: (
                            incidents.require_donation(i),
                            show_lost_damaged(),
                        ),
                    ).pack(side=LEFT, padx=4)

                if status == "donation_pending":
                    ttk.Button(
                        btn_row, text="Verify Donation ✓",
                        style="Success.TButton",
                        command=lambda i=incident_id: (
                            incidents.verify_donation(i, True),
                            show_lost_damaged(),
                        ),
                    ).pack(side=LEFT, padx=4)
                    ttk.Button(
                        btn_row, text="Reject Donation",
                        style="Danger.TButton",
                        command=lambda i=incident_id: (
                            incidents.verify_donation(i, False),
                            show_lost_damaged(),
                        ),
                    ).pack(side=LEFT, padx=4)

                if dmg_class:
                    ui_utils.make_label(
                        settlement,
                        f"Damage class: {dmg_class.title()}  •  Settlement: {settlement_method or 'pending'}",
                        variant="small", bg=ui_utils.CARD_COLOR,
                    ).pack(anchor="w", pady=(8, 0))

        def on_select(_event=None):
            selected = tree.selection()
            if not selected:
                return
            show_review_form(int(selected[0]))

        tree.bind("<<TreeviewSelect>>", on_select)
        tree.bind("<Double-1>", on_select)

        if tree.get_children():
            first = tree.get_children()[0]
            tree.selection_set(first)
            tree.focus(first)
            on_select()

        page_area.update_idletasks()

    def show_email_overdue():
        clear_main()
        try:
            _render_email_overdue_page()
        except Exception as exc:
            ui_utils.make_page_header(page_area, "Email Overdue Students", "Error loading page")
            ui_utils.make_label(
                page_area,
                f"Something went wrong: {exc}\n\nClick Refresh or contact support.",
                variant="small", bg=ui_utils.BG_COLOR, fg=ui_utils.DANGER, justify=LEFT,
            ).pack(padx=28, anchor="w")

    def _render_email_overdue_page():
        configured = email_service.is_configured()
        cfg = email_service.get_config_for_display()
        overdue_students = circulation.get_overdue_students_grouped()

        ui_utils.make_page_header(
            page_area, "Email Overdue Students",
            f"{len(overdue_students)} student(s) with overdue books",
        )

        # ---- STEP 1: Email setup (always visible on page) ----
        setup_outer = Frame(
            page_area, bg="#fef2f2",
            highlightbackground="#dc2626", highlightthickness=2,
        )
        setup_outer.pack(fill=X, padx=28, pady=(0, 12))

        setup_title = Label(
            setup_outer,
            text="STEP 1 — Email Setup (fill this in first or students will NOT receive mail)",
            font=("Helvetica Neue", 13, "bold"),
            fg="#dc2626", bg="#fef2f2", anchor="w",
        )
        setup_title.pack(fill=X, padx=16, pady=(12, 6))

        if configured:
            Label(
                setup_outer, text="Status: Email is configured.",
                font=ui_utils.FONT_SMALL, fg=ui_utils.SUCCESS, bg="#fef2f2", anchor="w",
            ).pack(fill=X, padx=16, pady=(0, 6))

        setup_form = Frame(setup_outer, bg="#fef2f2")
        setup_form.pack(fill=X, padx=16, pady=(0, 12))

        row1 = Frame(setup_form, bg="#fef2f2")
        row1.pack(fill=X, pady=4)
        Label(row1, text="SMTP Host:", width=18, anchor="w", bg="#fef2f2", fg="black").pack(side=LEFT)
        host_e = Entry(row1, width=30, font=ui_utils.FONT_NORMAL, fg="black", bg="white", insertbackground="black")
        host_e.pack(side=LEFT, padx=4)
        host_e.insert(0, cfg["smtp_host"])

        Label(row1, text="Port:", bg="#fef2f2", fg="black").pack(side=LEFT, padx=(12, 4))
        port_e = Entry(row1, width=6, font=ui_utils.FONT_NORMAL, fg="black", bg="white", insertbackground="black")
        port_e.pack(side=LEFT)
        port_e.insert(0, cfg["smtp_port"])

        row2 = Frame(setup_form, bg="#fef2f2")
        row2.pack(fill=X, pady=4)
        Label(row2, text="Gmail / Username:", width=18, anchor="w", bg="#fef2f2", fg="black").pack(side=LEFT)
        user_e = Entry(row2, width=40, font=ui_utils.FONT_NORMAL, fg="black", bg="white", insertbackground="black")
        user_e.pack(side=LEFT, padx=4)
        user_e.insert(0, cfg["smtp_user"])

        row3 = Frame(setup_form, bg="#fef2f2")
        row3.pack(fill=X, pady=4)
        Label(row3, text="App Password:", width=18, anchor="w", bg="#fef2f2", fg="black").pack(side=LEFT)
        pass_e = Entry(row3, width=40, font=ui_utils.FONT_NORMAL, show="*", fg="black", bg="white", insertbackground="black")
        pass_e.pack(side=LEFT, padx=4)
        if cfg["smtp_password"]:
            pass_e.insert(0, cfg["smtp_password"])

        row4 = Frame(setup_form, bg="#fef2f2")
        row4.pack(fill=X, pady=4)
        Label(row4, text="From Email:", width=18, anchor="w", bg="#fef2f2", fg="black").pack(side=LEFT)
        from_e = Entry(row4, width=40, font=ui_utils.FONT_NORMAL, fg="black", bg="white", insertbackground="black")
        from_e.pack(side=LEFT, padx=4)
        from_e.insert(0, cfg["from_email"])

        setup_feedback = Label(setup_form, text="", font=ui_utils.FONT_SMALL, bg="#fef2f2", anchor="w")
        setup_feedback.pack(fill=X, pady=(6, 0))

        setup_btns = Frame(setup_form, bg="#fef2f2")
        setup_btns.pack(fill=X, pady=(8, 0))

        def save_email_settings():
            ok, msg = email_service.save_config(
                host_e.get(), port_e.get(), user_e.get(), pass_e.get(),
                from_e.get(), "Readpath Library", use_tls=True, use_ssl=False,
            )
            setup_feedback.config(text=msg, fg=ui_utils.SUCCESS if ok else ui_utils.DANGER)
            if ok:
                show_email_overdue()

        def test_email_settings():
            ok, msg = email_service.test_connection(from_e.get().strip() or user_e.get().strip())
            setup_feedback.config(text=msg, fg=ui_utils.SUCCESS if ok else ui_utils.DANGER)

        # NOTE: switched from plain Button(...) to ttk.Button(..., style=...) because on macOS,
        # plain Tkinter Button widgets often ignore the bg= color (Aqua theme override) while
        # still applying fg="white" text, which made the button text invisible (white-on-white).
        # ttk.Button with the styles already defined in ui_utils.py renders correctly on all platforms.
        ttk.Button(
            setup_btns, text="Save Email Settings", style="Primary.TButton", command=save_email_settings,
        ).pack(side=LEFT, padx=(0, 8))
        ttk.Button(
            setup_btns, text="Send Test Email", style="Success.TButton", command=test_email_settings,
        ).pack(side=LEFT)

        # ---- STEP 2: Overdue students ----
        step2 = Label(
            page_area,
            text="STEP 2 — Click a student with overdue books, then send email",
            font=("Helvetica Neue", 13, "bold"),
            fg=ui_utils.TEXT_PRIMARY, bg=ui_utils.BG_COLOR, anchor="w",
        )
        step2.pack(fill=X, padx=28, pady=(4, 8))

        if not overdue_students:
            Label(
                page_area, text="No students with overdue books right now.",
                font=ui_utils.FONT_NORMAL, bg=ui_utils.BG_COLOR, anchor="w",
            ).pack(fill=X, padx=28, pady=8)
            return

        content = Frame(page_area, bg=ui_utils.BG_COLOR)
        content.pack(fill=X, padx=28, pady=(0, 16))

        list_frame = Frame(content, bg=ui_utils.BG_COLOR)
        list_frame.pack(fill=X)

        form_area = Frame(content, bg=ui_utils.CARD_COLOR,
                          highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                          padx=16, pady=16)
        form_area.pack(fill=X, pady=(12, 0))

        selected = {"frame": None}

        def highlight_row(frame, active):
            frame.config(
                bg="#dbeafe" if active else "#ffffff",
                highlightbackground="#2563eb" if active else "#cbd5e1",
                highlightthickness=2 if active else 1,
            )
            for child in frame.winfo_children():
                try:
                    child.config(bg=frame["bg"])
                except Exception:
                    pass

        def show_send_form(student):
            for widget in form_area.winfo_children():
                widget.destroy()

            total_fine = sum(b["fine_amount"] for b in student["books"])

            Label(
                form_area,
                text=f"Send to: {student['username']}  |  {student['email'] or 'NO EMAIL'}",
                font=("Helvetica Neue", 12, "bold"), bg=ui_utils.CARD_COLOR, anchor="w",
            ).pack(fill=X, pady=(0, 8))

            books_box = Frame(form_area, bg="#fef2f2", padx=10, pady=8)
            books_box.pack(fill=X, pady=(0, 10))
            for book in student["books"]:
                Label(
                    books_box,
                    text=(
                        f"- {book['title']}  |  due {book['due_date']}  |  "
                        f"{book['days_late']} days late  |  P{book['fine_amount']:.0f}"
                    ),
                    font=ui_utils.FONT_SMALL, bg="#fef2f2", anchor="w",
                ).pack(fill=X, pady=1)
            Label(
                books_box, text=f"Total fine: P{total_fine:.0f}",
                font=ui_utils.FONT_SMALL, fg="#dc2626", bg="#fef2f2", anchor="w",
            ).pack(fill=X, pady=(4, 0))

            if not student["email"]:
                Label(
                    form_area,
                    text="No email saved for this student. Add email in All Accounts first.",
                    font=ui_utils.FONT_SMALL, fg="#dc2626", bg=ui_utils.CARD_COLOR, anchor="w",
                ).pack(fill=X, pady=8)
                return

            Label(
                form_area, text="Your message to the student:",
                font=ui_utils.FONT_SMALL, bg=ui_utils.CARD_COLOR, anchor="w",
            ).pack(fill=X, pady=(0, 4))

            message_box = Text(
                form_area, height=5, font=ui_utils.FONT_NORMAL, wrap=WORD,
                fg="black", bg="white", insertbackground="black",
            )
            message_box.pack(fill=X, pady=(0, 10))
            message_box.insert(END, email_service.build_default_overdue_message(student))

            send_feedback = Label(form_area, text="", font=ui_utils.FONT_SMALL, bg=ui_utils.CARD_COLOR, anchor="w")
            send_feedback.pack(fill=X, pady=(0, 6))

            def send_now(uid=student["user_id"], email_addr=student["email"]):
                if not email_service.is_configured():
                    messagebox.showerror("Email Not Set Up", "Fill in STEP 1 above and click Save Email Settings.")
                    return
                custom_msg = message_box.get("1.0", END).strip()
                if not custom_msg:
                    messagebox.showerror("Empty Message", "Please type a message.")
                    return
                ok, msg = email_service.send_overdue_email_to_student(uid, custom_msg)
                send_feedback.config(text=msg, fg=ui_utils.SUCCESS if ok else ui_utils.DANGER)
                if ok:
                    messagebox.showinfo("Email Sent", f"Email sent to {email_addr}")
                else:
                    messagebox.showerror("Could Not Send", msg)

            ttk.Button(
                form_area, text="Send Email to Student", style="Primary.TButton", command=send_now,
            ).pack(anchor="w")

        def pick_student(student, row_frame):
            if selected["frame"]:
                highlight_row(selected["frame"], False)
            selected["frame"] = row_frame
            highlight_row(row_frame, True)
            show_send_form(student)

        first_row = None
        for student in overdue_students:
            total_fine = sum(b["fine_amount"] for b in student["books"])
            info = (
                f"{student['username']}  |  {student['student_number']}  |  "
                f"{student['email'] or 'NO EMAIL'}  |  "
                f"{len(student['books'])} overdue  |  fine P{total_fine:.0f}"
            )
            row = Frame(
                list_frame, bg="#ffffff",
                highlightbackground="#cbd5e1", highlightthickness=1,
                padx=12, pady=10, cursor="hand2",
            )
            row.pack(fill=X, pady=4)
            if first_row is None:
                first_row = row

            lbl = Label(row, text=info, font=ui_utils.FONT_NORMAL, bg="#ffffff", anchor="w")
            lbl.pack(fill=X)
            lbl.bind("<Button-1>", lambda e, s=student, r=row: pick_student(s, r))
            row.bind("<Button-1>", lambda e, s=student, r=row: pick_student(s, r))

        if first_row:
            pick_student(overdue_students[0], first_row)

        page_area.update_idletasks()

    def logout():
        root.destroy()
        import auth
        auth.login_screen()

    page_area, open_page, _refresh_page, clear_main = ui_utils.setup_page_navigation(
        main_area, show_welcome_page,
    )

    ui_utils.sidebar_button(sidebar, "Dashboard", lambda: open_page(show_welcome_page, is_home=True))
    ui_utils.sidebar_button(sidebar, "Add Book", lambda: open_page(show_add_book))
    ui_utils.sidebar_button(sidebar, "Approve Requests", lambda: open_page(show_requests))
    ui_utils.sidebar_button(sidebar, "View Books", lambda: open_page(show_view_books))
    ui_utils.sidebar_button(sidebar, "Reports", lambda: open_page(show_reports))
    ui_utils.sidebar_button(sidebar, "Fines", lambda: open_page(show_fines))
    ui_utils.sidebar_button(sidebar, "Lost & Damaged", lambda: open_page(show_lost_damaged))
    ui_utils.sidebar_button(sidebar, "Email Overdue", lambda: open_page(show_email_overdue))
    ui_utils.sidebar_button(sidebar, "Book Logs", lambda: open_page(show_book_logs))
    ui_utils.sidebar_button(sidebar, "All Accounts", lambda: open_page(show_all_users))
    ui_utils.sidebar_button(sidebar, "Register Student", lambda: open_page(show_register_student))
    ui_utils.sidebar_button(sidebar, "Register Admin", lambda: open_page(show_register_admin))
    ui_utils.sidebar_logout(sidebar, logout)

    open_page(show_welcome_page, is_home=True)
    root.mainloop()