"""
気象データを Open-Meteo API から取得して DB に保存する。

使い方:
    # 差分取得（最終レコードの翌日〜昨日）
    python manage.py fetch_weather

    # 全件取得（初回インポート）
    python manage.py fetch_weather --full

    # 期間指定
    python manage.py fetch_weather --start-date 2024-01-01 --end-date 2024-12-31
"""

import datetime
import requests

from django.core.management.base import BaseCommand, CommandError
from apps.weather.models import WeatherRecord


LATITUDE  = 33.213
LONGITUDE = 133.133
TIMEZONE  = 'Asia/Tokyo'
FULL_START = '2016-01-01'

OPEN_METEO_URL = 'https://archive-api.open-meteo.com/v1/archive'
DAILY_VARS = [
    'temperature_2m_mean',
    'temperature_2m_max',
    'temperature_2m_min',
    'sunshine_duration',
    'precipitation_sum',
    'wind_speed_10m_max',
    'surface_pressure_min',
]


def fetch_from_api(start_date: str, end_date: str) -> list[dict]:
    """Open-Meteo から daily データを取得し、WeatherRecord 形式のリストで返す。"""
    params = {
        'latitude':  LATITUDE,
        'longitude': LONGITUDE,
        'start_date': start_date,
        'end_date':   end_date,
        'daily':      DAILY_VARS,
        'timezone':   TIMEZONE,
    }
    resp = requests.get(OPEN_METEO_URL, params=params, timeout=30)
    if resp.status_code != 200:
        raise CommandError(f'Open-Meteo API エラー: {resp.status_code} {resp.text[:200]}')

    data  = resp.json().get('daily', {})
    dates = data.get('time', [])

    if not dates:
        return []

    sunshine_raw = data.get('sunshine_duration', [])
    results = []
    for i, d in enumerate(dates):
        # 日照: 秒 → 時間
        sun_sec = sunshine_raw[i]
        sunshine_h = round(sun_sec / 3600, 2) if sun_sec is not None else None

        results.append({
            'date':         d,
            'temp_mean':    data['temperature_2m_mean'][i],
            'temp_max':     data['temperature_2m_max'][i],
            'temp_min':     data['temperature_2m_min'][i],
            'sunshine_h':   sunshine_h,
            'precip_mm':    data['precipitation_sum'][i],
            'wind_max':     data['wind_speed_10m_max'][i],
            'pressure_min': data['surface_pressure_min'][i],
        })
    return results


def upsert_records(records: list[dict], stdout=None) -> int:
    """WeatherRecord に upsert し、保存件数を返す。"""
    saved = 0
    for item in records:
        record, created = WeatherRecord.objects.get_or_create(date=item['date'])
        for field in ['temp_mean', 'temp_max', 'temp_min',
                       'sunshine_h', 'precip_mm', 'wind_max', 'pressure_min']:
            val = item.get(field)
            if val is not None:
                setattr(record, field, val)
        record.save()
        saved += 1
        if stdout and created:
            stdout.write(f'  new: {item["date"]}')
    return saved


class Command(BaseCommand):
    help = '気象データを Open-Meteo から取得して DB に保存する'

    def add_arguments(self, parser):
        parser.add_argument(
            '--full',
            action='store_true',
            help=f'2016-01-01 から昨日まで全件取得（初回インポート用）',
        )
        parser.add_argument(
            '--start-date',
            type=str,
            help='取得開始日 (YYYY-MM-DD)。省略時は最終レコードの翌日',
        )
        parser.add_argument(
            '--end-date',
            type=str,
            help='取得終了日 (YYYY-MM-DD)。省略時は昨日',
        )

    def handle(self, *args, **options):
        yesterday = (datetime.date.today() - datetime.timedelta(days=1)).isoformat()

        # 終了日
        end_date = options.get('end_date') or yesterday

        # 開始日の決定
        if options['full']:
            start_date = FULL_START
        elif options.get('start_date'):
            start_date = options['start_date']
        else:
            # 最終レコードの翌日を自動算出
            last = WeatherRecord.objects.order_by('-date').first()
            if last:
                start_date = (last.date + datetime.timedelta(days=1)).isoformat()
            else:
                start_date = FULL_START
                self.stdout.write(
                    self.style.WARNING('DB にデータがないため 2016-01-01 から取得します。')
                )

        if start_date > end_date:
            self.stdout.write(self.style.SUCCESS('すでに最新の状態です。取得をスキップします。'))
            return

        self.stdout.write(f'取得期間: {start_date} 〜 {end_date}')

        # Open-Meteo は 1回のリクエストで最大1年分程度が安定。
        # 長期間の場合は年単位で分割して取得する。
        start = datetime.date.fromisoformat(start_date)
        end   = datetime.date.fromisoformat(end_date)
        total_saved = 0

        current = start
        while current <= end:
            chunk_end = min(
                datetime.date(current.year, 12, 31),
                end
            )
            self.stdout.write(f'  → {current} 〜 {chunk_end} を取得中...')
            records = fetch_from_api(current.isoformat(), chunk_end.isoformat())
            saved = upsert_records(records, stdout=None)
            total_saved += saved
            self.stdout.write(f'     {saved} 件保存')
            current = datetime.date(chunk_end.year + 1, 1, 1)

        self.stdout.write(self.style.SUCCESS(f'完了: 合計 {total_saved} 件を保存しました。'))
