from datetime import UTC, datetime
from typing import TYPE_CHECKING

from PIL import Image, ImageDraw, ImageFont

from .chart_context import build_chart_context
from .cloud_cover import draw_cloud_cover_chart
from .common import (
    COLOUR_BLACK,
    COLOUR_WHITE,
    ChartBounds,
    build_chart_bounds,
    build_render_metrics,
    finalise_dashboard_image,
)
from .feedback import WEATHER_LOADING, draw_forecast_feedback
from .forecast import build_next_rain_header_message, normalise_chart_now
from .header import draw_header
from .precipitation import draw_precipitation_chart
from .temperature import draw_temperature_chart

if TYPE_CHECKING:
    from kindle_dashboard.weather import Forecast


def render_dashboard(
    width: int,
    height: int,
    *,
    battery_level: int | None,
    forecast: Forecast | None,
    forecast_error_message: str | None,
    weather_configured: bool,
    weather_data_stale: bool,
    supersample_scale: int,
    weather_data_stale_hours: int | None = None,
    next_rain_gap_tolerance_hours: float = 1.0,
) -> Image.Image:
    scaled_width = width * supersample_scale
    scaled_height = height * supersample_scale

    metrics = build_render_metrics(width, height, supersample_scale)

    image = Image.new("L", (scaled_width, scaled_height), color=COLOUR_WHITE)
    draw = ImageDraw.Draw(image)
    font = ImageFont.load_default(size=16 * supersample_scale)

    draw.rectangle(
        (0, 0, scaled_width - 1, scaled_height - 1),
        outline=COLOUR_BLACK,
        width=metrics.border_width,
    )

    now = datetime.now(tz=UTC)
    chart_now = normalise_chart_now(now, forecast) if forecast is not None else now
    weather_status_message = None
    if forecast is not None:
        weather_status_message = build_next_rain_header_message(
            forecast,
            chart_now,
            rain_gap_tolerance_hours=next_rain_gap_tolerance_hours,
        )
    elif weather_configured and forecast_error_message is None:
        weather_status_message = WEATHER_LOADING

    _, header_bottom = draw_header(
        draw=draw,
        font=font,
        scaled_width=scaled_width,
        now=now,
        battery_level=battery_level,
        weather_data_stale=weather_data_stale,
        weather_data_stale_hours=weather_data_stale_hours,
        weather_status_message=weather_status_message,
        metrics=metrics,
    )

    base_chart_bounds = build_chart_bounds(scaled_width, scaled_height, metrics)
    chart_bounds = ChartBounds(
        left=base_chart_bounds.left,
        top=max(base_chart_bounds.top, header_bottom + metrics.padding),
        right=base_chart_bounds.right,
        bottom=base_chart_bounds.bottom,
    )

    if forecast_error_message is not None:
        draw_forecast_feedback(
            draw=draw,
            font=font,
            left=chart_bounds.left,
            top=chart_bounds.top,
            right=chart_bounds.right,
            bottom=chart_bounds.bottom,
            message=forecast_error_message,
            error_text_line_spacing=metrics.error_text_line_spacing,
        )
    elif forecast is not None:
        chart_context = build_chart_context(
            draw=draw,
            font=font,
            chart_bounds=chart_bounds,
            now=chart_now,
            metrics=metrics,
        )
        draw_precipitation_chart(
            draw=draw,
            font=font,
            chart_bounds=chart_bounds,
            chart_context=chart_context,
            forecast=forecast,
            metrics=metrics,
        )
        draw_cloud_cover_chart(
            draw=draw,
            chart_bounds=chart_bounds,
            chart_context=chart_context,
            forecast=forecast,
        )
        draw_temperature_chart(
            draw=draw,
            font=font,
            chart_bounds=chart_bounds,
            chart_context=chart_context,
            forecast=forecast,
            metrics=metrics,
        )
    return finalise_dashboard_image(image, width, height)
