# flightmonitor/map/map_drawing.py

import logging
import math
from typing import Optional, Tuple, List, Dict, Any
from collections import deque
import os

try:
    from PIL import Image, ImageDraw, ImageFont, ImageColor
    PIL_LIB_AVAILABLE_DRAWING = True
except ImportError:
    Image, ImageDraw, ImageFont, ImageColor = None, None, None, None
    PIL_LIB_AVAILABLE_DRAWING = False
    import logging
    logging.error("MapDrawing: Pillow not found. Drawing disabled.")

try:
    from flightmonitor.map.map_utils import geo_to_pixel_on_unscaled_map, calculate_graticule_interval
    from flightmonitor.data.common_models import CanonicalFlightState
    from flightmonitor.map import map_constants
    from flightmonitor.data import config as app_config

    logger = logging.getLogger(__name__)
    TRACK_LINE_WIDTH = 2

except ImportError as e:
    logger = logging.getLogger(__name__)
    logger.error(f"MapDrawing failed to import dependencies: {e}. Using fallbacks.")
    class CanonicalFlightState: pass
    class MockMapConstants:
        AREA_BOUNDARY_COLOR, TILE_TEXT_BG_COLOR, TILE_TEXT_COLOR = "blue", "rgba(0, 0, 0, 150)", "white"
        AREA_BOUNDARY_THICKNESS_PX, DEFAULT_LABEL_FONT_PATH, TRACK_COLOR_PALETTE = 2, None, ["#FF0000"]
        GRATICULE_ENABLED, GRATICULE_LINE_COLOR, GRATICULE_LINE_WIDTH, GRATICULE_LABEL_FONT_SIZE = True, "gray", 1, 10
    map_constants = MockMapConstants()
    TRACK_LINE_WIDTH = 2
    def geo_to_pixel_on_unscaled_map(*args, **kwargs): return None
    def calculate_graticule_interval(span, pixels): return 1.0


def draw_area_bounding_box(
    pil_image_to_draw_on: Any,
    area_geo_bbox: Tuple[float, float, float, float],
    current_map_geo_bounds: Optional[Tuple[float, float, float, float]],
    current_stitched_map_pixel_shape: Optional[Tuple[int, int]],
    color: str = "blue",
    thickness: int = 2,
) -> Any:
    if not (PIL_LIB_AVAILABLE_DRAWING and ImageDraw and pil_image_to_draw_on and current_map_geo_bounds and current_stitched_map_pixel_shape):
        return pil_image_to_draw_on

    west, south, east, north = area_geo_bbox
    corners_geo = [(west, north), (east, north), (east, south), (west, south)]
    pixel_corners: List[Tuple[int, int]] = []

    for lon, lat in corners_geo:
        pixel_coords = geo_to_pixel_on_unscaled_map(lat, lon, current_map_geo_bounds, current_stitched_map_pixel_shape)
        if pixel_coords:
            pixel_corners.append(pixel_coords)
        else:
            logger.warning(f"Could not convert BBox corner {lat},{lon} to pixel. Aborting draw.")
            return pil_image_to_draw_on

    if len(pixel_corners) == 4:
        draw = ImageDraw.Draw(pil_image_to_draw_on)
        draw.line(pixel_corners + [pixel_corners[0]], fill=color, width=thickness)

    return pil_image_to_draw_on

def draw_graticules(
    draw: ImageDraw.ImageDraw,
    map_geo_bounds: Tuple[float, float, float, float],
    image_size: Tuple[int, int],
    font: Optional[ImageFont.FreeTypeFont]
):
    if not (PIL_LIB_AVAILABLE_DRAWING and draw and map_geo_bounds):
        return

    west, south, east, north = map_geo_bounds
    lon_span, lat_span = east - west, north - south
    img_width, img_height = image_size

    # MODIFICATO: Passaggio dei parametri corretti alla funzione
    lon_interval = calculate_graticule_interval(lon_span, img_width)
    lat_interval = calculate_graticule_interval(lat_span, img_height)

    line_color, line_width = map_constants.GRATICULE_LINE_COLOR, map_constants.GRATICULE_LINE_WIDTH
    epsilon = 1e-9

    start_lon = math.floor(west / lon_interval) * lon_interval
    if start_lon < west - epsilon: start_lon += lon_interval
    
    lon = start_lon
    while lon < east + epsilon:
        p1_pixel = geo_to_pixel_on_unscaled_map(north, lon, map_geo_bounds, image_size)
        p2_pixel = geo_to_pixel_on_unscaled_map(south, lon, map_geo_bounds, image_size)

        if p1_pixel and p2_pixel:
            draw.line([p1_pixel, p2_pixel], fill=line_color, width=line_width)
            label = f"{abs(round(lon, 4)):g}°{'E' if lon >= 0 else 'W'}"
            label_pos = (p1_pixel[0] + 5, 5)
            if font: draw.text(label_pos, label, font=font, fill=line_color, anchor="lt")
        
        lon += lon_interval

    start_lat = math.floor(south / lat_interval) * lat_interval
    if start_lat < south - epsilon: start_lat += lat_interval
    
    lat = start_lat
    while lat < north + epsilon:
        p1_pixel = geo_to_pixel_on_unscaled_map(lat, west, map_geo_bounds, image_size)
        p2_pixel = geo_to_pixel_on_unscaled_map(lat, east, map_geo_bounds, image_size)

        if p1_pixel and p2_pixel:
            draw.line([p1_pixel, p2_pixel], fill=line_color, width=line_width)
            label = f"{abs(round(lat, 4)):g}°{'N' if lat >= 0 else 'S'}"
            label_pos = (5, p1_pixel[1] - 5)
            if font: draw.text(label_pos, label, font=font, fill=line_color, anchor="lb")
        
        lat += lat_interval


def _load_label_font(scaled_font_size: int, is_graticule: bool = False) -> Optional[Any]:
    if not (PIL_LIB_AVAILABLE_DRAWING and ImageFont):
        return None
    
    font_size = map_constants.GRATICULE_LABEL_FONT_SIZE if is_graticule else max(1, scaled_font_size)
    font_path = getattr(map_constants, "DEFAULT_LABEL_FONT_PATH", None)
    
    try:
        if font_path and os.path.exists(font_path): return ImageFont.truetype(font_path, font_size)
        else: return ImageFont.load_default(size=font_size)
    except (IOError, OSError): logger.warning(f"Font file not found at {font_path}. Falling back.")
    except AttributeError: return ImageFont.load_default()
    except Exception as e: logger.error(f"Error loading font: {e}")
        
    return ImageFont.load_default()

def _calculate_faded_color(base_color_hex: str, index_from_end: int, total_visible_points: int, min_intensity: float = 0.1) -> Optional[Tuple[int, int, int, int]]:
    if not (0 <= index_from_end < total_visible_points and ImageColor): return None
    try:
        r, g, b = ImageColor.getrgb(base_color_hex)
        intensity = min_intensity + (1.0 - min_intensity) * ((total_visible_points - 1 - index_from_end) / (total_visible_points - 1)) if total_visible_points > 1 else 1.0
        return (r, g, b, int(max(0, min(255, intensity * 255))))
    except Exception: return (128, 128, 128, 255)


def _draw_single_flight(
    draw: Any, pixel_coords: Tuple[int, int], flight_state: CanonicalFlightState, label_font: Optional[Any],
    track_deque: Optional[deque], current_map_geo_bounds: Optional[Tuple[float, float, float, float]],
    current_stitched_map_pixel_shape: Optional[Tuple[int, int]],
):
    if not (PIL_LIB_AVAILABLE_DRAWING and draw and pixel_coords and flight_state): return

    px, py = pixel_coords
    base_length, side_length = 8, 12
    label_text = flight_state.callsign.strip() if flight_state.callsign and flight_state.callsign.strip() else flight_state.icao24

    color_index = hash(flight_state.icao24) % len(map_constants.TRACK_COLOR_PALETTE)
    base_color = map_constants.TRACK_COLOR_PALETTE[color_index]

    if track_deque and len(track_deque) > 1:
        pixel_points = [p for p in [geo_to_pixel_on_unscaled_map(pt[0], pt[1], current_map_geo_bounds, current_stitched_map_pixel_shape) for pt in list(track_deque)] if p]
        if len(pixel_points) > 1:
            for i in range(len(pixel_points) - 1):
                faded_color = _calculate_faded_color(base_color, len(pixel_points) - 2 - i, len(pixel_points))
                if faded_color: draw.line([pixel_points[i], pixel_points[i+1]], fill=faded_color, width=TRACK_LINE_WIDTH)

    angle_rad = math.radians(90 - (flight_state.true_track_deg or 0.0))
    tip = (px + side_length * math.cos(angle_rad), py - side_length * math.sin(angle_rad))
    base1 = (px + (base_length / 2) * math.cos(angle_rad + math.pi/2), py - (base_length / 2) * math.sin(angle_rad + math.pi/2))
    base2 = (px + (base_length / 2) * math.cos(angle_rad - math.pi/2), py - (base_length / 2) * math.sin(angle_rad - math.pi/2))
    draw.polygon([tip, base1, base2], fill=base_color, outline="black")

    if label_font:
        try:
            anchor_x, anchor_y = px + 15, py
            bbox = draw.textbbox((anchor_x, anchor_y), label_text, font=label_font, anchor="lm")
            padding = 2
            bg_bbox = (bbox[0] - padding, bbox[1] - padding, bbox[2] + padding, bbox[3] + padding)
            draw.rectangle(bg_bbox, fill=map_constants.TILE_TEXT_BG_COLOR)
            draw.text((anchor_x, anchor_y), label_text, fill=map_constants.TILE_TEXT_COLOR, font=label_font, anchor="lm")
        except Exception as e:
            logger.error(f"Error drawing label for '{label_text}': {e}", exc_info=False)

def _draw_text_on_placeholder(draw: Any, image_size: Tuple[int, int], text: str):
    if not (PIL_LIB_AVAILABLE_DRAWING and draw): return
    img_w, img_h = image_size
    font = _load_label_font(12, is_graticule=True)
    if not font:
        draw.text((10, 10), text, fill="black")
        return
    try:
        bbox = draw.textbbox((0, 0), text, font=font, anchor="lt", spacing=4)
        text_w, text_h = bbox[2] - bbox[0], bbox[3] - bbox[1]
        pos_x, pos_y = (img_w - text_w) / 2, (img_h - text_h) / 2
        draw.text((pos_x, pos_y), text, fill="black", font=font, spacing=4)
    except Exception as e:
        logger.warning(f"Error drawing placeholder text: {e}. Falling back.")
        draw.text((10, 10), text, fill="black")