from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import requests
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error


API_URL = "https://archive-api.open-meteo.com/v1/archive"
CACHE_FILE = Path("weather_tokyo.csv")


def load_weather() -> pd.DataFrame:
    """Open-Meteoから東京の日別データを取得し、CSVにも保存する。"""
    if CACHE_FILE.exists():
        print(f"キャッシュを読み込みます: {CACHE_FILE}")
        return pd.read_csv(CACHE_FILE, parse_dates=["time"])

    params = {
        "latitude": 35.6762,
        "longitude": 139.6503,
        "start_date": "2022-01-01",
        "end_date": "2025-12-31",
        "daily": ",".join(
            [
                "temperature_2m_max",
                "temperature_2m_min",
                "precipitation_sum",
                "wind_speed_10m_max",
            ]
        ),
        "timezone": "Asia/Tokyo",
    }
    response = requests.get(API_URL, params=params, timeout=30)
    response.raise_for_status()
    payload = response.json()
    if "daily" not in payload:
        raise RuntimeError(f"APIから日別データを取得できませんでした: {payload}")

    frame = pd.DataFrame(payload["daily"])
    frame["time"] = pd.to_datetime(frame["time"])
    frame.to_csv(CACHE_FILE, index=False)
    print(f"{len(frame)}日分を取得し、{CACHE_FILE} に保存しました")
    return frame


def make_dataset(weather: pd.DataFrame):
    """今日までに分かる値から、翌日の最高気温を予測する表を作る。"""
    frame = weather.sort_values("time").copy()
    frame["target_date"] = frame["time"].shift(-1)
    frame["target_max_temperature"] = frame["temperature_2m_max"].shift(-1)
    frame["target_day_sin"] = np.sin(
        2 * np.pi * frame["target_date"].dt.dayofyear / 365.25
    )
    frame["target_day_cos"] = np.cos(
        2 * np.pi * frame["target_date"].dt.dayofyear / 365.25
    )

    feature_names = [
        "temperature_2m_max",
        "temperature_2m_min",
        "precipitation_sum",
        "wind_speed_10m_max",
        "target_day_sin",
        "target_day_cos",
    ]
    frame = frame.dropna(subset=feature_names + ["target_max_temperature"])
    return frame, feature_names


def main() -> None:
    dataset, feature_names = make_dataset(load_weather())

    # 時系列データなので、未来側20%をテスト用として分ける。
    split_at = int(len(dataset) * 0.8)
    train = dataset.iloc[:split_at]
    test = dataset.iloc[split_at:]

    model = RandomForestRegressor(
        n_estimators=300,
        max_depth=12,
        min_samples_leaf=3,
        random_state=42,
        n_jobs=-1,
    )
    model.fit(train[feature_names], train["target_max_temperature"])
    prediction = model.predict(test[feature_names])

    model_mae = mean_absolute_error(test["target_max_temperature"], prediction)
    baseline = test["temperature_2m_max"]
    baseline_mae = mean_absolute_error(test["target_max_temperature"], baseline)

    print(f"学習期間: {train['time'].min().date()} 〜 {train['time'].max().date()}")
    print(f"評価期間: {test['time'].min().date()} 〜 {test['time'].max().date()}")
    print(f"モデルのMAE: {model_mae:.2f} ℃")
    print(f"単純予測のMAE: {baseline_mae:.2f} ℃")

    shown = test.tail(60)
    shown_prediction = prediction[-len(shown) :]
    plt.figure(figsize=(11, 5))
    plt.plot(
        shown["target_date"],
        shown["target_max_temperature"],
        label="actual",
    )
    plt.plot(shown["target_date"], shown_prediction, label="prediction")
    plt.ylabel("Maximum temperature (°C)")
    plt.title("Tokyo: next-day maximum temperature")
    plt.legend()
    plt.tight_layout()
    plt.savefig("weather_prediction.png", dpi=150)
    print("グラフを weather_prediction.png に保存しました")


if __name__ == "__main__":
    main()
