"""
open_meteo_mcp - MCP server for global weather data via the Open-Meteo API.

Provides tools for fetching current weather conditions and multi-day forecasts
for any location worldwide. No API key required.
"""

import logging
from typing import Optional

import httpx
from mcp.server.fastmcp import FastMCP

# ---------------------------------------------------------------------------
# Logging & Output Redirection
# ---------------------------------------------------------------------------
import os
import sys

LOG_FILE = os.environ.get("OPEN_METEO_MCP_LOG_FILE", "open_meteo_mcp.log")

def setup_redirection():
    """Reduces noise by suppressing stderr and logging to a file."""
    # Suppress stderr
    devnull = open(os.devnull, "w")
    os.dup2(devnull.fileno(), sys.stderr.fileno())

    # Configure logging to write to the log file directly
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s [%(levelname)s] %(message)s",
        filename=LOG_FILE,
        filemode="a"
    )

setup_redirection()
logger = logging.getLogger("open_meteo_mcp")

# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
OPEN_METEO_BASE = "https://api.open-meteo.com/v1/forecast"

# WMO Weather interpretation codes
WMO_CODES = {
    0: "Clear sky",
    1: "Mainly clear",
    2: "Partly cloudy",
    3: "Overcast",
    45: "Fog",
    48: "Depositing rime fog",
    51: "Light drizzle",
    53: "Moderate drizzle",
    55: "Dense drizzle",
    56: "Light freezing drizzle",
    57: "Dense freezing drizzle",
    61: "Slight rain",
    63: "Moderate rain",
    65: "Heavy rain",
    66: "Light freezing rain",
    67: "Heavy freezing rain",
    71: "Slight snowfall",
    73: "Moderate snowfall",
    75: "Heavy snowfall",
    77: "Snow grains",
    80: "Slight rain showers",
    81: "Moderate rain showers",
    82: "Violent rain showers",
    85: "Slight snow showers",
    86: "Heavy snow showers",
    95: "Thunderstorm",
    96: "Thunderstorm with slight hail",
    99: "Thunderstorm with heavy hail",
}

# ---------------------------------------------------------------------------
# MCP Server
# ---------------------------------------------------------------------------
mcp = FastMCP(
    "open_meteo_mcp",
    instructions="Global weather data via Open-Meteo API",
)


def _describe_weather_code(code: int) -> str:
    return WMO_CODES.get(code, f"Unknown ({code})")


def _c_to_f(celsius: float) -> float:
    return round(celsius * 9 / 5 + 32, 1)


def _temp_both(celsius: float) -> str:
    return f"{celsius}°C / {_c_to_f(celsius)}°F"


def _kmh_to_mph(kmh: float) -> float:
    return round(kmh * 0.621371, 1)


def _wind_both(kmh: float) -> str:
    return f"{kmh}km/h / {_kmh_to_mph(kmh)}mph"


@mcp.tool()
async def get_current_weather(latitude: float, longitude: float) -> str:
    """Get current weather conditions for a location.

    Args:
        latitude: Latitude of the location (e.g. 51.752 for Oxford, UK)
        longitude: Longitude of the location (e.g. -1.258 for Oxford, UK)
    """
    try:
        params = {
            "latitude": latitude,
            "longitude": longitude,
            "current": ",".join([
                "temperature_2m",
                "relative_humidity_2m",
                "apparent_temperature",
                "weather_code",
                "wind_speed_10m",
                "wind_direction_10m",
                "wind_gusts_10m",
                "precipitation",
                "cloud_cover",
                "pressure_msl",
            ]),
            "timezone": "auto",
        }

        async with httpx.AsyncClient() as client:
            resp = await client.get(OPEN_METEO_BASE, params=params, timeout=30)
            resp.raise_for_status()
            data = resp.json()

        current = data["current"]
        units = data["current_units"]
        tz = data.get("timezone", "Unknown")

        lines = [
            f"Current Weather (timezone: {tz})",
            f"  Time: {current['time']}",
            f"  Condition: {_describe_weather_code(current['weather_code'])}",
            f"  Temperature: {_temp_both(current['temperature_2m'])}",
            f"  Feels like: {_temp_both(current['apparent_temperature'])}",
            f"  Humidity: {current['relative_humidity_2m']}{units['relative_humidity_2m']}",
            f"  Wind: {_wind_both(current['wind_speed_10m'])} from {current['wind_direction_10m']}{units['wind_direction_10m']}",
            f"  Gusts: {_wind_both(current['wind_gusts_10m'])}",
            f"  Precipitation: {current['precipitation']}{units['precipitation']}",
            f"  Cloud cover: {current['cloud_cover']}{units['cloud_cover']}",
            f"  Pressure: {current['pressure_msl']}{units['pressure_msl']}",
        ]

        return "\n".join(lines)
    except Exception as e:
        logger.error("Error in get_current_weather(%s, %s): %s", latitude, longitude, e, exc_info=True)
        return f"Error fetching current weather: {type(e).__name__}: {e}"


@mcp.tool()
async def get_forecast(
    latitude: float,
    longitude: float,
    days: int = 7,
) -> str:
    """Get a multi-day weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
        days: Number of forecast days (1-16, default 7)
    """
    try:
        days = max(1, min(16, days))

        params = {
            "latitude": latitude,
            "longitude": longitude,
            "daily": ",".join([
                "weather_code",
                "temperature_2m_max",
                "temperature_2m_min",
                "apparent_temperature_max",
                "apparent_temperature_min",
                "precipitation_sum",
                "precipitation_probability_max",
                "wind_speed_10m_max",
                "wind_gusts_10m_max",
                "wind_direction_10m_dominant",
                "sunrise",
                "sunset",
            ]),
            "temperature_unit": "celsius",
            "timezone": "auto",
            "forecast_days": days,
        }

        async with httpx.AsyncClient() as client:
            resp = await client.get(OPEN_METEO_BASE, params=params, timeout=30)
            resp.raise_for_status()
            data = resp.json()

        daily = data["daily"]
        units = data["daily_units"]
        tz = data.get("timezone", "Unknown")

        sections = [f"Forecast for {days} day(s) (timezone: {tz})"]

        for i in range(len(daily["time"])):
            section = [
                f"\n--- {daily['time'][i]} ---",
                f"  Condition: {_describe_weather_code(daily['weather_code'][i])}",
                f"  High: {_temp_both(daily['temperature_2m_max'][i])}  Low: {_temp_both(daily['temperature_2m_min'][i])}",
                f"  Feels like: {_temp_both(daily['apparent_temperature_max'][i])} / {_temp_both(daily['apparent_temperature_min'][i])}",
                f"  Precipitation: {daily['precipitation_sum'][i]}{units['precipitation_sum']} (chance: {daily['precipitation_probability_max'][i]}{units['precipitation_probability_max']})",
                f"  Wind: {_wind_both(daily['wind_speed_10m_max'][i])} gusts {_wind_both(daily['wind_gusts_10m_max'][i])} from {daily['wind_direction_10m_dominant'][i]}{units['wind_direction_10m_dominant']}",
                f"  Sunrise: {daily['sunrise'][i]}  Sunset: {daily['sunset'][i]}",
            ]
            sections.append("\n".join(section))

        return "\n".join(sections)
    except Exception as e:
        logger.error("Error in get_forecast(%s, %s, days=%s): %s", latitude, longitude, days, e, exc_info=True)
        return f"Error fetching forecast: {type(e).__name__}: {e}"


# ---------------------------------------------------------------------------
# Entrypoints
# ---------------------------------------------------------------------------
def main():
    logger.info("Starting open_meteo_mcp on streamable-http")
    mcp.run(transport="streamable-http")


def main_stdio():
    logger.info("Starting open_meteo_mcp via stdio")
    mcp.run(transport="stdio")


if __name__ == "__main__":
    main()
