from __future__ import annotations

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

from app_core.theme import DEFAULT_DESKTOP_THEME, DESKTOP_THEME_FIELDS, normalize_desktop_theme


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"])
    self.widget_text["tool_adjust_theme_button"] = ttk.Button(
        tool_buttons,
        command=self.open_adjust_theme_dialog,
    )
    self.widget_text["tool_adjust_theme_button"].pack(side="left", padx=(10, 0))

    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 open_adjust_theme_dialog(self) -> None:
    if hasattr(self, "adjust_theme_dialog") and self.adjust_theme_dialog is not None:
        try:
            if self.adjust_theme_dialog.winfo_exists():
                self.adjust_theme_dialog.lift()
                self.adjust_theme_dialog.focus_force()
                return
        except tk.TclError:
            pass

    dialog = tk.Toplevel(self.root)
    dialog.title(self.t("theme_dialog_title"))
    dialog.transient(self.root)
    dialog.resizable(False, False)
    self.adjust_theme_dialog = dialog

    current_theme = normalize_desktop_theme(getattr(self, "desktop_theme", DEFAULT_DESKTOP_THEME))
    color_vars: dict[str, tk.StringVar] = {}
    swatches: dict[str, tk.Label] = {}

    frame = ttk.Frame(dialog, padding=18)
    frame.grid(row=0, column=0, sticky="nsew")
    frame.columnconfigure(1, weight=1)

    ttk.Label(frame, text=self.t("theme_dialog_title"), style="Section.TLabel").grid(row=0, column=0, columnspan=3, sticky="w")
    ttk.Label(frame, text=self.t("theme_dialog_subtitle"), style="Muted.TLabel", wraplength=560, justify="left").grid(
        row=1,
        column=0,
        columnspan=3,
        sticky="w",
        pady=(4, 14),
    )

    def set_swatch(key: str) -> None:
        value = color_vars[key].get().strip()
        normalized = normalize_desktop_theme({key: value})[key]
        color_vars[key].set(normalized)
        swatches[key].configure(bg=normalized)

    def choose_color(key: str) -> None:
        initial = color_vars[key].get().strip() or current_theme[key]
        _rgb, selected = colorchooser.askcolor(color=initial, parent=dialog, title=self.t("theme_choose_color"))
        if selected:
            color_vars[key].set(str(selected).lower())
            set_swatch(key)

    def collect_theme() -> dict[str, str]:
        return normalize_desktop_theme({key: var.get() for key, var in color_vars.items()})

    def apply_theme() -> None:
        theme = collect_theme()
        for key, value in theme.items():
            color_vars[key].set(value)
            swatches[key].configure(bg=value)
        self.apply_desktop_theme(theme)

    def save_theme() -> None:
        theme = collect_theme()
        self.apply_desktop_theme(theme, persist=True)
        self.set_status(self.t("theme_saved"))
        messagebox.showinfo(self.t("theme_dialog_title"), self.t("theme_saved"), parent=dialog)

    def reset_theme() -> None:
        for key, value in DEFAULT_DESKTOP_THEME.items():
            if key in color_vars:
                color_vars[key].set(value)
                swatches[key].configure(bg=value)
        self.apply_desktop_theme(DEFAULT_DESKTOP_THEME)

    for row_index, (key, label_key) in enumerate(DESKTOP_THEME_FIELDS, start=2):
        ttk.Label(frame, text=self.t(label_key)).grid(row=row_index, column=0, sticky="w", pady=4)
        var = tk.StringVar(value=current_theme[key])
        swatch = tk.Label(
            frame,
            width=18,
            height=1,
            relief="solid",
            bd=1,
            bg=current_theme[key],
            cursor="hand2",
        )
        swatch.grid(row=row_index, column=1, sticky="ew", padx=(16, 8), pady=4, ipady=6)
        swatch.bind("<Button-1>", lambda _event, color_key=key: choose_color(color_key))
        ttk.Button(frame, text=self.t("theme_choose_color"), command=lambda color_key=key: choose_color(color_key)).grid(
            row=row_index,
            column=2,
            sticky="w",
            padx=(8, 0),
            pady=4,
        )
        color_vars[key] = var
        swatches[key] = swatch

    actions = ttk.Frame(frame)
    actions.grid(row=2 + len(DESKTOP_THEME_FIELDS), column=0, columnspan=3, sticky="ew", pady=(16, 0))
    ttk.Button(actions, text=self.t("theme_apply"), command=apply_theme).pack(side="left")
    ttk.Button(actions, text=self.t("theme_save"), command=save_theme).pack(side="left", padx=(8, 0))
    ttk.Button(actions, text=self.t("theme_reset"), command=reset_theme).pack(side="left", padx=(8, 0))
    ttk.Button(actions, text=self.t("close"), command=dialog.destroy).pack(side="right")

    dialog.protocol("WM_DELETE_WINDOW", dialog.destroy)
    self.center_dialog(dialog, min_width=560, min_height=520)


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,
        "open_adjust_theme_dialog": open_adjust_theme_dialog,
        "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)
