# FlightMonitor/controller/map_command_handler.py
"""
Handles map-related commands and information updates, delegating to MapCanvasManager
and updating MainWindow's map info panels.
"""
import tkinter as tk # For TkinterError in specific callbacks, though ideally abstracted
import math # For geographic calculations, e.g., in deg_to_dms_string
from typing import Optional, Dict, Any, Tuple, TYPE_CHECKING

from flightmonitor.utils.logger import get_logger
from flightmonitor.data import config as app_config # For map size decimal places, etc.
from flightmonitor.map.map_utils import _is_valid_bbox_dict, deg_to_dms_string, calculate_geographic_bbox_size_km, calculate_meters_per_pixel, calculate_zoom_level_for_geographic_size, get_bounding_box_from_center_size

# Type checking imports to avoid circular dependencies at runtime
if TYPE_CHECKING:
    from flightmonitor.controller.app_controller import AppController # For type hinting AppController
    from flightmonitor.gui.main_window import MainWindow
    from flightmonitor.map.map_canvas_manager import MapCanvasManager

module_logger = get_logger(__name__)

DEFAULT_CLICK_AREA_SIZE_KM = 50.0 # Default for context menu 'set bbox'


class MapCommandHandler:
    """
    Manages all map-related commands (zoom, pan, recenter, set bbox) and
    updates to the map information display in the GUI.
    It acts as an intermediary between the AppController and the MapCanvasManager/MainWindow.
    """

    def __init__(self, app_controller: "AppController"):
        """
        Initializes the MapCommandHandler.

        Args:
            app_controller: The main AppController instance. This handler accesses
                            MainWindow and MapCanvasManager via the controller.
        """
        self.app_controller = app_controller
        module_logger.debug("MapCommandHandler initialized.")

    def on_map_left_click(self, latitude: float, longitude: float, canvas_x: int, canvas_y: int, screen_x: int, screen_y: int):
        """
        Handles a left-click event on the map canvas.
        Updates the clicked map info panel and attempts to identify/select a flight.
        """
        module_logger.debug(f"MapCommandHandler: Map left-clicked at Geo ({latitude:.5f}, {longitude:.5f})")
        
        main_window = self.app_controller.main_window
        if not main_window:
            module_logger.warning("MapCommandHandler: MainWindow not available for left click handling.")
            return

        # Update clicked map info panel (Lat/Lon in degrees and DMS)
        if hasattr(main_window, "update_clicked_map_info"):
            lat_dms_str = deg_to_dms_string(latitude, "lat") if latitude is not None else "N/A"
            lon_dms_str = deg_to_dms_string(longitude, "lon") if longitude is not None else "N/A"
            try:
                main_window.update_clicked_map_info(
                    lat_deg=latitude,
                    lon_deg=longitude,
                    lat_dms=lat_dms_str,
                    lon_dms=lon_dms_str,
                )
            except tk.TclError as e_tcl:
                module_logger.warning(f"MapCommandHandler: TclError updating map clicked info panel: {e_tcl}. GUI closing.")
            except Exception as e_update:
                module_logger.error(f"MapCommandHandler: Error updating map clicked info panel: {e_update}", exc_info=False)

        # Attempt to select a flight near the clicked coordinates
        if hasattr(main_window, "map_manager_instance") and main_window.map_manager_instance:
            map_manager: "MapCanvasManager" = main_window.map_manager_instance
            if hasattr(map_manager, "get_clicked_flight_icao"):
                # Use canvas_x, canvas_y for flight identification
                clicked_flight_icao = map_manager.get_clicked_flight_icao(canvas_x, canvas_y)
                if clicked_flight_icao:
                    module_logger.info(f"MapCommandHandler: Flight selected by click: {clicked_flight_icao}")
                    if hasattr(self.app_controller, "request_detailed_flight_info"):
                        self.app_controller.request_detailed_flight_info(clicked_flight_icao)
                elif not map_manager.is_detail_map: # Only clear for main map if no flight selected
                    module_logger.info(f"MapCommandHandler: No specific flight selected by click on MAIN map. Clearing details.")
                    if hasattr(self.app_controller, "request_detailed_flight_info"):
                        self.app_controller.request_detailed_flight_info("") # Request clearing details # Request clearing details


    def on_map_right_click(self, latitude: float, longitude: float, screen_x: int, screen_y: int):
        """
        Handles a right-click event on the map canvas.
        Updates the clicked map info panel and shows a context menu.
        """
        module_logger.debug(f"MapCommandHandler: Map right-clicked at Geo ({latitude:.5f}, {longitude:.5f})")
        
        main_window = self.app_controller.main_window
        if not main_window:
            module_logger.warning("MapCommandHandler: MainWindow not available for right click handling.")
            return

        # Update clicked map info panel (Lat/Lon in degrees and DMS)
        if hasattr(main_window, "update_clicked_map_info"):
            lat_dms_str = deg_to_dms_string(latitude, "lat") if latitude is not None else "N/A"
            lon_dms_str = deg_to_dms_string(longitude, "lon") if longitude is not None else "N/A"
            try:
                main_window.update_clicked_map_info(
                    lat_deg=latitude,
                    lon_deg=longitude,
                    lat_dms=lat_dms_str,
                    lon_dms=lon_dms_str,
                )
            except tk.TclError as e_tcl:
                module_logger.warning(f"MapCommandHandler: TclError updating map clicked info (right): {e_tcl}. GUI closing.")
            except Exception as e_update:
                module_logger.error(f"MapCommandHandler: Error updating map clicked info (right): {e_update}", exc_info=False)

        # Show map context menu
        if hasattr(main_window, "show_map_context_menu"):
            try:
                main_window.show_map_context_menu(latitude, longitude, screen_x, screen_y)
            except tk.TclError as e_tcl_menu:
                module_logger.warning(f"MapCommandHandler: TclError showing map context menu: {e_tcl_menu}. GUI closing.")
            except Exception as e_menu:
                module_logger.error(f"MapCommandHandler: Error showing map context menu: {e_menu}", exc_info=False)


    def on_map_context_menu_request(self, latitude: float, longitude: float, screen_x: int, screen_y: int):
        """
        Delegates a request to show the map context menu. This might be called directly by MapCanvasManager.
        """
        module_logger.debug(f"MapCommandHandler: Received context menu request for ({latitude:.5f}, {longitude:.5f}) at screen ({screen_x}, {screen_y}).")
        
        main_window = self.app_controller.main_window
        if main_window and hasattr(main_window, "show_map_context_menu"):
            try:
                main_window.show_map_context_menu(latitude, longitude, screen_x, screen_y)
            except tk.TclError as e_tcl:
                module_logger.warning(f"MapCommandHandler: TclError delegating show_map_context_menu: {e_tcl}. GUI closing.")
            except Exception as e_menu:
                module_logger.error(f"MapCommandHandler: Error delegating show_map_context_menu: {e_menu}", exc_info=False)
        else:
            module_logger.warning("MapCommandHandler: Main window N/A to show context menu.")

    def recenter_map_at_coords(self, lat: float, lon: float):
        """
        Requests the MapCanvasManager to recenter the map at the given coordinates.
        """
        module_logger.info(f"MapCommandHandler: Request: recenter map at ({lat:.5f}, {lon:.5f}).")
        
        main_window = self.app_controller.main_window
        if (
            main_window and hasattr(main_window, "map_manager_instance")
            and main_window.map_manager_instance is not None
        ):
            map_manager: "MapCanvasManager" = main_window.map_manager_instance
            if hasattr(map_manager, "recenter_map_at_coords"):
                try:
                    map_manager.recenter_map_at_coords(lat, lon)
                except Exception as e_recenter:
                    module_logger.error(f"MapCommandHandler: Error map_manager.recenter_map_at_coords: {e_recenter}", exc_info=False)
            else:
                module_logger.warning("MapCommandHandler: MapCanvasManager missing 'recenter_map_at_coords' method.")
        else:
            module_logger.warning("MapCommandHandler: Main window or MapCanvasManager N/A to recenter map.")

    def set_bbox_around_coords(self, center_lat: float, center_lon: float, area_size_km: float = DEFAULT_CLICK_AREA_SIZE_KM):
        """
        Requests the MapCanvasManager to set the map's bounding box around given coordinates with a specified area size.
        This is typically used for map context menu actions on the main map.
        """
        module_logger.info(f"MapCommandHandler: Request: set BBox ({area_size_km:.1f}km) around ({center_lat:.5f}, {center_lon:.5f}).")
        
        main_window = self.app_controller.main_window
        if (
            main_window and hasattr(main_window, "map_manager_instance")
            and main_window.map_manager_instance is not None
        ):
            map_manager: "MapCanvasManager" = main_window.map_manager_instance
            # Ensure this is only called on the main map (not a detail map)
            if hasattr(map_manager, "is_detail_map") and map_manager.is_detail_map:
                module_logger.warning("MapCommandHandler: Attempt to set BBox on detail map. Ignoring.")
                if hasattr(main_window, "show_info_message"):
                    main_window.show_info_message("Map Action", "Cannot set monitoring area on detail map.")
                return

            if hasattr(map_manager, "set_bbox_around_coords"):
                try:
                    map_manager.set_bbox_around_coords(center_lat, center_lon, area_size_km)
                except Exception as e_set_bbox:
                    module_logger.error(f"MapCommandHandler: Error map_manager.set_bbox_around_coords: {e_set_bbox}", exc_info=False)
                    if hasattr(main_window, "show_error_message"):
                        main_window.show_error_message("Map Error", f"Failed to set BBox: {e_set_bbox}")
            else:
                module_logger.warning("MapCommandHandler: MapCanvasManager missing 'set_bbox_around_coords' method.")
                if hasattr(main_window, "show_error_message"):
                    main_window.show_error_message("Map Error", "Map function missing to set BBox.")
        else:
            module_logger.warning("MapCommandHandler: Main window or MapCanvasManager N/A to set BBox.")
            if hasattr(main_window, "show_error_message"):
                main_window.show_error_message("Map Error", "Map not ready to set BBox.")

    def update_bbox_gui_fields(self, bbox_dict: Dict[str, float]):
        """
        Requests the MainWindow to update the GUI input fields for the bounding box.
        This is typically called by MapCanvasManager when its internal bbox changes.
        """
        module_logger.debug(f"MapCommandHandler: Request: update BBox GUI fields with: {bbox_dict}")
        
        main_window = self.app_controller.main_window
        if main_window and hasattr(main_window, "update_bbox_gui_fields"):
            try:
                main_window.update_bbox_gui_fields(bbox_dict)
            except tk.TclError as e_tcl:
                module_logger.warning(f"MapCommandHandler: TclError updating BBox GUI fields: {e_tcl}. GUI closing.")
            except Exception as e_update:
                module_logger.error(f"MapCommandHandler: Error updating BBox GUI fields: {e_update}", exc_info=False)
        else:
            module_logger.warning("MapCommandHandler: Main window N/A to update BBox GUI fields.")

    def update_general_map_info(self):
        """
        Requests the MapCanvasManager for current map information and
        then updates the MainWindow's general map info display panel.
        """
        module_logger.debug("MapCommandHandler: Request to update general map info.")
        
        main_window = self.app_controller.main_window
        if not (main_window and hasattr(main_window, "map_manager_instance") and main_window.map_manager_instance):
            # If map manager is not available, ensure info display is cleared
            if main_window and hasattr(main_window, "update_general_map_info_display"):
                try:
                    main_window.update_general_map_info_display(
                        zoom=None, map_size_str="N/A", map_geo_bounds=None, target_bbox_input=None, flight_count=None,
                    )
                except Exception:
                    pass
            return

        map_manager: "MapCanvasManager" = main_window.map_manager_instance
        if hasattr(map_manager, "get_current_map_info"):
            map_info = {}
            try:
                map_info = map_manager.get_current_map_info()
                zoom = map_info.get("zoom")
                map_geo_bounds = map_info.get("map_geo_bounds")
                target_bbox_input_from_map_info = map_info.get("target_bbox_input")
                flight_count = map_info.get("flight_count")
                map_size_km_w = map_info.get("map_size_km_w")
                map_size_km_h = map_info.get("map_size_km_h")
                
                map_size_str = "N/A"
                if map_size_km_w is not None and map_size_km_h is not None:
                    try:
                        decimals = getattr(app_config, "MAP_SIZE_KM_DECIMAL_PLACES", 1)
                        map_size_str = f"{map_size_km_w:.{decimals}f}km x {map_size_km_h:.{decimals}f}km"
                    except Exception as e_format:
                        module_logger.warning(f"MapCommandHandler: Error formatting map size: {e_format}. Using N/A.", exc_info=False)
                        map_size_str = "N/A (FormatErr)"

                if hasattr(main_window, "update_general_map_info_display"):
                    try:
                        main_window.update_general_map_info_display(
                            zoom=zoom,
                            map_size_str=map_size_str,
                            map_geo_bounds=map_geo_bounds,
                            target_bbox_input=target_bbox_input_from_map_info,
                            flight_count=flight_count,
                        )
                    except tk.TclError as e_tcl:
                        module_logger.warning(f"MapCommandHandler: TclError updating general map info panel: {e_tcl}. GUI closing.")
                    except Exception as e_update:
                        module_logger.error(f"MapCommandHandler: Error updating general map info panel: {e_update}", exc_info=False)
            except Exception as e_get_info:
                module_logger.error(f"MapCommandHandler: Error getting map info from map manager: {e_get_info}", exc_info=True)
                if main_window and hasattr(main_window, "update_general_map_info_display"):
                    try:
                        main_window.update_general_map_info_display(
                            zoom=None, map_size_str="N/A", map_geo_bounds=None, target_bbox_input=None, flight_count=None,
                        )
                    except Exception:
                        pass
        else:
            module_logger.warning("MapCommandHandler: MapCanvasManager missing 'get_current_map_info' method.")


    def map_zoom_in(self):
        """Requests MapCanvasManager to zoom in at the current map center."""
        module_logger.debug("MapCommandHandler: Map Zoom In requested.")
        
        main_window = self.app_controller.main_window
        if (
            main_window and hasattr(main_window, "map_manager_instance")
            and main_window.map_manager_instance is not None
        ):
            map_manager: "MapCanvasManager" = main_window.map_manager_instance
            if hasattr(map_manager, "zoom_in_at_center"):
                try:
                    map_manager.zoom_in_at_center()
                except Exception as e:
                    module_logger.error(f"MapCommandHandler: Error map_manager.zoom_in_at_center: {e}", exc_info=False)
            else:
                module_logger.warning("MapCommandHandler: MapCanvasManager missing 'zoom_in_at_center' method.")
        else:
            module_logger.warning("MapCommandHandler: Map manager N/A for zoom in.")

    def map_zoom_out(self):
        """Requests MapCanvasManager to zoom out at the current map center."""
        module_logger.debug("MapCommandHandler: Map Zoom Out requested.")
        
        main_window = self.app_controller.main_window
        if (
            main_window and hasattr(main_window, "map_manager_instance")
            and main_window.map_manager_instance is not None
        ):
            map_manager: "MapCanvasManager" = main_window.map_manager_instance
            if hasattr(map_manager, "zoom_out_at_center"):
                try:
                    map_manager.zoom_out_at_center()
                except Exception as e:
                    module_logger.error(f"MapCommandHandler: Error map_manager.zoom_out_at_center: {e}", exc_info=False)
            else:
                module_logger.warning("MapCommandHandler: MapCanvasManager missing 'zoom_out_at_center' method.")
        else:
            module_logger.warning("MapCommandHandler: Map manager N/A for zoom out.")

    def map_pan_direction(self, direction: str):
        """
        Requests MapCanvasManager to pan the map in a specified direction.

        Args:
            direction: The direction to pan ("up", "down", "left", "right").
        """
        module_logger.debug(f"MapCommandHandler: Map Pan '{direction}' requested.")
        
        main_window = self.app_controller.main_window
        if (
            main_window and hasattr(main_window, "map_manager_instance")
            and main_window.map_manager_instance is not None
        ):
            map_manager: "MapCanvasManager" = main_window.map_manager_instance
            if hasattr(map_manager, "pan_map_fixed_step"):
                try:
                    map_manager.pan_map_fixed_step(direction)
                except Exception as e:
                    module_logger.error(f"MapCommandHandler: Error map_manager.pan_map_fixed_step('{direction}'): {e}", exc_info=False)
            else:
                module_logger.warning("MapCommandHandler: MapCanvasManager missing 'pan_map_fixed_step' method.")
        else:
            module_logger.warning(f"MapCommandHandler: Map manager N/A for pan {direction}.")

    def map_center_on_coords_and_fit_patch(self, lat: float, lon: float, patch_size_km: float):
        """
        Requests MapCanvasManager to center the map at given coordinates and adjust zoom to fit a patch size.
        """
        module_logger.debug(f"MapCommandHandler: Center map at ({lat:.4f},{lon:.4f}) and fit {patch_size_km}km patch.")
        
        main_window = self.app_controller.main_window
        if (
            main_window and hasattr(main_window, "map_manager_instance")
            and main_window.map_manager_instance is not None
        ):
            map_manager: "MapCanvasManager" = main_window.map_manager_instance
            if hasattr(map_manager, "center_map_and_fit_patch"):
                try:
                    map_manager.center_map_and_fit_patch(lat, lon, patch_size_km)
                except Exception as e:
                    module_logger.error(f"MapCommandHandler: Error map_manager.center_map_and_fit_patch: {e}", exc_info=False)
            else:
                module_logger.warning("MapCommandHandler: MapCanvasManager missing 'center_map_and_fit_patch' method.")
        else:
            module_logger.warning("MapCommandHandler: Map manager N/A for center and fit patch.")

    def set_map_track_length(self, length: int):
        """
        Requests MapCanvasManager to set the maximum number of track points to display.
        """
        module_logger.info(f"MapCommandHandler: Request to set map track length to {length} points.")
        
        main_window = self.app_controller.main_window
        if (
            main_window and hasattr(main_window, "map_manager_instance")
            and main_window.map_manager_instance is not None
            and hasattr(main_window.map_manager_instance, "set_max_track_points")
        ):
            try:
                main_window.map_manager_instance.set_max_track_points(length)
            except Exception as e:
                module_logger.error(f"MapCommandHandler: Error calling map_manager.set_max_track_points({length}): {e}", exc_info=True)
                if hasattr(main_window, "show_error_message"):
                    main_window.show_error_message("Map Configuration Error", f"Failed to set track length on map: {e}")
        else:
            module_logger.warning("MapCommandHandler: MapCanvasManager or 'set_max_track_points' N/A to set track length.")