from tkinter import *
from tkinter import ttk, messagebox
import database
import circulation
import books
import incidents
import notifications
import library_rules
import ui_utils
from datetime import datetime, timedelta


def open_student_dashboard(user_id):

    notifications.sync_overdue_alerts(user_id, send_email=True)

    root = Tk()
    root.title("Student Dashboard")
    ui_utils.maximize_window(root)
    ui_utils.setup_app_style(root)

    sidebar = ui_utils.make_sidebar(root, "STUDENT PANEL")
    main_area = Frame(root, bg=ui_utils.BG_COLOR)
    main_area.pack(side=RIGHT, expand=True, fill=BOTH)

    def show_dashboard():
        clear_main()
        notifications.sync_overdue_alerts(user_id, send_email=True)

        ui_utils.make_page_header(
            page_area, "My Dashboard",
            "Overdue alerts, fines, and library notifications",
        )

        today = datetime.now().date()
        fines, overdue_fine_total = circulation.get_student_fines(user_id)
        incident_penalties, incident_total = incidents.get_student_open_penalties(user_id)
        total_to_pay = overdue_fine_total + incident_total
        unread = notifications.get_unread_count(user_id)

        scroll_host = Frame(page_area, bg=ui_utils.BG_COLOR)
        scroll_host.pack(fill=BOTH, expand=True, padx=28, pady=8)

        canvas = Canvas(scroll_host, bg=ui_utils.BG_COLOR, highlightthickness=0)
        scrollbar = ttk.Scrollbar(scroll_host, orient=VERTICAL, command=canvas.yview)
        scroll_frame = Frame(canvas, bg=ui_utils.BG_COLOR)
        scroll_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all")),
        )
        canvas.create_window((0, 0), window=scroll_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        canvas.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbar.pack(side=RIGHT, fill=Y)

        def _on_mousewheel(event):
            canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

        canvas.bind_all("<MouseWheel>", _on_mousewheel)

        card = Frame(
            scroll_frame, bg=ui_utils.CARD_COLOR,
            highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
            padx=20, pady=20,
        )
        card.pack(fill=BOTH, expand=True)
        has_content = False

        suspended, suspend_reason = incidents.is_user_suspended(user_id)
        if suspended:
            has_content = True
            item = Frame(card, bg="#fef2f2", padx=16, pady=12)
            item.pack(fill=X, pady=6)
            Label(
                item, text="BORROWING SUSPENDED",
                font=("Helvetica Neue", 12, "bold"), fg=ui_utils.DANGER, bg=item["bg"],
            ).pack(anchor="w")
            Label(
                item, text=suspend_reason or "Contact the librarian to resolve open incidents.",
                font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg=item["bg"], wraplength=700, justify=LEFT,
            ).pack(anchor="w")

        with database.connect() as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT books.title, loans.due_date, loans.id
                FROM loans
                JOIN books ON loans.book_id = books.id
                WHERE loans.user_id = ?
                AND loans.status = 'borrowed'
                AND loans.returned = 0
                AND loans.due_date IS NOT NULL
            """, (user_id,))
            due_books = cursor.fetchall()

        overdue_books = []
        due_soon_books = []
        for title, due_str, loan_id in due_books:
            due_date = database.parse_loan_date(due_str)
            if not due_date:
                continue
            if due_date < today:
                days_late = (today - due_date).days
                fine_amount = days_late * database.FINE_PER_DAY
                overdue_books.append((title, due_date, days_late, fine_amount, loan_id))
            elif due_date <= today + timedelta(days=2):
                due_soon_books.append((title, due_date))

        if overdue_books:
            has_content = True
            banner = Frame(card, bg="#dc2626", padx=16, pady=14)
            banner.pack(fill=X, pady=(0, 10))
            Label(
                banner,
                text=f"⚠  {len(overdue_books)} OVERDUE BOOK(S) — RETURN IMMEDIATELY",
                font=("Helvetica Neue", 14, "bold"), fg="#ffffff", bg="#dc2626",
            ).pack(anchor="w")
            for title, due_date, days_late, fine_amount, _loan_id in overdue_books:
                item = Frame(banner, bg="#dc2626", padx=4, pady=4)
                item.pack(fill=X)
                Label(
                    item,
                    text=f"• {title} — was due {due_date} ({days_late} day(s) late) — fine: ₱{fine_amount:.0f}",
                    font=ui_utils.FONT_NORMAL, fg="#ffffff", bg="#dc2626", wraplength=680, justify=LEFT,
                ).pack(anchor="w")

        if total_to_pay > 0:
            has_content = True
            fine_banner = Frame(card, bg="#fef2f2", padx=16, pady=14,
                                highlightbackground="#dc2626", highlightthickness=2)
            fine_banner.pack(fill=X, pady=(0, 10))
            Label(
                fine_banner,
                text=f"TOTAL AMOUNT TO PAY: ₱{total_to_pay:.0f}",
                font=("Helvetica Neue", 16, "bold"), fg=ui_utils.DANGER, bg="#fef2f2",
            ).pack(anchor="w")
            if overdue_fine_total > 0:
                Label(
                    fine_banner,
                    text=f"Overdue book fines: ₱{overdue_fine_total:.0f} (₱{database.FINE_PER_DAY}/day)",
                    font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg="#fef2f2",
                ).pack(anchor="w", pady=(4, 2))
            if incident_total > 0:
                Label(
                    fine_banner,
                    text=f"Lost/damage penalties: ₱{incident_total:.0f}",
                    font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg="#fef2f2",
                ).pack(anchor="w", pady=(0, 4))
            Label(
                fine_banner,
                text="Pay at the library. The admin will mark it as paid or clear your case.",
                font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg="#fef2f2",
            ).pack(anchor="w", pady=(0, 8))
            for fine in fines:
                if fine["paid"]:
                    continue
                status_note = " (book returned)" if fine.get("book_returned") else " (book still out)"
                item = Frame(fine_banner, bg="#fef2f2", padx=4, pady=2)
                item.pack(fill=X)
                Label(
                    item,
                    text=(
                        f"• Overdue: {fine['title']} — ₱{fine['amount']:.0f} "
                        f"({fine['days_late']} day(s) late){status_note}"
                    ),
                    font=("Helvetica Neue", 11, "bold"),
                    fg=ui_utils.DANGER, bg="#fef2f2", wraplength=680, justify=LEFT,
                ).pack(anchor="w")
            for penalty in incident_penalties:
                method = "donate replacement" if penalty["settlement_method"] == "donation" else "pay cash"
                item = Frame(fine_banner, bg="#fef2f2", padx=4, pady=2)
                item.pack(fill=X)
                Label(
                    item,
                    text=(
                        f"• {penalty['incident_type'].title()}: {penalty['title']} — "
                        f"₱{penalty['amount']:.0f} ({method})"
                    ),
                    font=("Helvetica Neue", 11, "bold"),
                    fg=ui_utils.DANGER, bg="#fef2f2", wraplength=680, justify=LEFT,
                ).pack(anchor="w")

        if unread > 0:
            has_content = True
            ui_utils.make_label(
                card, f"Admin & library notifications ({unread} unread):",
                variant="subtitle", bg=ui_utils.CARD_COLOR, fg=ui_utils.DANGER,
            ).pack(anchor="w", pady=(0, 6))

        incident_reports = incidents.get_student_incidents(user_id)
        active_reports = [r for r in incident_reports if r[4] not in ("settled",)]
        if active_reports:
            has_content = True
            ui_utils.make_label(
                card, "Lost / damage reports:",
                variant="subtitle", bg=ui_utils.CARD_COLOR, fg=ui_utils.WARNING,
            ).pack(anchor="w", pady=(8, 4))
            for inc in active_reports:
                _iid, title, itype, dmg_class, status, penalty, reported_at, admin_notes, description, settlement_method = inc
                if status == "open":
                    line = f"• {title} ({itype}) — waiting for librarian review"
                    fg = ui_utils.WARNING
                elif status == "payment_pending":
                    line = f"• {title} ({itype}) — pay ₱{(penalty or 0):.0f} at the library"
                    fg = ui_utils.DANGER
                elif status == "donation_pending":
                    line = f"• {title} ({itype}) — donate a replacement copy"
                    fg = ui_utils.DANGER
                elif status == "warning_issued":
                    line = f"• {title} ({itype}) — written warning, fee ₱{(penalty or 0):.0f}"
                    fg = ui_utils.WARNING
                else:
                    line = f"• {title} ({itype}) — {status.replace('_', ' ')}"
                    fg = ui_utils.TEXT_SECONDARY
                item = Frame(card, bg="#fff7ed", padx=12, pady=8)
                item.pack(fill=X, pady=4)
                Label(
                    item, text=line,
                    font=("Helvetica Neue", 11, "bold"), fg=fg, bg="#fff7ed", wraplength=680, justify=LEFT,
                ).pack(anchor="w")
                if admin_notes:
                    Label(
                        item, text=f"Librarian: {admin_notes}",
                        font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_PRIMARY, bg="#fff7ed",
                        wraplength=680, justify=LEFT,
                    ).pack(anchor="w", pady=(2, 0))

        for nid, title, message, ntype, is_read, email_sent, created_at in notifications.get_user_notifications(user_id):
            has_content = True
            if ntype == "overdue":
                bg, fg = "#fef2f2", ui_utils.DANGER
            elif ntype == "incident":
                bg, fg = "#fff7ed", ui_utils.WARNING
            elif ntype == "suspension":
                bg, fg = "#fffbeb", ui_utils.DANGER
            else:
                bg, fg = "#f8fafc", ui_utils.TEXT_PRIMARY
            item = Frame(card, bg=bg, padx=16, pady=10)
            item.pack(fill=X, pady=4)
            read_mark = " [NEW]" if not is_read else ""
            Label(
                item, text=f"{title}{read_mark}",
                font=("Helvetica Neue", 11, "bold"),
                fg=fg,
                bg=bg,
            ).pack(anchor="w")
            Label(
                item, text=f"{message}\n({created_at})",
                font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg=bg,
                wraplength=700, justify=LEFT,
            ).pack(anchor="w")
            if not is_read:
                ttk.Button(
                    item, text="Mark Read",
                    command=lambda n=nid: (notifications.mark_read(n, user_id), show_dashboard()),
                ).pack(anchor="e", pady=(4, 0))

        for title, due_date in due_soon_books:
            has_content = True
            item = Frame(card, bg="#fffbeb", padx=16, pady=10)
            item.pack(fill=X, pady=4)
            Label(
                item, text=f"DUE SOON: {title}",
                font=("Helvetica Neue", 11, "bold"), fg=ui_utils.WARNING, bg=item["bg"],
            ).pack(anchor="w")
            Label(
                item, text=f"Due on {due_date}",
                font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg=item["bg"],
            ).pack(anchor="w")

        holds = circulation.get_student_reservations(user_id)
        for hold in holds:
            has_content = True
            hold_id, title, author, status, created_at, notified_at = hold
            if status == "ready":
                bg, fg, label = "#ecfdf5", ui_utils.SUCCESS, f"HOLD READY: {title}"
                detail = "A copy is available — go to My Holds to claim it."
            else:
                bg, fg, label = "#f8fafc", ui_utils.TEXT_SECONDARY, f"ON WAITLIST: {title}"
                detail = f"Waiting since {created_at or '—'}"
            item = Frame(card, bg=bg, padx=16, pady=10)
            item.pack(fill=X, pady=4)
            Label(item, text=label, font=("Helvetica Neue", 11, "bold"), fg=fg, bg=bg).pack(anchor="w")
            Label(item, text=detail, font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg=bg).pack(anchor="w")

        active = circulation.count_active_loans(user_id)
        ui_utils.make_label(
            card,
            f"Borrow limit: {active}/{database.MAX_BORROW_LIMIT} books in use",
            variant="subtitle", bg=ui_utils.CARD_COLOR,
        ).pack(anchor="w", pady=(12, 4))
        rules_link = Label(
            card,
            text="Read Library Rules & Regulations →",
            font=ui_utils.FONT_SMALL, fg=ui_utils.ACCENT, bg=ui_utils.CARD_COLOR,
            cursor="hand2",
        )
        rules_link.pack(anchor="w", pady=(0, 8))
        rules_link.bind("<Button-1>", lambda e: open_page(show_library_rules))
        has_content = True

        catalog_count = books.count_books()
        ui_utils.make_label(
            card,
            f"Library catalog: {catalog_count} book(s) available to search",
            variant="subtitle", bg=ui_utils.CARD_COLOR, fg=ui_utils.ACCENT,
        ).pack(anchor="w", pady=(0, 4))
        ui_utils.make_label(
            card,
            "Books are listed under Search Books in the left menu.",
            variant="small", bg=ui_utils.CARD_COLOR,
        ).pack(anchor="w", pady=(0, 12))

        if not has_content:
            ui_utils.make_label(
                card,
                "You're all caught up — no fines, holds, or due date alerts.",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(pady=20)

    def show_search():
        clear_main()
        ui_utils.make_page_header(
            page_area, "Search Books",
            f"{books.count_books()} book(s) in catalog — double-click to borrow or place hold",
        )

        search_row = Frame(page_area, bg=ui_utils.BG_COLOR)
        search_row.pack(fill=X, padx=28, pady=(0, 8))
        ui_utils.make_label(
            search_row, "Search by title or author:", variant="small", bg=ui_utils.BG_COLOR,
        ).pack(side=LEFT, padx=(0, 8))
        title_entry = ui_utils.make_entry(search_row, width=40)
        title_entry.pack(side=LEFT)

        def clear_search():
            title_entry.delete(0, END)
            load_books()

        ttk.Button(search_row, text="Show All", command=clear_search).pack(side=LEFT, padx=8)

        action_row = Frame(page_area, bg=ui_utils.BG_COLOR)
        action_row.pack(fill=X, padx=28, pady=(0, 4))
        ui_utils.make_label(
            action_row,
            "Select a book, then click Borrow Selected — or double-click the row.",
            variant="small", bg=ui_utils.BG_COLOR,
        ).pack(side=LEFT)
        ttk.Button(
            action_row, text="Borrow Selected Book", style="Primary.TButton", command=lambda: on_select(),
        ).pack(side=RIGHT)

        count_label = ui_utils.make_label(
            page_area, "", variant="small", bg=ui_utils.BG_COLOR,
        )
        count_label.pack(anchor="w", padx=28, pady=(0, 4))

        columns = ("Title", "Author", "Shelf", "Available", "Status")
        tree = ui_utils.make_treeview(page_area, columns)

        def load_books(filter_text=""):
            for item in tree.get_children():
                tree.delete(item)

            catalog = books.get_all_books(filter_text)
            term = filter_text.strip()

            if term:
                count_label.config(text=f"{len(catalog)} book(s) matching '{term}'")
            else:
                count_label.config(text=f"{len(catalog)} book(s) in the catalog")

            for book in catalog:
                book_id, title, author, shelf, _desc, quantity, available = book
                status = f"{available} available" if available > 0 else "Checked out"
                tree.insert("", "end", iid=str(book_id), values=(
                    title, author, shelf or "-", available, status,
                ))

        def on_select(_event=None):
            selected = tree.selection()
            if not selected:
                messagebox.showinfo("Select a Book", "Click a book row first, then click Borrow Selected Book.")
                return
            book_data = books.get_book_by_id(int(selected[0]))
            if not book_data:
                messagebox.showerror("Error", "Book not found. Refreshing list.")
                load_books(title_entry.get())
                return

            book_id, title, author, shelf, description, quantity, available = book_data

            popup = Toplevel(root)
            popup.title(title)
            popup.geometry("480x440")
            popup.configure(bg=ui_utils.CARD_COLOR)
            popup.resizable(False, False)

            ui_utils.make_label(
                popup, title, variant="title", bg=ui_utils.CARD_COLOR,
            ).pack(pady=(20, 10), padx=24, anchor="w")
            for line in [
                f"Author: {author}",
                f"Shelf: {shelf}",
                f"Description: {description or 'No description'}",
                f"Available copies: {available}",
            ]:
                ui_utils.make_label(
                    popup, line, variant="normal", bg=ui_utils.CARD_COLOR,
                ).pack(anchor="w", padx=24, pady=2)

            msg_label = ui_utils.make_label(popup, "", variant="small", bg=ui_utils.CARD_COLOR)
            msg_label.pack(padx=24, anchor="w", pady=8)

            btn_row = Frame(popup, bg=ui_utils.CARD_COLOR)
            btn_row.pack(padx=24, anchor="w", pady=8)

            if available > 0:
                ui_utils.make_label(
                    popup, "Number of copies to borrow:", variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(padx=24, anchor="w", pady=(8, 4))
                num_entry = ui_utils.make_entry(popup, width=10)
                num_entry.pack(padx=24, anchor="w")
                num_entry.insert(0, "1")

                def request_borrow():
                    try:
                        num_to_borrow = int(num_entry.get())
                        if num_to_borrow <= 0:
                            raise ValueError("Must borrow at least 1 copy")
                        if num_to_borrow > available:
                            raise ValueError(f"Only {available} copies available")
                        success, message = circulation.request_book(
                            book_id, user_id, num_to_borrow,
                        )
                        msg_label.config(
                            text=message,
                            fg=ui_utils.SUCCESS if success else ui_utils.DANGER,
                        )
                        if success:
                            messagebox.showinfo(
                                "Request Sent",
                                f"{message}\n\nThe librarian will see this under Admin → Approve Requests.",
                            )
                            popup.destroy()
                        load_books(title_entry.get())
                    except Exception as e:
                        msg_label.config(text=str(e), fg=ui_utils.DANGER)

                ttk.Button(
                    btn_row, text="Request Borrow",
                    style="Primary.TButton", command=request_borrow,
                ).pack(side=LEFT)
            else:
                def place_hold():
                    success, message = circulation.place_hold(book_id, user_id)
                    msg_label.config(
                        text=message,
                        fg=ui_utils.SUCCESS if success else ui_utils.DANGER,
                    )

                ttk.Button(
                    btn_row, text="Place Hold",
                    style="Warning.TButton", command=place_hold,
                ).pack(side=LEFT)
                ui_utils.make_label(
                    popup,
                    "All copies are checked out. Place a hold to join the waitlist.",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(padx=24, anchor="w", pady=(8, 0))

        title_entry.bind("<KeyRelease>", lambda e: load_books(title_entry.get()))
        tree.bind("<Double-1>", on_select)
        load_books()

    def show_my_holds():
        clear_main()
        ui_utils.make_page_header(
            page_area, "My Books & Holds",
            "All books you currently have and books on your waitlist",
        )

        card = ui_utils.make_card(page_area)
        has_any = False
        today = datetime.now().date()

        borrowed = incidents.get_active_loans_with_ids(user_id)
        if borrowed:
            has_any = True
            ui_utils.make_label(
                card, f"Currently Borrowed ({len(borrowed)})",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w", pady=(0, 8))
            for loan_id, title, author, due_date, status in borrowed:
                due = database.parse_loan_date(due_date)
                overdue = due and due < today
                bg = "#fef2f2" if overdue else "#f8fafc"
                frame = Frame(
                    card, bg=bg,
                    highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                    padx=16, pady=12,
                )
                frame.pack(fill=X, pady=4)
                status_text = "OVERDUE" if overdue else status.title()
                Label(
                    frame, text=f"{title} by {author}",
                    font=("Helvetica Neue", 12, "bold"), fg=ui_utils.DANGER if overdue else ui_utils.TEXT_PRIMARY, bg=bg,
                ).pack(anchor="w")
                Label(
                    frame,
                    text=f"Due: {due_date or '—'}  •  Status: {status_text}",
                    font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg=bg,
                ).pack(anchor="w", pady=(2, 0))

        pending = circulation.get_student_loans(user_id)
        pending_rows = [r for r in pending if r[4] == "pending" and r[5] == 0]
        if pending_rows:
            has_any = True
            ui_utils.make_label(
                card, f"Pending Approval ({len(pending_rows)})",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w", pady=(16, 8))
            for title, author, borrow_date, due_date, status, returned in pending_rows:
                frame = Frame(
                    card, bg="#fffbeb",
                    highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                    padx=16, pady=12,
                )
                frame.pack(fill=X, pady=4)
                Label(
                    frame, text=f"{title} by {author}",
                    font=("Helvetica Neue", 12, "bold"), fg=ui_utils.WARNING, bg="#fffbeb",
                ).pack(anchor="w")
                Label(
                    frame, text="Waiting for admin approval",
                    font=ui_utils.FONT_SMALL, fg=ui_utils.TEXT_SECONDARY, bg="#fffbeb",
                ).pack(anchor="w", pady=(2, 0))

        holds = circulation.get_student_reservations(user_id)
        if holds:
            has_any = True
            ui_utils.make_label(
                card, f"Waitlist Holds ({len(holds)})",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w", pady=(16, 8))
            for hold_id, title, author, status, created_at, notified_at in holds:
                frame = Frame(
                    card, bg=ui_utils.CARD_COLOR,
                    highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                    padx=16, pady=12,
                )
                frame.pack(fill=X, pady=6)

                ui_utils.make_label(
                    frame, f"{title} by {author}",
                    variant="normal", bg=ui_utils.CARD_COLOR,
                    font=("Helvetica Neue", 12, "bold"),
                ).pack(anchor="w")
                ui_utils.make_label(
                    frame, f"Hold status: {status.title()}  •  Joined: {created_at or '—'}",
                    variant="small", bg=ui_utils.CARD_COLOR,
                ).pack(anchor="w", pady=(2, 8))

                actions = Frame(frame, bg=ui_utils.CARD_COLOR)
                actions.pack(fill=X)

                if status == "ready":
                    def claim(hid=hold_id):
                        ok, msg = circulation.claim_ready_hold(hid, user_id)
                        if ok:
                            show_my_holds()
                        else:
                            messagebox.showerror("Error", msg)

                    ttk.Button(
                        actions, text="Claim & Request Borrow",
                        style="Success.TButton", command=claim,
                    ).pack(side=LEFT)

                def cancel(hid=hold_id):
                    circulation.cancel_hold(hid, user_id)
                    show_my_holds()

                ttk.Button(
                    actions, text="Cancel Hold", style="Danger.TButton",
                    command=lambda h=hold_id: cancel(h),
                ).pack(side=RIGHT)

        if not has_any:
            ui_utils.make_label(
                card, "You have no borrowed books or holds right now.",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(pady=20)

    def show_my_books():
        clear_main()
        ui_utils.make_page_header(page_area, "My Borrowed Books", "History and report lost/damaged books")

        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 = ("Title", "Author", "Due Date", "Status")
        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=8)
        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)

        loan_map = {}
        for loan_id, title, author, due_date, status in incidents.get_active_loans_with_ids(user_id):
            tree.insert("", "end", iid=str(loan_id), values=(
                title, author, due_date or "-", status.title(),
            ))
            loan_map[str(loan_id)] = title

        logs = circulation.get_student_loans(user_id)
        for log in logs:
            if log[4] == "borrowed" and log[5] == 0:
                continue
            tree.insert("", "end", values=(
                log[0], log[1], log[3] or "-", log[4].title(),
            ))

        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))

        ui_utils.make_label(
            action_panel,
            "Select an active borrowed book above to report lost or damaged (Library Rules Art. I & II)",
            variant="small", bg=ui_utils.CARD_COLOR,
        ).pack(anchor="w", pady=(0, 8))

        btn_row = Frame(action_panel, bg=ui_utils.CARD_COLOR)
        btn_row.pack(fill=X)

        def report_lost():
            sel = tree.selection()
            if not sel or sel[0] not in loan_map:
                messagebox.showinfo("Select Book", "Select an active borrowed book first.")
                return
            if not messagebox.askyesno(
                "Report Lost",
                f"Report '{loan_map[sel[0]]}' as lost?\n\n"
                "The librarian will review your report and tell you to pay or donate a replacement.",
            ):
                ok, msg = incidents.report_lost(int(sel[0]), user_id)
                messagebox.showinfo("Result", msg) if ok else messagebox.showerror("Error", msg)
                show_my_books()

        def report_damaged():
            sel = tree.selection()
            if not sel or sel[0] not in loan_map:
                messagebox.showinfo("Select Book", "Select an active borrowed book first.")
                return
            popup = Toplevel(root)
            popup.title("Report Damage")
            popup.geometry("420x280")
            popup.configure(bg=ui_utils.CARD_COLOR)
            ui_utils.make_label(
                popup, f"Report damage: {loan_map[sel[0]]}",
                variant="title", bg=ui_utils.CARD_COLOR,
            ).pack(pady=(16, 8), padx=20, anchor="w")
            ui_utils.make_label(
                popup, "Describe the damage (scratches, torn pages, water damage, etc.):",
                variant="small", bg=ui_utils.CARD_COLOR,
            ).pack(padx=20, anchor="w")
            desc = Text(popup, height=5, width=40, font=ui_utils.FONT_NORMAL)
            desc.pack(padx=20, pady=8)

            def submit():
                ok, msg = incidents.report_damage(int(sel[0]), user_id, desc.get("1.0", END))
                messagebox.showinfo("Result", msg) if ok else messagebox.showerror("Error", msg)
                popup.destroy()
                show_my_books()

            ttk.Button(popup, text="Submit Report", style="Primary.TButton", command=submit).pack(padx=20, anchor="w")

        ttk.Button(btn_row, text="Report Lost Book", style="Danger.TButton", command=report_lost).pack(side=LEFT, padx=4)
        ttk.Button(btn_row, text="Report Damaged Book", style="Warning.TButton", command=report_damaged).pack(side=LEFT, padx=4)

        incident_rows = incidents.get_student_incidents(user_id)
        if incident_rows:
            ui_utils.make_label(
                action_panel, "Your lost/damage reports:",
                variant="subtitle", bg=ui_utils.CARD_COLOR,
            ).pack(anchor="w", pady=(12, 4))
            for inc in incident_rows:
                iid, title, itype, dmg_class, status, penalty, reported_at, admin_notes, description, settlement_method = inc
                frame = Frame(
                    action_panel, bg="#fff7ed",
                    highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
                    padx=12, pady=10,
                )
                frame.pack(fill=X, pady=4)

                if status == "open":
                    status_text = "Waiting for librarian review"
                    status_color = ui_utils.WARNING
                elif status == "payment_pending":
                    status_text = f"Pay ₱{(penalty or 0):.0f} at the library"
                    status_color = ui_utils.DANGER
                elif status == "donation_pending":
                    status_text = "Donate a replacement copy to the library"
                    status_color = ui_utils.DANGER
                elif status == "warning_issued":
                    status_text = f"Written warning — fee ₱{(penalty or 0):.0f}"
                    status_color = ui_utils.WARNING
                elif status == "settled":
                    status_text = "Settled"
                    status_color = ui_utils.SUCCESS
                else:
                    status_text = status.replace("_", " ").title()
                    status_color = ui_utils.TEXT_SECONDARY

                ui_utils.make_label(
                    frame,
                    f"{title} — {itype.title()} ({dmg_class or 'unassessed'})",
                    variant="normal", bg="#fff7ed",
                    font=("Helvetica Neue", 11, "bold"),
                ).pack(anchor="w")
                ui_utils.make_label(
                    frame,
                    f"Status: {status_text}  •  Reported: {reported_at}",
                    variant="small", bg="#fff7ed", fg=status_color,
                ).pack(anchor="w", pady=(2, 0))

                if description and description.strip() not in ("Book reported lost by student", ""):
                    ui_utils.make_label(
                        frame, f"Your report: {description.strip()}",
                        variant="small", bg="#fff7ed", justify=LEFT, wraplength=650,
                    ).pack(anchor="w", pady=(2, 0))

                if admin_notes:
                    ui_utils.make_label(
                        frame,
                        f"Librarian says: {admin_notes}",
                        variant="small", bg="#fff7ed", fg=ui_utils.TEXT_PRIMARY,
                        justify=LEFT, wraplength=650,
                        font=("Helvetica Neue", 10, "bold"),
                    ).pack(anchor="w", pady=(4, 0))

                if settlement_method == "donation" and status == "donation_pending":
                    ui_utils.make_label(
                        frame,
                        "Action: Bring a new copy of the same book to the library for verification.",
                        variant="small", bg="#fff7ed", fg=ui_utils.DANGER,
                    ).pack(anchor="w", pady=(2, 0))
                elif status == "payment_pending":
                    ui_utils.make_label(
                        frame,
                        "Action: Pay at the library. Borrowing is blocked until settled.",
                        variant="small", bg="#fff7ed", fg=ui_utils.DANGER,
                    ).pack(anchor="w", pady=(2, 0))
                elif status == "open":
                    ui_utils.make_label(
                        frame,
                        "The librarian will review your report and notify you here.",
                        variant="small", bg="#fff7ed",
                    ).pack(anchor="w", pady=(2, 0))

    def show_library_rules():
        clear_main()
        ui_utils.make_page_header(
            page_area, library_rules.LIBRARY_RULES_TITLE,
            "Please read and follow these rules when borrowing books",
        )

        wrapper = Frame(page_area, bg=ui_utils.BG_COLOR)
        wrapper.pack(fill=BOTH, expand=True, padx=28, pady=8)

        text_frame = Frame(
            wrapper, bg=ui_utils.CARD_COLOR,
            highlightbackground=ui_utils.BORDER_COLOR, highlightthickness=1,
        )
        text_frame.pack(fill=BOTH, expand=True)

        scrollbar = Scrollbar(text_frame)
        scrollbar.pack(side=RIGHT, fill=Y)

        rules_text = Text(
            text_frame, wrap=WORD, font=ui_utils.FONT_NORMAL,
            fg=ui_utils.TEXT_PRIMARY, bg=ui_utils.CARD_COLOR,
            padx=20, pady=16, relief=FLAT, yscrollcommand=scrollbar.set,
        )
        rules_text.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbar.config(command=rules_text.yview)

        rules_text.insert(END, library_rules.LIBRARY_RULES_TITLE + "\n\n")
        for article_title, sections in library_rules.LIBRARY_RULES_SECTIONS:
            rules_text.insert(END, article_title + "\n", "title")
            for section_title, section_body in sections:
                rules_text.insert(END, f"\n{section_title}\n", "section")
                rules_text.insert(END, section_body + "\n")
            rules_text.insert(END, "\n")

        rules_text.tag_configure("title", font=("Helvetica Neue", 14, "bold"), foreground=ui_utils.ACCENT)
        rules_text.tag_configure("section", font=("Helvetica Neue", 11, "bold"), foreground=ui_utils.TEXT_PRIMARY)
        rules_text.config(state=DISABLED)

    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_dashboard,
    )

    ui_utils.sidebar_button(sidebar, "Dashboard", lambda: open_page(show_dashboard, is_home=True))
    ui_utils.sidebar_button(sidebar, "Library Rules", lambda: open_page(show_library_rules))
    ui_utils.sidebar_button(sidebar, "Search Books", lambda: open_page(show_search))
    ui_utils.sidebar_button(sidebar, "My Books & Holds", lambda: open_page(show_my_holds))
    ui_utils.sidebar_button(sidebar, "Report Lost/Damage", lambda: open_page(show_my_books))
    ui_utils.sidebar_logout(sidebar, logout)

    open_page(show_dashboard, is_home=True)
    root.mainloop()
