#!/usr/bin/env python3

"""
Tests for the frontend_tkinter_template_overview.py file.

This file is part of ArduPilot Methodic Configurator. https://github.com/ArduPilot/MethodicConfigurator

SPDX-FileCopyrightText: 2024-2026 Amilcar do Carmo Lucas <amilcar.lucas@iav.de>

SPDX-License-Identifier: GPL-3.0-or-later
"""

import tkinter as tk
from collections.abc import Generator
from tkinter import ttk
from unittest.mock import ANY, MagicMock, patch

import pytest

from ardupilot_methodic_configurator.frontend_tkinter_base_window import BaseWindow
from ardupilot_methodic_configurator.frontend_tkinter_template_overview import (
    TemplateOverviewWindow,
)

# pylint: disable=protected-access,redefined-outer-name,unused-argument


@pytest.fixture
def template_overview_window_setup() -> Generator[None, None, None]:
    """Fixture providing a properly mocked TemplateOverviewWindow for behavior testing."""
    with (
        patch("tkinter.Toplevel"),
        patch.object(BaseWindow, "__init__", return_value=None),
        patch.object(TemplateOverviewWindow, "_configure_window"),
        patch.object(TemplateOverviewWindow, "_initialize_ui_components"),
        patch.object(TemplateOverviewWindow, "_setup_layout"),
        patch.object(TemplateOverviewWindow, "_configure_treeview"),
        patch.object(TemplateOverviewWindow, "_bind_events"),
    ):
        yield


@pytest.fixture
def mock_vehicle_provider() -> MagicMock:
    """Fixture providing a mock vehicle components provider with common test data."""
    provider = MagicMock()

    # Create properly structured mock templates
    template_1 = MagicMock()
    template_1.attributes.return_value = ["name", "fc", "gnss"]
    template_1.name = "QuadCopter X"
    template_1.fc = "Pixhawk 6C"
    template_1.gnss = "Here3+"

    template_2 = MagicMock()
    template_2.attributes.return_value = ["name", "fc"]
    template_2.name = "Plane"
    template_2.fc = "Cube Orange"
    template_2.gnss = ""  # This attribute doesn't exist but getattr will return "" as default

    provider.get_vehicle_components_overviews.return_value = {
        "Copter/QuadX": template_1,
        "Plane/FixedWing": template_2,
    }
    provider.get_vehicle_image_filepath.return_value = "/mock/path/image.jpg"
    return provider


@pytest.fixture
def mock_program_provider() -> MagicMock:
    """Fixture providing a mock program settings provider."""
    return MagicMock()


@pytest.fixture
def vehicle_filtering_templates() -> dict[str, MagicMock]:
    """Fixture providing mock templates for vehicle filtering tests."""
    return {
        "Copter/QuadX": MagicMock(attributes=lambda: ["name"], name="QuadCopter"),
        "Plane/FixedWing": MagicMock(attributes=lambda: ["name"], name="Airplane"),
        "Copter/HexaX": MagicMock(attributes=lambda: ["name"], name="Hexacopter"),
        "Rover/SkidSteer": MagicMock(attributes=lambda: ["name"], name="Rover"),
        "ArduCopter/QuadX": MagicMock(attributes=lambda: ["name"], name="ArduCopter QuadX"),
        "ArduPlane/FixedWing": MagicMock(attributes=lambda: ["name"], name="ArduPlane"),
        "Custom/Copter/Experimental": MagicMock(attributes=lambda: ["name"], name="Experimental Copter"),
    }


@pytest.fixture
def vehicle_window(mock_vehicle_provider, mock_program_provider, vehicle_filtering_templates) -> TemplateOverviewWindow:
    """Fixture providing a configured window for vehicle filtering tests."""
    mock_vehicle_provider.get_vehicle_components_overviews.return_value = vehicle_filtering_templates

    with (
        patch("tkinter.Toplevel"),
        patch.object(BaseWindow, "__init__", return_value=None),
        patch.object(TemplateOverviewWindow, "_configure_window"),
        patch.object(TemplateOverviewWindow, "_initialize_ui_components"),
        patch.object(TemplateOverviewWindow, "_setup_layout"),
        patch.object(TemplateOverviewWindow, "_configure_treeview"),
        patch.object(TemplateOverviewWindow, "_bind_events"),
    ):
        window = TemplateOverviewWindow(
            vehicle_components_provider=mock_vehicle_provider,
            program_settings_provider=mock_program_provider,
            connected_fc_vehicle_type="ArduCopter",  # Default vehicle type for filtering tests
        )
        window.root = MagicMock()
        window.tree = MagicMock()
        return window


@pytest.fixture
def template_window(mock_vehicle_provider, mock_program_provider) -> TemplateOverviewWindow:
    """Fixture providing a configured TemplateOverviewWindow for behavior testing."""
    with (
        patch("tkinter.Toplevel"),
        patch.object(BaseWindow, "__init__", return_value=None),
        patch.object(TemplateOverviewWindow, "_configure_window"),
        patch.object(TemplateOverviewWindow, "_initialize_ui_components"),
        patch.object(TemplateOverviewWindow, "_setup_layout"),
        patch.object(TemplateOverviewWindow, "_configure_treeview"),
        patch.object(TemplateOverviewWindow, "_bind_events"),
    ):
        window = TemplateOverviewWindow(
            vehicle_components_provider=mock_vehicle_provider,
            program_settings_provider=mock_program_provider,
            connected_fc_vehicle_type=None,  # No filtering for most tests
        )
        # Mock essential UI components that tests actually interact with
        window.root = MagicMock()
        window.tree = MagicMock()
        return window


class TestUserTemplateSelection:
    """Test user stories around template selection behavior."""

    def test_user_can_select_template_by_double_clicking(self, template_window) -> None:
        """
        User can select a template by double-clicking on it.

        GIVEN: A user is viewing the template overview window
        WHEN: They double-click on a template row
        THEN: The selected template should be stored for use
        AND: The window should close
        """
        # Arrange: Configure mock responses for double-click behavior
        template_window.tree.identify_row.return_value = "item_1"
        template_window.tree.item.return_value = {"text": "Copter/QuadCopter"}

        mock_event = MagicMock(y=100)

        # Act: User double-clicks
        template_window._on_row_double_click(mock_event)

        # Assert: Template is stored and window closes
        template_window.program_settings_provider.store_template_dir.assert_called_once_with("Copter/QuadCopter")
        template_window.root.destroy.assert_called_once()

    def test_user_sees_vehicle_image_when_selecting_template(self, template_window) -> None:
        """
        When a template is highlighted or selected, the corresponding vehicle image should be displayed.

        GIVEN: A user is browsing templates
        WHEN: They click on a template with an available image
        THEN: The vehicle image should be displayed
        """
        # Arrange: Configure selection behavior
        template_window.tree.selection.return_value = ["item_1"]
        template_window.tree.item.return_value = {"text": "Copter/QuadX"}

        with patch.object(template_window, "_display_vehicle_image") as mock_display:
            mock_event = MagicMock()

            # Act: User selects template
            template_window._on_row_selection_change(mock_event)
            template_window._update_selection()  # Simulate the after callback

            # Assert: Image is displayed for selected template
            mock_display.assert_called_once_with("Copter/QuadX")

    def test_user_sees_fallback_message_when_no_vehicle_image_available(self, template_window) -> None:
        """
        When no vehicle image is available for a template, a helpful fallback message is shown.

        GIVEN: A user selects a template
        WHEN: No vehicle image is available for that template
        THEN: A helpful message should be displayed instead
        """
        # Arrange: Configure vehicle provider to return no image
        template_window.vehicle_components_provider.get_vehicle_image_filepath.return_value = ""
        template_window.tree.selection.return_value = ["item_1"]
        template_window.tree.item.return_value = {"text": "Experimental/NewDesign"}

        with patch.object(template_window, "_display_vehicle_image") as mock_display:
            mock_event = MagicMock()

            # Act: User selects template without image
            template_window._on_row_selection_change(mock_event)
            template_window._update_selection()

            # Assert: Display method is called (internal logic handles missing image gracefully)
            mock_display.assert_called_once_with("Experimental/NewDesign")


class TestTemplateDataDisplay:
    """Test how template data is presented to users."""

    def test_templates_are_populated_from_vehicle_components(self, template_window) -> None:
        """
        Test that templates are correctly populated from vehicle components.

        GIVEN: Vehicle templates exist in the system
        WHEN: The template overview window is opened
        THEN: All available templates should be displayed in the tree
        """
        # Act: Populate the tree with templates from our mock provider
        template_window._populate_treeview("")  # Empty string means no filtering

        # Assert: Both templates are added to tree with correct data
        assert template_window.tree.insert.call_count == 2

        # Check first template (Copter/QuadX)
        first_call = template_window.tree.insert.call_args_list[0]
        assert first_call[0] == ("", "end")
        assert first_call[1]["text"] == "Copter/QuadX"
        assert first_call[1]["values"] == ("Copter/QuadX", "QuadCopter X", "Pixhawk 6C", "Here3+")

        # Check second template (Plane/FixedWing)
        second_call = template_window.tree.insert.call_args_list[1]
        assert second_call[0] == ("", "end")
        assert second_call[1]["text"] == "Plane/FixedWing"
        assert second_call[1]["values"] == ("Plane/FixedWing", "Plane", "Cube Orange")

    def test_sorting_helps_users_find_templates(self, template_window) -> None:
        """
        Test that sorting templates by column helps users find what they need.

        GIVEN: Multiple templates are displayed
        WHEN: User clicks a column header to sort
        THEN: Templates should be reordered to help users find what they need
        """
        # Arrange: Mock tree data for sorting
        template_window.tree.get_children.return_value = ["item1", "item2", "item3"]
        template_window.tree.set.side_effect = lambda item, col: {
            ("item1", "Frame"): "QuadCopter",
            ("item2", "Frame"): "Airplane",
            ("item3", "Frame"): "Helicopter",
        }.get((item, col), "")

        # Act: User clicks on Frame column to sort
        template_window._sort_by_column("Frame", reverse=False)

        # Assert: Items are reordered (Airplane, Helicopter, QuadCopter)
        assert template_window.tree.move.call_count == 3
        # Verify ascending sort indicator is shown
        template_window.tree.heading.assert_called_with("Frame", command=ANY)


class TestAccessibilityAndUsability:
    """Test accessibility and usability features."""

    def test_window_scales_properly_for_high_dpi_displays(self, template_window) -> None:
        """
        Test that the template overview window scales properly on high-DPI displays.

        GIVEN: A user has a high-DPI display
        WHEN: The template overview window opens
        THEN: UI elements should be appropriately scaled for readability
        """
        # Arrange: Simulate high-DPI display
        template_window.dpi_scaling_factor = 2.0

        # Mock the geometry method to capture calls
        with patch.object(template_window.root, "geometry") as mock_geometry:
            # Act: Configure window for high-DPI
            template_window._configure_window()

            # Assert: Window size is scaled appropriately
            mock_geometry.assert_called_once()
            geometry_call = mock_geometry.call_args[0][0]
            # Should be 2400x1200 (1200x600 * 2.0 scaling)
            assert "2400x1200" in geometry_call

    def test_keyboard_navigation_works_for_accessibility(self, template_window) -> None:
        """
        Test that keyboard navigation works for users with accessibility needs.

        GIVEN: A user navigating with keyboard
        WHEN: They use up/down arrow keys
        THEN: The selection should update and show vehicle image
        """
        # Arrange: Set up selection return values
        template_window.tree.selection.return_value = ["new_item"]
        template_window.tree.item.return_value = {"text": "Plane/Glider"}

        with patch.object(template_window, "_display_vehicle_image") as mock_display:
            # Act: User presses down arrow key
            mock_event = MagicMock()
            template_window._on_row_selection_change(mock_event)

            # Trigger the delayed update
            template_window.root.after.assert_called_once_with(0, template_window._update_selection)

            # Simulate the after callback
            template_window._update_selection()

            # Assert: Image updates for new selection
            mock_display.assert_called_once_with("Plane/Glider")


class TestErrorHandlingAndEdgeCases:
    """Test system behavior in error conditions."""

    def test_graceful_handling_when_no_templates_available(self, template_overview_window_setup) -> None:
        """
        Test that the system handles no available templates gracefully.

        GIVEN: No templates are available in the system
        WHEN: User opens the template overview
        THEN: The system should handle this gracefully without crashing
        """
        # Arrange: Mock empty template data
        mock_vehicle_provider = MagicMock()
        mock_vehicle_provider.get_vehicle_components_overviews.return_value = {}

        window = TemplateOverviewWindow(vehicle_components_provider=mock_vehicle_provider)
        window.tree = MagicMock()

        # Act: Try to populate empty template list
        window._populate_treeview("")  # Empty string means no filtering

        # Assert: No crash, no tree items added
        window.tree.insert.assert_not_called()

    def test_window_closes_properly_on_user_cancel(self, template_window) -> None:
        """
        Test that the window closes properly when the user cancels the operation.

        GIVEN: A user has the template overview open
        WHEN: They close the window (cancel operation)
        THEN: Resources should be cleaned up properly
        """
        # Act: User closes window
        template_window.close_window()

        # Assert: Window is properly destroyed
        template_window.root.destroy.assert_called_once()


class TestUIComponentInitialization:
    """Test UI component initialization and responsive layout behavior."""

    def test_high_dpi_displays_get_properly_scaled_components(self, template_window) -> None:
        """
        Users on high-DPI displays should see properly scaled UI components.

        GIVEN: A user opens the template overview on a high-DPI display
        WHEN: UI components are initialized
        THEN: All components should be created with appropriate DPI scaling
        """
        # Arrange: Set high-DPI environment
        template_window.dpi_scaling_factor = 2.0
        template_window.calculate_scaled_font_size = MagicMock(return_value=24)

        # Act: Initialize UI components (already done by fixture, verify state)
        # Assert: Window has proper scaling setup
        assert hasattr(template_window, "dpi_scaling_factor")
        assert template_window.dpi_scaling_factor == 2.0

    def test_users_see_clear_instructions_for_template_selection(self, template_window) -> None:
        """
        Users should see clear, localized instructions for selecting templates.

        GIVEN: A user opens the template overview window
        WHEN: The instruction text is displayed
        THEN: Text should be clear, multilingual, and properly formatted
        """
        # Act: Get instruction text
        instruction_text = template_window._get_instruction_text()

        # Assert: Text contains helpful guidance
        assert "double-click" in instruction_text.lower()
        assert "template" in instruction_text.lower()
        assert "\n" in instruction_text  # Multi-line for readability

    def test_window_layout_adapts_to_different_screen_sizes(self, template_window) -> None:
        """
        Window layout should adapt gracefully to different screen sizes.

        GIVEN: A user opens the template overview on any screen size
        WHEN: The layout is configured
        THEN: Components should be positioned with responsive scaling
        """
        # Arrange: Set up variable DPI environment
        template_window.dpi_scaling_factor = 1.25
        template_window.calculate_scaled_padding_tuple = MagicMock(return_value=(15, 30))

        # Mock the necessary components for layout
        template_window.top_frame = MagicMock()
        template_window.instruction_label = MagicMock()
        template_window.image_label = MagicMock()
        template_window.tree_frame = MagicMock()
        template_window.tree = MagicMock()
        template_window.scrollbar = MagicMock()

        # Act: Test layout setup
        try:
            TemplateOverviewWindow._setup_layout(template_window)
            layout_success = True
        except Exception:  # pylint: disable=broad-exception-caught
            layout_success = False

        # Assert: Layout setup completes successfully
        assert layout_success, "Layout setup should complete without errors"
        assert hasattr(template_window, "top_frame")
        assert hasattr(template_window, "instruction_label")


class TestTreeviewConfiguration:
    """Test treeview configuration and user experience optimization."""

    def test_treeview_styling_adapts_to_user_display_settings(self, template_window) -> None:
        """
        Treeview styling should provide optimal readability on all displays.

        GIVEN: A user opens the template overview on any display
        WHEN: Treeview styling is configured
        THEN: Styling should be applied with proper DPI scaling for readability
        """
        # Arrange: Set up style environment
        template_window.calculate_scaled_padding = MagicMock(return_value=3)

        with patch("tkinter.ttk.Style") as mock_style_class:
            mock_style = MagicMock()
            mock_style_class.return_value = mock_style

            # Act: Apply treeview styling
            template_window._setup_treeview_style()

            # Assert: Style configuration provides good UX
            mock_style.layout.assert_called_once()
            mock_style.configure.assert_called_once()

    def test_treeview_columns_resize_for_content_readability(self, template_window) -> None:
        """
        Treeview columns should automatically size for optimal content readability.

        GIVEN: A populated treeview with varying content lengths
        WHEN: Column widths are adjusted
        THEN: Widths should accommodate content with appropriate DPI scaling
        """
        # Arrange: Set up mock font and treeview data
        with patch("tkinter.font.Font") as mock_font_class:
            mock_font = MagicMock()
            mock_font.measure.return_value = 100
            mock_font_class.return_value = mock_font

            # Configure template_window tree with test data
            template_window.tree.__getitem__.return_value = ["Frame", "Size"]
            template_window.tree.get_children.return_value = ["item1", "item2"]
            template_window.tree.item.side_effect = lambda item, key: {
                "values": ["QuadCopter", "Large"] if item == "item1" else ["Airplane", "Medium"]
            }.get(key, ["QuadCopter", "Large"])
            template_window.dpi_scaling_factor = 1.5

            # Act: Adjust column widths
            template_window._adjust_treeview_column_widths()

            # Assert: Columns are properly sized
            assert template_window.tree.column.call_count >= 2


class TestUserInteractionBehavior:
    """Test user interaction patterns and responsive behavior."""

    def test_user_selection_updates_provide_immediate_feedback(self, template_window) -> None:
        """
        Users should see immediate visual feedback when selecting templates.

        GIVEN: A user browses through different templates
        WHEN: They select a row in the treeview
        THEN: Their selection should be immediately stored and image updated
        """
        # Arrange: Set up selection behavior
        template_window.tree.selection.return_value = ["item1"]
        template_window.tree.item.return_value = {"text": "Copter/QuadCopter"}
        template_window.store_template_dir = MagicMock()
        template_window._display_vehicle_image = MagicMock()

        # Act: User clicks on a template
        mock_event = MagicMock()
        template_window._on_row_selection_change(mock_event)
        template_window._update_selection()  # Simulate the after callback

        # Assert: Immediate feedback provided
        template_window.store_template_dir.assert_called_once_with("Copter/QuadCopter")
        template_window._display_vehicle_image.assert_called_once_with("Copter/QuadCopter")

    def test_user_double_click_provides_quick_template_selection(self, template_window) -> None:
        """
        Users should be able to quickly select templates with double-click.

        GIVEN: A user has found their desired template
        WHEN: They double-click on the template row
        THEN: Template should be selected and window closed for efficient workflow
        """
        # Arrange: Set up double-click behavior
        template_window.tree.identify_row.return_value = "item1"
        template_window.tree.item.return_value = {"text": "Copter/QuadCopter"}
        template_window.store_template_dir = MagicMock()
        template_window.close_window = MagicMock()

        # Act: User double-clicks to select
        mock_event = MagicMock()
        mock_event.y = 100
        template_window._on_row_double_click(mock_event)

        # Assert: Quick selection workflow completed
        template_window.store_template_dir.assert_called_once_with("Copter/QuadCopter")
        template_window.close_window.assert_called_once()


class TestVisualFeedbackAndImageDisplay:  # pylint: disable=too-few-public-methods
    """Test visual feedback mechanisms for better user experience."""

    def test_missing_vehicle_images_handled_gracefully_for_users(self, template_window) -> None:
        """
        Users should experience graceful handling when vehicle images are missing.

        GIVEN: A user selects a template without an associated image
        WHEN: The system tries to display the vehicle image
        THEN: The missing image should be handled without disrupting user workflow
        """
        # Arrange: Template without image
        template_window.top_frame = MagicMock()
        template_window.top_frame.winfo_children.return_value = []
        template_window.image_label = MagicMock()
        template_window.dpi_scaling_factor = 1.0
        template_window.vehicle_components_provider.get_vehicle_image_filepath.return_value = "missing.png"
        template_window.get_vehicle_image_filepath = MagicMock(return_value="missing.png")

        # Act: User selects template without image
        template_window._display_vehicle_image("Copter/ExperimentalQuad")

        # Assert: Graceful handling without errors (method completes without exceptions)


class TestWindowInitialization:
    """Test comprehensive window initialization behavior."""

    def test_window_initialization_creates_all_required_components(self, template_overview_window_setup) -> None:
        """
        Window initialization should create all required UI components.

        GIVEN: A user opens the template overview window
        WHEN: Window initialization occurs
        THEN: All UI components should be properly created and configured
        """
        # Arrange: Mock all required components for full initialization
        with (
            patch("tkinter.ttk.Frame"),
            patch("tkinter.ttk.Label"),
            patch("tkinter.ttk.Treeview"),
        ):
            mock_vehicle_provider = MagicMock()
            mock_program_provider = MagicMock()

            # Act: Initialize window with real initialization call
            window = TemplateOverviewWindow(
                vehicle_components_provider=mock_vehicle_provider,
                program_settings_provider=mock_program_provider,
            )

            # Assert: Components are properly initialized
            assert window.vehicle_components_provider == mock_vehicle_provider
            assert window.program_settings_provider == mock_program_provider

    def test_ui_components_get_proper_dependency_injection(self, template_overview_window_setup) -> None:
        """
        UI components should receive proper dependency injection.

        GIVEN: A user provides custom providers
        WHEN: Window is initialized with dependency injection
        THEN: Custom providers should be used instead of defaults
        """
        # Arrange: Custom providers
        mock_vehicle_provider = MagicMock()
        mock_program_provider = MagicMock()

        # Act: Initialize with custom providers
        window = TemplateOverviewWindow(
            vehicle_components_provider=mock_vehicle_provider,
            program_settings_provider=mock_program_provider,
        )

        # Assert: Custom providers are used
        assert window.vehicle_components_provider is mock_vehicle_provider
        assert window.program_settings_provider is mock_program_provider

    def test_window_uses_default_providers_when_none_specified(self, template_overview_window_setup) -> None:
        """
        Window should use default providers when none are specified.

        GIVEN: A user doesn't specify custom providers
        WHEN: Window is initialized without providers
        THEN: Default providers should be used
        """
        # Arrange: Patch the default classes
        with (
            patch("ardupilot_methodic_configurator.frontend_tkinter_template_overview.VehicleComponents") as mock_vc,
            patch("ardupilot_methodic_configurator.frontend_tkinter_template_overview.ProgramSettings") as mock_ps,
        ):
            # Act: Initialize without providers
            window = TemplateOverviewWindow()

            # Assert: Default providers are used
            assert window.vehicle_components_provider is mock_vc
            assert window.program_settings_provider is mock_ps


class TestWindowApplicationRunner:
    """Test window application running behavior."""

    def test_toplevel_window_runs_with_update_loop(self, template_window) -> None:
        """
        Toplevel window should run with proper update loop.

        GIVEN: A window is created as Toplevel
        WHEN: run_app is called
        THEN: Update loop should run properly
        """
        # Arrange: Mock Toplevel window
        template_window.root = MagicMock(spec=tk.Toplevel)
        # Start with children, will be cleared to exit loop
        template_window.root.children = {"child1": MagicMock()}

        def clear_children_after_call() -> None:
            template_window.root.children = {}

        template_window.root.update_idletasks.side_effect = clear_children_after_call

        # Act: Run the app
        template_window.run_app()

        # Assert: Update methods were called
        template_window.root.update_idletasks.assert_called()

    def test_tk_window_runs_with_mainloop(self, template_window) -> None:
        """
        Tk window should run with mainloop.

        GIVEN: A window is created as Tk root
        WHEN: run_app is called
        THEN: Mainloop should be called
        """
        # Arrange: Mock Tk root window
        template_window.root = MagicMock(spec=tk.Tk)

        # Act: Run the app
        template_window.run_app()

        # Assert: Mainloop was called
        template_window.root.mainloop.assert_called_once()

    def test_tcl_error_handled_gracefully_in_toplevel_run(self, template_window) -> None:
        """
        TclError should be handled gracefully in Toplevel run.

        GIVEN: A Toplevel window encounters TclError
        WHEN: run_app is called and TclError occurs
        THEN: Error should be handled without crashing
        """
        # Arrange: Mock Toplevel with TclError
        template_window.root = MagicMock(spec=tk.Toplevel)
        template_window.root.children = {"child1": MagicMock()}
        template_window.root.update_idletasks.side_effect = tk.TclError("Test error")

        # Act: Run the app (should not raise exception)
        try:
            template_window.run_app()
            test_passed = True
        except tk.TclError:
            test_passed = False

        # Assert: Error was handled gracefully
        assert test_passed


class TestTreeviewConfigurationDetails:
    """Test detailed treeview configuration behavior."""

    def test_treeview_style_configuration_uses_proper_scaling(self, template_window) -> None:
        """
        Treeview style should use proper DPI scaling.

        GIVEN: A window with specific DPI scaling
        WHEN: Treeview style is configured
        THEN: Style should use appropriate scaling values
        """
        # Arrange: Set scaling and mock style
        template_window.dpi_scaling_factor = 1.5
        template_window.calculate_scaled_padding = MagicMock(return_value=15)

        with patch("tkinter.ttk.Style") as mock_style_class:
            mock_style = MagicMock()
            mock_style_class.return_value = mock_style

            # Act: Configure treeview style
            template_window._setup_treeview_style()

            # Assert: Style methods were called with scaling
            mock_style.layout.assert_called_once()
            mock_style.configure.assert_called_once()

    def test_treeview_columns_setup_creates_proper_headings(self, template_window) -> None:
        """
        Treeview columns should be set up with proper headings.

        GIVEN: A treeview needs column configuration
        WHEN: Columns are set up
        THEN: Proper headings should be created
        """
        # Arrange: Mock the tree columns directly
        template_window.tree = MagicMock()
        template_window.tree.__getitem__.return_value = ["Template", "Frame", "FC"]

        # Act: Setup treeview columns
        template_window._setup_treeview_columns()

        # Assert: Headings were configured for each column
        assert template_window.tree.heading.call_count == 3

    def test_event_binding_creates_all_required_handlers(self, template_window) -> None:
        """
        Event binding should create all required event handlers.

        GIVEN: A treeview needs event handling
        WHEN: Events are bound
        THEN: All required events should have handlers
        """
        # Arrange: Mock tree columns
        template_window.tree.__getitem__ = MagicMock(return_value=["Template", "Frame"])

        # Act: Bind events
        template_window._bind_events()

        # Assert: All required events are bound
        expected_events = ["<ButtonRelease-1>", "<Up>", "<Down>", "<Double-1>"]
        actual_bind_calls = [call[0][0] for call in template_window.tree.bind.call_args_list]
        for event in expected_events:
            assert event in actual_bind_calls


class TestSortingBehaviorDetails:
    """Test detailed sorting behavior."""

    def test_numeric_sorting_works_with_float_values(self, template_window) -> None:
        """
        Numeric sorting should work with float values.

        GIVEN: A treeview with numeric data
        WHEN: User sorts by numeric column
        THEN: Values should be sorted numerically, not alphabetically
        """
        # Arrange: Mock tree with numeric data
        template_window.tree.get_children.return_value = ["item1", "item2", "item3"]
        template_window.tree.set.side_effect = lambda item, col: {
            ("item1", "Size"): "1.5",
            ("item2", "Size"): "10.2",
            ("item3", "Size"): "2.0",
        }.get((item, col), "0")

        # Act: Sort by Size column
        template_window._sort_by_column("Size", reverse=False)

        # Assert: Items were moved (sorted)
        assert template_window.tree.move.call_count == 3

    def test_string_sorting_fallback_handles_non_numeric_data(self, template_window) -> None:
        """
        String sorting should work as fallback for non-numeric data.

        GIVEN: A treeview with non-numeric data
        WHEN: User sorts by text column
        THEN: Values should be sorted alphabetically
        """
        # Arrange: Mock tree with text data
        template_window.tree.get_children.return_value = ["item1", "item2"]
        template_window.tree.set.side_effect = lambda item, col: {
            ("item1", "Name"): "Zebra",
            ("item2", "Name"): "Alpha",
        }.get((item, col), "")

        # Act: Sort by Name column
        template_window._sort_by_column("Name", reverse=False)

        # Assert: String sorting was used (move was called)
        assert template_window.tree.move.call_count == 2

    def test_sorting_indicators_update_properly(self, template_window) -> None:
        """
        Sorting indicators should update properly.

        GIVEN: A user sorts by different columns
        WHEN: Sort direction changes
        THEN: Visual indicators should update correctly
        """
        # Arrange: Initial sort state
        template_window.sort_column = "OldColumn"
        template_window.tree.get_children.return_value = ["item1"]
        template_window.tree.set.return_value = "value"

        # Act: Sort by new column
        template_window._sort_by_column("NewColumn", reverse=False)

        # Assert: Old column indicator cleared, new column shows ascending
        template_window.tree.heading.assert_any_call("OldColumn", text="OldColumn")
        template_window.tree.heading.assert_any_call("NewColumn", text="NewColumn ▲")


class TestImageDisplayBehavior:
    """Test vehicle image display behavior."""

    def test_image_display_removes_previous_image_correctly(self, template_window) -> None:
        """
        Image display should remove previous image correctly.

        GIVEN: A previous image is displayed
        WHEN: A new template is selected
        THEN: Previous image should be removed before showing new one
        """
        # Arrange: Mock existing image widget and top_frame
        old_image_widget = MagicMock(spec=ttk.Label)
        template_window.image_label = old_image_widget
        template_window.top_frame = MagicMock()
        template_window.top_frame.winfo_children.return_value = [old_image_widget]
        template_window.dpi_scaling_factor = 1.0

        with (
            patch.object(template_window, "get_vehicle_image_filepath", return_value="/path/to/image.jpg"),
            patch.object(template_window, "put_image_in_label", return_value=MagicMock()),
        ):
            # Act: Display new image
            template_window._display_vehicle_image("Copter/NewTemplate")

            # Assert: Old image was destroyed
            old_image_widget.destroy.assert_called_once()

    def test_image_display_handles_file_not_found_gracefully(self, template_window) -> None:
        """
        Image display should handle FileNotFoundError gracefully.

        GIVEN: A template without an associated image
        WHEN: Image display is attempted
        THEN: A fallback message should be shown
        """
        # Arrange: Mock FileNotFoundError and top_frame
        template_window.top_frame = MagicMock()
        template_window.top_frame.winfo_children.return_value = []
        template_window.dpi_scaling_factor = 1.0

        with (
            patch.object(template_window, "get_vehicle_image_filepath", side_effect=FileNotFoundError),
            patch("tkinter.ttk.Label") as mock_label,
        ):
            # Act: Try to display image
            template_window._display_vehicle_image("Copter/NoImage")

            # Assert: Fallback label was created
            mock_label.assert_called_once()
            call_args = mock_label.call_args[1]
            assert "No 'vehicle.jpg'" in call_args["text"]

    def test_get_vehicle_image_filepath_delegates_to_provider(self, template_window) -> None:
        """
        get_vehicle_image_filepath should delegate to vehicle components provider.

        GIVEN: A template path
        WHEN: Vehicle image filepath is requested
        THEN: Request should be delegated to provider
        """
        # Arrange: Mock provider response
        template_window.vehicle_components_provider.get_vehicle_image_filepath.return_value = "/test/path.jpg"

        # Act: Get image filepath
        result = template_window.get_vehicle_image_filepath("Copter/Test")

        # Assert: Provider was called and result returned
        template_window.vehicle_components_provider.get_vehicle_image_filepath.assert_called_once_with("Copter/Test")
        assert result == "/test/path.jpg"


class TestVehicleTypeFiltering:
    """Test vehicle type filtering functionality."""

    def test_filtering_shows_only_matching_vehicle_templates(self, vehicle_window) -> None:
        """
        Test that vehicle filtering shows only templates matching the connected flight controller.

        GIVEN: Multiple templates exist for different vehicle types
        WHEN: User has a specific vehicle type connected (e.g., "ArduCopter")
        THEN: Only templates matching that vehicle type should be displayed
        """
        # Act: Populate treeview with Copter filtering
        vehicle_window._populate_treeview("ArduCopter")

        # Assert: Only ArduCopter templates are added
        # Should not include: Copter/QuadX, Copter/HexaX, Custom/Copter/Experimental
        assert vehicle_window.tree.insert.call_count == 1

        # Verify the calls contain only Copter templates
        call_args_list = vehicle_window.tree.insert.call_args_list
        inserted_keys = [call[1]["text"] for call in call_args_list]
        assert "Copter/QuadX" not in inserted_keys
        assert "Copter/HexaX" not in inserted_keys
        assert "ArduCopter/QuadX" in inserted_keys
        assert "Custom/Copter/Experimental" not in inserted_keys
        assert "Plane/FixedWing" not in inserted_keys
        assert "Rover/SkidSteer" not in inserted_keys

    def test_no_filtering_shows_all_templates(self, vehicle_window) -> None:
        """
        Test that passing empty/None vehicle type shows all available templates.

        GIVEN: Multiple templates exist for different vehicle types
        WHEN: No specific vehicle type is provided for filtering
        THEN: All available templates should be displayed
        """
        # Act: Populate treeview without filtering
        vehicle_window._populate_treeview("")

        # Assert: All templates are added
        assert vehicle_window.tree.insert.call_count == 7  # All seven templates

        # Verify all template types are included
        call_args_list = vehicle_window.tree.insert.call_args_list
        inserted_keys = [call[1]["text"] for call in call_args_list]
        assert "Copter/QuadX" in inserted_keys
        assert "Plane/FixedWing" in inserted_keys
        assert "Rover/SkidSteer" in inserted_keys
        assert "ArduCopter/QuadX" in inserted_keys

    def test_partial_vehicle_matching_works_correctly(self, vehicle_window) -> None:
        """
        Test that vehicle filtering works with partial string matching.

        GIVEN: Templates with vehicle types as part of their keys
        WHEN: A specific vehicle substring is used for filtering
        THEN: All templates containing that substring should be included
        """
        # Act: Filter by "ArduCopter" substring
        vehicle_window._populate_treeview("ArduCopter")

        # Assert: Only ArduCopter templates are included
        assert vehicle_window.tree.insert.call_count == 1

        call_args_list = vehicle_window.tree.insert.call_args_list
        inserted_keys = [call[1]["text"] for call in call_args_list]
        assert "ArduCopter/QuadX" in inserted_keys
        assert "Custom/Copter/Experimental" not in inserted_keys
        assert "Copter/QuadX" not in inserted_keys
        assert "ArduPlane/FixedWing" not in inserted_keys

    def test_constructor_passes_vehicle_type_to_configure_treeview(self, mock_vehicle_provider, mock_program_provider) -> None:
        """
        Test that the constructor properly passes vehicle type to the configuration methods.

        GIVEN: A TemplateOverviewWindow is created with a specific vehicle type
        WHEN: The constructor initializes the UI components
        THEN: The vehicle type should be passed down to the treeview configuration
        """
        with (
            patch("tkinter.Toplevel"),
            patch.object(BaseWindow, "__init__", return_value=None),
            patch.object(TemplateOverviewWindow, "_configure_window"),
            patch.object(TemplateOverviewWindow, "_initialize_ui_components"),
            patch.object(TemplateOverviewWindow, "_setup_layout"),
            patch.object(TemplateOverviewWindow, "_configure_treeview") as mock_configure_treeview,
            patch.object(TemplateOverviewWindow, "_bind_events"),
        ):
            # Act: Create window with specific vehicle type
            TemplateOverviewWindow(
                vehicle_components_provider=mock_vehicle_provider,
                program_settings_provider=mock_program_provider,
                connected_fc_vehicle_type="Plane",
            )

            # Assert: vehicle type is passed to configure_treeview
            mock_configure_treeview.assert_called_once_with("Plane")
