from __future__ import annotations

import tkinter as tk
from tkinter import messagebox, scrolledtext, ttk


REPORT_FIELDS_BY_SCOPE_REF: dict[str, list[tuple[str, str]]] = {}


def _build_report_builder_module(self, parent: ttk.Frame) -> None:
    parent.columnconfigure(0, weight=1)
    parent.rowconfigure(1, weight=1)

    tool_home = ttk.Frame(parent)
    tool_home.grid(row=0, column=0, sticky="nsew")
    tool_home.columnconfigure(0, weight=1)
    self.tool_home_frame = tool_home

    self.widget_text["tool_section_title"] = ttk.Label(tool_home, style="Section.TLabel")
    self.widget_text["tool_section_title"].grid(row=0, column=0, sticky="w")
    self.widget_text["tool_home_hint"] = ttk.Label(tool_home, style="Muted.TLabel", wraplength=900, justify="left")
    self.widget_text["tool_home_hint"].grid(row=1, column=0, sticky="w", pady=(6, 0))

    tool_buttons = ttk.Frame(tool_home)
    tool_buttons.grid(row=2, column=0, sticky="w", pady=(18, 0))
    self.widget_text["tool_report_builder_button"] = ttk.Button(
        tool_buttons,
        command=self.show_tool_report_builder,
    )
    self.widget_text["tool_report_builder_button"].pack(side="left")
    self.register_permission_widget("action.tool.report_builder", self.widget_text["tool_report_builder_button"])
    self.widget_text["tool_download_tech_pack_button"] = ttk.Button(
        tool_buttons,
        command=self.show_tool_download_tech_pack,
    )
    self.widget_text["tool_download_tech_pack_button"].pack(side="left", padx=(10, 0))
    self.register_permission_widget("action.tool.download_tech_pack", self.widget_text["tool_download_tech_pack_button"])

    report_container = ttk.Frame(parent)
    report_container.grid(row=0, column=0, sticky="nsew")
    report_container.columnconfigure(0, weight=1)
    report_container.rowconfigure(2, weight=1)
    self.tool_report_container = report_container

    report_toolbar = ttk.Frame(report_container)
    report_toolbar.grid(row=0, column=0, sticky="ew", pady=(0, 10))
    self.widget_text["tool_back_home_button"] = ttk.Button(report_toolbar, command=self.show_tool_home)
    self.widget_text["tool_back_home_button"].pack(side="left")

    report_header = ttk.Frame(report_container)
    report_header.grid(row=1, column=0, sticky="ew", pady=(0, 10))
    self.widget_text["report_title"] = ttk.Label(report_header, style="Section.TLabel")
    self.widget_text["report_title"].pack(anchor="w")
    self.widget_text["report_subtitle"] = ttk.Label(report_header, wraplength=980)
    self.widget_text["report_subtitle"].pack(anchor="w", pady=(4, 0))

    report_body = ttk.Frame(report_container)
    report_body.grid(row=2, column=0, sticky="nsew")
    report_body.columnconfigure(0, weight=0)
    report_body.columnconfigure(1, weight=1)
    report_body.columnconfigure(3, weight=1)
    report_body.rowconfigure(1, weight=1)

    self.widget_text["saved_templates"] = ttk.Label(report_body, style="FieldLabel.TLabel")
    self.widget_text["saved_templates"].grid(row=0, column=0, sticky="w")
    self.widget_text["available_fields"] = ttk.Label(report_body, style="FieldLabel.TLabel")
    self.widget_text["available_fields"].grid(row=0, column=1, sticky="w", padx=(12, 0))
    self.widget_text["selected_fields"] = ttk.Label(report_body, style="FieldLabel.TLabel")
    self.widget_text["selected_fields"].grid(row=0, column=3, sticky="w", padx=(12, 0))

    self.saved_templates_listbox = tk.Listbox(report_body, exportselection=False, height=18, width=22)
    self.saved_templates_listbox.grid(row=1, column=0, sticky="nsw", pady=(6, 0))
    self.saved_templates_listbox.bind("<<ListboxSelect>>", self.on_saved_template_selected)

    self.available_fields_listbox = tk.Listbox(report_body, exportselection=False, height=18)
    self.available_fields_listbox.grid(row=1, column=1, sticky="nsew", pady=(6, 0), padx=(12, 0))

    middle_buttons = ttk.Frame(report_body)
    middle_buttons.grid(row=1, column=2, sticky="ns", padx=12, pady=(6, 0))
    self.widget_text["add_field"] = ttk.Button(middle_buttons, command=self.add_report_field)
    self.widget_text["add_field"].pack(fill="x")
    self.widget_text["add_all_fields"] = ttk.Button(middle_buttons, command=self.add_all_report_fields)
    self.widget_text["add_all_fields"].pack(fill="x", pady=(8, 0))
    self.widget_text["remove_field"] = ttk.Button(middle_buttons, command=self.remove_report_field)
    self.widget_text["remove_field"].pack(fill="x", pady=(16, 0))
    self.widget_text["remove_all_fields"] = ttk.Button(middle_buttons, command=self.remove_all_report_fields)
    self.widget_text["remove_all_fields"].pack(fill="x", pady=(8, 0))
    self.widget_text["move_up"] = ttk.Button(middle_buttons, command=self.move_report_field_up)
    self.widget_text["move_up"].pack(fill="x", pady=(24, 0))
    self.widget_text["move_down"] = ttk.Button(middle_buttons, command=self.move_report_field_down)
    self.widget_text["move_down"].pack(fill="x", pady=(8, 0))

    self.selected_fields_listbox = tk.Listbox(report_body, exportselection=False, height=18)
    self.selected_fields_listbox.grid(row=1, column=3, sticky="nsew", pady=(6, 0), padx=(12, 0))

    report_actions = ttk.Frame(report_container)
    report_actions.grid(row=4, column=0, sticky="ew", pady=(12, 0))
    self.widget_text["report_target"] = ttk.Label(report_actions)
    self.widget_text["report_target"].pack(side="left")
    self.widget_text["report_scope_label"] = ttk.Label(report_actions)
    self.widget_text["report_scope_label"].pack(side="left", padx=(18, 6))
    self.report_scope_combo = ttk.Combobox(report_actions, textvariable=self.report_scope_var, state="readonly", width=18)
    self.report_scope_combo.pack(side="left")
    self.report_scope_combo.bind("<<ComboboxSelected>>", self.on_report_scope_changed)
    self.widget_text["report_current_view"] = ttk.Label(report_actions, style="Muted.TLabel")
    self.widget_text["report_current_view"].pack(side="left", padx=(18, 0))
    self.widget_text["export_combine_button"] = ttk.Button(report_actions, command=self.export_combined_report)
    self.widget_text["export_combine_button"].pack(side="right", padx=(0, 8))
    self.register_permission_widget("action.tool.report_builder", self.widget_text["export_combine_button"])
    self.widget_text["save_template"] = ttk.Button(report_actions, command=self.save_report_template_action)
    self.widget_text["save_template"].pack(side="right")
    self.register_permission_widget("action.tool.report_builder", self.widget_text["save_template"])
    self.widget_text["reset_template"] = ttk.Button(report_actions, command=self.reset_report_template)
    self.widget_text["reset_template"].pack(side="right", padx=(0, 8))
    self.register_permission_widget("action.tool.report_builder", self.widget_text["reset_template"])

    tech_pack_container = ttk.Frame(parent)
    tech_pack_container.grid(row=0, column=0, sticky="nsew")
    tech_pack_container.columnconfigure(0, weight=1)
    tech_pack_container.rowconfigure(2, weight=1)
    self.tool_download_tech_pack_container = tech_pack_container

    tech_pack_toolbar = ttk.Frame(tech_pack_container)
    tech_pack_toolbar.grid(row=0, column=0, sticky="ew", pady=(0, 10))
    self.widget_text["tool_download_tech_pack_back_button"] = ttk.Button(
        tech_pack_toolbar,
        command=self.show_tool_home,
    )
    self.widget_text["tool_download_tech_pack_back_button"].pack(side="left")

    tech_pack_header = ttk.Frame(tech_pack_container)
    tech_pack_header.grid(row=1, column=0, sticky="ew")
    self.widget_text["download_tech_pack_title"] = ttk.Label(tech_pack_header, style="Section.TLabel")
    self.widget_text["download_tech_pack_title"].pack(anchor="w")
    self.widget_text["download_tech_pack_subtitle"] = ttk.Label(
        tech_pack_header,
        style="Muted.TLabel",
        wraplength=980,
        justify="left",
    )
    self.widget_text["download_tech_pack_subtitle"].pack(anchor="w", pady=(4, 0))

    form_container = ttk.Frame(tech_pack_container)
    form_container.grid(row=2, column=0, sticky="nsew", pady=(12, 0))
    form_container.columnconfigure(0, weight=3)
    form_container.columnconfigure(1, weight=0)
    form_container.columnconfigure(2, weight=2)
    form_container.rowconfigure(0, weight=1)

    tech_pack_form = ttk.Frame(form_container)
    tech_pack_form.grid(row=0, column=0, sticky="nsew", padx=(0, 12))
    tech_pack_form.columnconfigure(0, weight=1)

    self.widget_text["download_tech_pack_user_id_label"] = ttk.Label(tech_pack_form)
    self.widget_text["download_tech_pack_user_id_label"].grid(row=0, column=0, sticky="w")
    self.download_tech_pack_user_entry = ttk.Entry(
        tech_pack_form,
        textvariable=self.download_tech_pack_user_var,
        width=42,
    )
    self.download_tech_pack_user_entry.grid(row=1, column=0, sticky="ew", pady=(4, 12))
    self.download_tech_pack_user_entry.bind("<KeyRelease>", self.on_download_tech_pack_form_changed)

    self.widget_text["download_tech_pack_password_label"] = ttk.Label(tech_pack_form)
    self.widget_text["download_tech_pack_password_label"].grid(row=2, column=0, sticky="w")
    password_row = ttk.Frame(tech_pack_form)
    password_row.grid(row=3, column=0, sticky="ew", pady=(4, 12))
    password_row.columnconfigure(0, weight=1)
    self.download_tech_pack_password_entry = ttk.Entry(
        password_row,
        textvariable=self.download_tech_pack_password_var,
        show="*",
        width=36,
    )
    self.download_tech_pack_password_entry.grid(row=0, column=0, sticky="ew")
    self.download_tech_pack_password_entry.bind("<KeyRelease>", self.on_download_tech_pack_form_changed)
    self.widget_text["download_tech_pack_show_password"] = ttk.Checkbutton(
        password_row,
        variable=self.download_tech_pack_show_password_var,
        command=self.toggle_download_tech_pack_password,
    )
    self.widget_text["download_tech_pack_show_password"].grid(row=0, column=1, sticky="w", padx=(8, 0))

    self.widget_text["download_tech_pack_style_list_label"] = ttk.Label(tech_pack_form)
    self.widget_text["download_tech_pack_style_list_label"].grid(row=4, column=0, sticky="w")
    self.widget_text["download_tech_pack_style_hint"] = ttk.Label(
        tech_pack_form,
        style="Muted.TLabel",
        justify="left",
    )
    self.widget_text["download_tech_pack_style_hint"].grid(row=5, column=0, sticky="w", pady=(0, 4))
    self.download_tech_pack_styles_text = tk.Text(
        tech_pack_form,
        height=8,
        wrap="word",
        font=("Segoe UI", 10),
    )
    self.download_tech_pack_styles_text.grid(row=6, column=0, sticky="ew", pady=(0, 12))
    self.download_tech_pack_styles_text.bind("<KeyRelease>", self.on_download_tech_pack_form_changed)

    self.widget_text["download_tech_pack_download_folder_label"] = ttk.Label(tech_pack_form)
    self.widget_text["download_tech_pack_download_folder_label"].grid(row=7, column=0, sticky="w")
    self.widget_text["download_tech_pack_download_folder_hint"] = ttk.Label(
        tech_pack_form,
        style="Muted.TLabel",
        justify="left",
    )
    self.widget_text["download_tech_pack_download_folder_hint"].grid(row=8, column=0, sticky="w", pady=(0, 4))
    download_folder_row = ttk.Frame(tech_pack_form)
    download_folder_row.grid(row=9, column=0, sticky="ew", pady=(0, 12))
    download_folder_row.columnconfigure(0, weight=1)
    self.download_tech_pack_download_folder_entry = ttk.Entry(
        download_folder_row,
        textvariable=self.download_tech_pack_download_folder_var,
    )
    self.download_tech_pack_download_folder_entry.grid(row=0, column=0, sticky="ew")
    self.download_tech_pack_download_folder_entry.bind("<KeyRelease>", self.on_download_tech_pack_form_changed)
    self.widget_text["download_tech_pack_browse_folder"] = ttk.Button(
        download_folder_row,
        command=self.browse_download_tech_pack_folder,
    )
    self.widget_text["download_tech_pack_browse_folder"].grid(row=0, column=1, padx=(8, 0))

    self.widget_text["download_tech_pack_headless"] = ttk.Checkbutton(
        tech_pack_form,
        variable=self.download_tech_pack_headless_var,
        command=self.persist_download_tech_pack_settings,
    )
    self.widget_text["download_tech_pack_headless"].grid(row=10, column=0, sticky="w", pady=2)
    self.widget_text["download_tech_pack_debug"] = ttk.Checkbutton(
        tech_pack_form,
        variable=self.download_tech_pack_debug_var,
        command=self.persist_download_tech_pack_settings,
    )
    self.widget_text["download_tech_pack_debug"].grid(row=11, column=0, sticky="w", pady=2)
    self.widget_text["download_tech_pack_pattern_zip"] = ttk.Checkbutton(
        tech_pack_form,
        variable=self.download_tech_pack_pattern_zip_var,
        command=self.persist_download_tech_pack_settings,
    )
    self.widget_text["download_tech_pack_pattern_zip"].grid(row=12, column=0, sticky="w", pady=2)
    self.widget_text["download_tech_pack_comment"] = ttk.Checkbutton(
        tech_pack_form,
        variable=self.download_tech_pack_comment_var,
        command=self.persist_download_tech_pack_settings,
    )
    self.widget_text["download_tech_pack_comment"].grid(row=13, column=0, sticky="w", pady=(2, 14))

    action_row = ttk.Frame(tech_pack_form)
    action_row.grid(row=14, column=0, sticky="ew", pady=(0, 14))
    self.widget_text["download_tech_pack_start_button"] = ttk.Button(
        action_row,
        command=self.start_download_tech_pack,
    )
    self.widget_text["download_tech_pack_start_button"].pack(side="left", fill="x", expand=True, ipady=6)
    self.register_permission_widget("action.tool.download_tech_pack", self.widget_text["download_tech_pack_start_button"])
    self.widget_text["download_tech_pack_checkpoint_button"] = ttk.Button(
        action_row,
        command=self.save_download_tech_pack_checkpoint,
    )
    self.widget_text["download_tech_pack_checkpoint_button"].pack(side="left", padx=(10, 0), ipady=6)
    self.register_permission_widget("action.tool.download_tech_pack", self.widget_text["download_tech_pack_checkpoint_button"])

    ttk.Separator(form_container, orient="vertical").grid(row=0, column=1, sticky="ns", padx=(0, 12))

    log_area = ttk.Frame(form_container)
    log_area.grid(row=0, column=2, sticky="nsew")
    log_area.columnconfigure(0, weight=1)
    log_area.rowconfigure(2, weight=1)
    self.widget_text["download_tech_pack_log_title"] = ttk.Label(log_area)
    self.widget_text["download_tech_pack_log_title"].grid(row=0, column=0, sticky="w")
    self.widget_text["download_tech_pack_log_hint"] = ttk.Label(
        log_area,
        style="Muted.TLabel",
        justify="left",
    )
    self.widget_text["download_tech_pack_log_hint"].grid(row=1, column=0, sticky="w", pady=(0, 4))
    self.download_tech_pack_log = scrolledtext.ScrolledText(
        log_area,
        height=28,
        wrap="none",
        state="disabled",
        font=("Consolas", 10),
    )
    self.download_tech_pack_log.grid(row=2, column=0, sticky="nsew", pady=(6, 0))

    self.load_download_tech_pack_settings_into_form()
    self.append_download_tech_pack_log(self.t("download_tech_pack_ready"))

    if self.tool_view_key == "report_builder":
        self.show_tool_report_builder()
    elif self.tool_view_key == "download_tech_pack":
        self.show_tool_download_tech_pack()
    else:
        self.show_tool_home()


def report_scope_label(self, scope: str) -> str:
    if scope == "costing":
        key = "report_scope_costing"
    elif scope == "bom":
        key = "report_scope_bom"
    elif scope == "samples":
        key = "report_scope_samples"
    elif scope == "tech_pack":
        key = "report_scope_tech_pack"
    elif scope == "all":
        key = "report_scope_all"
    else:
        key = "report_scope_purchase_order"
    return self.t(key)


def report_scope_display_values(self) -> list[str]:
    return [
        self.report_scope_label("purchase_order"),
        self.report_scope_label("costing"),
        self.report_scope_label("bom"),
        self.report_scope_label("samples"),
        self.report_scope_label("tech_pack"),
        self.report_scope_label("all"),
    ]


def set_report_scope(self, scope: str) -> None:
    if scope not in REPORT_FIELDS_BY_SCOPE_REF:
        scope = "purchase_order"
    self.report_templates[self.report_scope_key] = list(self.report_selected_fields)
    self.report_scope_key = scope
    self.report_selected_fields = list(self.report_templates.get(scope, self.default_report_fields(scope)))
    if hasattr(self, "report_scope_var"):
        self.report_scope_var.set(self.report_scope_label(scope))
    if "report_subtitle" in self.widget_text:
        subtitle_key = "report_subtitle"
        if scope == "costing":
            subtitle_key = "report_subtitle_costing"
        elif scope == "bom":
            subtitle_key = "report_subtitle_bom"
        elif scope == "samples":
            subtitle_key = "report_subtitle_samples"
        elif scope == "tech_pack":
            subtitle_key = "report_subtitle_tech_pack"
        elif scope == "all":
            subtitle_key = "report_subtitle_all"
        self.widget_text["report_subtitle"].configure(text=self.t(subtitle_key))
    self.refresh_report_listboxes()
    self.refresh_saved_templates_listbox()
    self.update_report_target_summary()


def show_tool_home(self) -> None:
    self.tool_view_key = "home"
    if hasattr(self, "tool_report_container"):
        self.tool_report_container.grid_remove()
    if hasattr(self, "tool_download_tech_pack_container"):
        self.tool_download_tech_pack_container.grid_remove()
    if hasattr(self, "tool_home_frame"):
        self.tool_home_frame.grid()


def show_tool_report_builder(self) -> None:
    if not self.is_action_allowed("action.tool.report_builder"):
        return
    self.tool_view_key = "report_builder"
    if hasattr(self, "tool_home_frame"):
        self.tool_home_frame.grid_remove()
    if hasattr(self, "tool_download_tech_pack_container"):
        self.tool_download_tech_pack_container.grid_remove()
    if hasattr(self, "tool_report_container"):
        self.tool_report_container.grid()
    self.update_report_target_summary()


def show_tool_download_tech_pack(self) -> None:
    if not self.is_action_allowed("action.tool.download_tech_pack"):
        return
    self.tool_view_key = "download_tech_pack"
    if hasattr(self, "tool_home_frame"):
        self.tool_home_frame.grid_remove()
    if hasattr(self, "tool_report_container"):
        self.tool_report_container.grid_remove()
    if hasattr(self, "tool_download_tech_pack_container"):
        self.tool_download_tech_pack_container.grid()


def update_report_target_summary(self) -> None:
    if "report_current_view" not in self.widget_text:
        return
    po_selected_count = len(getattr(self, "checked_order_ids", set()))
    costing_selected_count = len(getattr(self, "checked_costing_keys", set()))
    samples_selected_count = len(getattr(self, "checked_sample_ids", set()))
    tech_pack_selected_count = len(getattr(self, "checked_tech_pack_styles", set()))
    bom_selected_count = len(getattr(self, "checked_bom_item_ids", set()))
    if hasattr(self, "selected_po_count_var"):
        self.selected_po_count_var.set(self.t("selected_po_count", count=po_selected_count))
    if hasattr(self, "selected_costing_count_var"):
        self.selected_costing_count_var.set(self.t("selected_costing_count", count=costing_selected_count))
    if hasattr(self, "selected_tech_pack_count_var"):
        self.selected_tech_pack_count_var.set(self.t("selected_tech_pack_count", count=tech_pack_selected_count))
    if self.report_scope_key == "bom":
        count = len(getattr(self, "filtered_bom_items", []))
        self.widget_text["report_current_view"].configure(text=self.t("report_current_view_bom_count", count=count))
        self.widget_text["export_combine_button"].pack_forget()
        self.widget_text["export_combine_button"].configure(state="disabled")
        return
    if self.report_scope_key == "costing":
        count = len(getattr(self, "filtered_costing_items", []))
        self.widget_text["report_current_view"].configure(text=self.t("report_current_view_costing_count", count=count))
        self.widget_text["export_combine_button"].pack_forget()
        self.widget_text["export_combine_button"].configure(state="disabled")
        return
    if self.report_scope_key == "samples":
        count = len(getattr(self, "filtered_sample_rows", []))
        self.widget_text["report_current_view"].configure(text=self.t("report_current_view_samples_count", count=count))
        self.widget_text["export_combine_button"].pack_forget()
        self.widget_text["export_combine_button"].configure(state="disabled")
        return
    if self.report_scope_key == "tech_pack":
        count = len(getattr(self, "filtered_tech_pack_rows", []))
        self.widget_text["report_current_view"].configure(text=self.t("report_current_view_tech_pack_count", count=count))
        self.widget_text["export_combine_button"].pack_forget()
        self.widget_text["export_combine_button"].configure(state="disabled")
        return
    if self.report_scope_key == "all":
        self.widget_text["report_current_view"].configure(text=self.t("report_current_view_all"))
        self.widget_text["export_combine_button"].pack(side="right", padx=(0, 8))
        has_sources = (po_selected_count + costing_selected_count + samples_selected_count + tech_pack_selected_count + bom_selected_count) > 0
        self.widget_text["export_combine_button"].configure(state="normal" if has_sources else "disabled")
        return
    count = len(getattr(self, "filtered_orders", []))
    self.widget_text["report_current_view"].configure(text=self.t("report_current_view_count", count=count))
    self.widget_text["export_combine_button"].pack_forget()
    self.widget_text["export_combine_button"].configure(state="disabled")


def on_report_scope_changed(self, _event: object) -> None:
    selected = self.report_scope_var.get()
    if selected == self.report_scope_label("costing"):
        scope = "costing"
    elif selected == self.report_scope_label("bom"):
        scope = "bom"
    elif selected == self.report_scope_label("samples"):
        scope = "samples"
    elif selected == self.report_scope_label("tech_pack"):
        scope = "tech_pack"
    elif selected == self.report_scope_label("all"):
        scope = "all"
    else:
        scope = "purchase_order"
    self.set_report_scope(scope)


def refresh_report_listboxes(self) -> None:
    if not hasattr(self, "available_fields_listbox"):
        return

    self.available_fields_listbox.delete(0, tk.END)
    self.selected_fields_listbox.delete(0, tk.END)

    selected_set = set(self.report_selected_fields)
    available_fields = [field_key for field_key, _ in REPORT_FIELDS_BY_SCOPE_REF[self.report_scope_key] if field_key not in selected_set]

    for field_key in available_fields:
        self.available_fields_listbox.insert(tk.END, self.report_field_label(field_key))
    for field_key in self.report_selected_fields:
        self.selected_fields_listbox.insert(tk.END, self.report_field_label(field_key))


def refresh_saved_templates_listbox(self) -> None:
    if not hasattr(self, "saved_templates_listbox"):
        return
    self.saved_templates_listbox.delete(0, tk.END)
    scopes = ["purchase_order", "costing", "bom", "samples", "tech_pack", "all"]
    selected_index = 0
    for index, scope in enumerate(scopes):
        label = self.report_scope_label(scope)
        self.saved_templates_listbox.insert(tk.END, label)
        if scope == self.report_scope_key:
            selected_index = index
    if scopes:
        self.saved_templates_listbox.selection_clear(0, tk.END)
        self.saved_templates_listbox.selection_set(selected_index)


def on_saved_template_selected(self, _event: object) -> None:
    if not hasattr(self, "saved_templates_listbox"):
        return
    selection = self.saved_templates_listbox.curselection()
    if not selection:
        return
    scopes = ["purchase_order", "costing", "bom", "samples", "tech_pack", "all"]
    index = selection[0]
    if 0 <= index < len(scopes):
        self.set_report_scope(scopes[index])


def available_field_keys(self) -> list[str]:
    selected_set = set(self.report_selected_fields)
    return [field_key for field_key, _ in REPORT_FIELDS_BY_SCOPE_REF[self.report_scope_key] if field_key not in selected_set]


def add_report_field(self) -> None:
    selection = self.available_fields_listbox.curselection()
    if not selection:
        return
    available_keys = self.available_field_keys()
    field_key = available_keys[selection[0]]
    self.report_selected_fields.append(field_key)
    self.refresh_report_listboxes()
    self.selected_fields_listbox.selection_set(tk.END)


def add_all_report_fields(self) -> None:
    available_keys = self.available_field_keys()
    if not available_keys:
        return
    self.report_selected_fields.extend(available_keys)
    self.refresh_report_listboxes()
    if self.report_selected_fields:
        self.selected_fields_listbox.selection_clear(0, tk.END)
        self.selected_fields_listbox.selection_set(tk.END)


def remove_report_field(self) -> None:
    selection = self.selected_fields_listbox.curselection()
    if not selection:
        return
    index = selection[0]
    self.report_selected_fields.pop(index)
    self.refresh_report_listboxes()


def remove_all_report_fields(self) -> None:
    if not self.report_selected_fields:
        return
    self.report_selected_fields = []
    self.refresh_report_listboxes()


def move_report_field_up(self) -> None:
    selection = self.selected_fields_listbox.curselection()
    if not selection or selection[0] == 0:
        return
    index = selection[0]
    self.report_selected_fields[index - 1], self.report_selected_fields[index] = (
        self.report_selected_fields[index],
        self.report_selected_fields[index - 1],
    )
    self.refresh_report_listboxes()
    self.selected_fields_listbox.selection_set(index - 1)


def move_report_field_down(self) -> None:
    selection = self.selected_fields_listbox.curselection()
    if not selection or selection[0] >= len(self.report_selected_fields) - 1:
        return
    index = selection[0]
    self.report_selected_fields[index + 1], self.report_selected_fields[index] = (
        self.report_selected_fields[index],
        self.report_selected_fields[index + 1],
    )
    self.refresh_report_listboxes()
    self.selected_fields_listbox.selection_set(index + 1)


def save_report_template_action(self) -> None:
    self.persist_report_template()
    self.refresh_saved_templates_listbox()
    messagebox.showinfo(self.t("report_saved_title"), self.t("report_saved_message"))


def reset_report_template(self) -> None:
    self.report_selected_fields = self.default_report_fields(self.report_scope_key)
    self.persist_report_template()
    self.refresh_report_listboxes()
    self.refresh_saved_templates_listbox()
    messagebox.showinfo(self.t("report_reset_title"), self.t("report_reset_message"))


def bind_report_builder_module(app_cls: type, report_fields_by_scope: dict[str, list[tuple[str, str]]]) -> None:
    global REPORT_FIELDS_BY_SCOPE_REF
    REPORT_FIELDS_BY_SCOPE_REF = report_fields_by_scope
    method_map = {
        "_build_report_builder_module": _build_report_builder_module,
        "report_scope_label": report_scope_label,
        "report_scope_display_values": report_scope_display_values,
        "set_report_scope": set_report_scope,
        "show_tool_home": show_tool_home,
        "show_tool_report_builder": show_tool_report_builder,
        "show_tool_download_tech_pack": show_tool_download_tech_pack,
        "update_report_target_summary": update_report_target_summary,
        "on_report_scope_changed": on_report_scope_changed,
        "refresh_report_listboxes": refresh_report_listboxes,
        "refresh_saved_templates_listbox": refresh_saved_templates_listbox,
        "on_saved_template_selected": on_saved_template_selected,
        "available_field_keys": available_field_keys,
        "add_report_field": add_report_field,
        "add_all_report_fields": add_all_report_fields,
        "remove_report_field": remove_report_field,
        "remove_all_report_fields": remove_all_report_fields,
        "move_report_field_up": move_report_field_up,
        "move_report_field_down": move_report_field_down,
        "save_report_template_action": save_report_template_action,
        "reset_report_template": reset_report_template,
    }
    for method_name, function in method_map.items():
        setattr(app_cls, method_name, function)
