import os
from dataclasses import dataclass

from PIL import Image, ImageDraw, ImageFont

from .render.chart_context import compute_x_axis_y
from .render.common import build_chart_bounds, build_render_metrics


def read_positive_int(name: str, default: int) -> int:
    raw_value = os.getenv(name)
    if raw_value is None:
        return default

    try:
        value = int(raw_value)
    except ValueError as exc:
        message = f"{name} must be an integer"
        raise ValueError(message) from exc

    if value <= 0:
        message = f"{name} must be greater than 0"
        raise ValueError(message)

    return value


def read_float_env(name: str) -> float | None:
    raw_value = os.getenv(name)
    if raw_value is None:
        return None
    try:
        return float(raw_value)
    except ValueError as exc:
        message = f"{name} must be a number"
        raise ValueError(message) from exc


@dataclass(frozen=True, slots=True)
class ServerConfig:
    host: str
    port: int
    default_width: int
    default_height: int
    render_supersample_scale: int
    weather_update_secs: int
    weather_latitude: float | None
    weather_longitude: float | None


def validate_render_dimensions(
    width: int,
    height: int,
    render_supersample_scale: int,
) -> None:
    metrics = build_render_metrics(width, height, render_supersample_scale)
    scaled_width = width * render_supersample_scale
    scaled_height = height * render_supersample_scale
    chart_bounds = build_chart_bounds(scaled_width, scaled_height, metrics)

    if (
        chart_bounds.right <= chart_bounds.left + metrics.min_chart_dimension
        or chart_bounds.bottom <= chart_bounds.top + metrics.min_chart_dimension
    ):
        message = (
            "IMAGE_WIDTH and IMAGE_HEIGHT are too small for chart rendering "
            f"at RENDER_SUPERSAMPLE_SCALE={render_supersample_scale}"
        )
        raise ValueError(message)

    draw = ImageDraw.Draw(Image.new("L", (scaled_width, scaled_height), color=255))
    font = ImageFont.load_default(size=16 * render_supersample_scale)
    compute_x_axis_y(
        draw=draw,
        font=font,
        chart_top=chart_bounds.top,
        chart_bottom=chart_bounds.bottom,
        plot_padding=metrics.plot_padding,
    )


def load_server_config() -> ServerConfig:
    weather_latitude = read_float_env("WEATHER_LATITUDE")
    weather_longitude = read_float_env("WEATHER_LONGITUDE")

    if (weather_latitude is None) != (weather_longitude is None):
        message = "WEATHER_LATITUDE and WEATHER_LONGITUDE must both be set together"
        raise ValueError(message)

    config = ServerConfig(
        host=os.getenv("HOST", "127.0.0.1"),
        port=read_positive_int("PORT", default=42137),
        default_width=read_positive_int("IMAGE_WIDTH", default=600),
        default_height=read_positive_int("IMAGE_HEIGHT", default=800),
        render_supersample_scale=read_positive_int(
            "RENDER_SUPERSAMPLE_SCALE", default=2
        ),
        weather_update_secs=read_positive_int("WEATHER_UPDATE_SECS", default=30 * 60),
        weather_latitude=weather_latitude,
        weather_longitude=weather_longitude,
    )
    validate_render_dimensions(
        config.default_width,
        config.default_height,
        config.render_supersample_scale,
    )
    return config
