import secrets
import math
import datetime
from statistics import mean, stdev

from django.conf import settings
from django.db.models import Avg, Sum, Max, Min, Count, Q
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import BasePermission, IsAuthenticated

from .models import WeatherRecord
from .serializers import WeatherRecordSerializer, WeatherSyncSerializer


# ──────────────────────────────────────────────
# 認証クラス
# ──────────────────────────────────────────────

class WeatherAPIKeyPermission(BasePermission):
    """X-API-Key ヘッダーで認証（Windmill向け）"""

    def has_permission(self, request, view):
        key = request.headers.get('X-API-Key', '')
        expected = getattr(settings, 'MAIL_API_KEY', '')  # 既存キーを流用
        if not key or not expected:
            return False
        return secrets.compare_digest(key, expected)


# ──────────────────────────────────────────────
# Windmill 向け: 日次データ同期 (POST)
# ──────────────────────────────────────────────

class WeatherSyncView(APIView):
    """
    POST /api/weather/sync/
    Windmill から気象データを受け取り upsert する。
    単一オブジェクト、またはリスト形式どちらでも受け付ける。
    """
    permission_classes = [WeatherAPIKeyPermission]

    def post(self, request):
        data = request.data
        if isinstance(data, dict):
            data = [data]

        saved = 0
        errors = []
        for item in data:
            date_val = item.get('date')
            if not date_val:
                errors.append({'item': item, 'error': 'date is required'})
                continue
            try:
                record, _ = WeatherRecord.objects.get_or_create(date=date_val)
                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, float(val))
                record.save()
                saved += 1
            except Exception as e:
                errors.append({'item': item, 'error': str(e)})

        result = {'saved': saved}
        if errors:
            result['errors'] = errors
        code = status.HTTP_201_CREATED if saved > 0 else status.HTTP_400_BAD_REQUEST
        return Response(result, status=code)


# ──────────────────────────────────────────────
# フロントエンド向け: 日次レコード一覧 (GET)
# ──────────────────────────────────────────────

class WeatherRecordListView(APIView):
    """
    GET /api/weather/records/?year=2025
    GET /api/weather/records/?start=2025-01-01&end=2025-12-31
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        qs = WeatherRecord.objects.all()

        year = request.query_params.get('year')
        start = request.query_params.get('start')
        end = request.query_params.get('end')

        if year:
            qs = qs.filter(date__year=int(year))
        if start:
            qs = qs.filter(date__gte=start)
        if end:
            qs = qs.filter(date__lte=end)

        serializer = WeatherRecordSerializer(qs, many=True)
        return Response(serializer.data)


# ──────────────────────────────────────────────
# フロントエンド向け: 月別サマリー (GET)
# ──────────────────────────────────────────────

class WeatherSummaryView(APIView):
    """
    GET /api/weather/summary/?year=2025
    月別集計（平均気温、降水量合計、日照合計、猛暑日・冬日数など）
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        year = request.query_params.get('year')
        if not year:
            return Response({'error': 'year parameter is required'}, status=status.HTTP_400_BAD_REQUEST)

        qs = WeatherRecord.objects.filter(date__year=int(year))

        monthly = []
        for month in range(1, 13):
            mqs = qs.filter(date__month=month)
            agg = mqs.aggregate(
                temp_mean_avg=Avg('temp_mean'),
                temp_max_avg=Avg('temp_max'),
                temp_min_avg=Avg('temp_min'),
                precip_total=Sum('precip_mm'),
                sunshine_total=Sum('sunshine_h'),
                wind_max=Max('wind_max'),
            )
            hot_days  = mqs.filter(temp_max__gte=35).count()
            cold_days = mqs.filter(temp_min__lt=0).count()
            rainy_days = mqs.filter(precip_mm__gte=1.0).count()

            monthly.append({
                'month': month,
                **{k: (round(v, 1) if v is not None else None) for k, v in agg.items()},
                'hot_days': hot_days,
                'cold_days': cold_days,
                'rainy_days': rainy_days,
            })

        # 年間サマリー
        year_agg = qs.aggregate(
            temp_mean_avg=Avg('temp_mean'),
            precip_total=Sum('precip_mm'),
            sunshine_total=Sum('sunshine_h'),
        )
        annual = {k: (round(v, 1) if v is not None else None) for k, v in year_agg.items()}
        annual['hot_days']  = qs.filter(temp_max__gte=35).count()
        annual['cold_days'] = qs.filter(temp_min__lt=0).count()

        return Response({'year': int(year), 'monthly': monthly, 'annual': annual})


# ──────────────────────────────────────────────
# 積算温度 (GDD) (GET)
# ──────────────────────────────────────────────

class WeatherGDDView(APIView):
    """
    GET /api/weather/gdd/?start_date=2025-05-01&base_temp=10&end_date=2025-09-30

    起算日から指定日（省略時は昨日）までの有効積算温度を返す。
    日積算温度 = max(0, 平均気温 - 基準温度)
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        start_date = request.query_params.get('start_date')
        if not start_date:
            return Response({'error': 'start_date is required'}, status=status.HTTP_400_BAD_REQUEST)

        base_temp = float(request.query_params.get('base_temp', 0.0))
        end_date  = request.query_params.get('end_date') or (
            datetime.date.today() - datetime.timedelta(days=1)
        ).isoformat()

        records = WeatherRecord.objects.filter(
            date__gte=start_date,
            date__lte=end_date,
            temp_mean__isnull=False,
        ).values('date', 'temp_mean')

        cumulative = 0.0
        daily_records = []
        for r in records:
            daily_gdd = max(0.0, r['temp_mean'] - base_temp)
            cumulative += daily_gdd
            daily_records.append({
                'date': r['date'],
                'temp_mean': r['temp_mean'],
                'daily_gdd': round(daily_gdd, 2),
                'cumulative_gdd': round(cumulative, 2),
            })

        return Response({
            'start_date': start_date,
            'end_date': end_date,
            'base_temp': base_temp,
            'total_gdd': round(cumulative, 2),
            'records': daily_records,
        })


# ──────────────────────────────────────────────
# 類似年分析 (GET)
# ──────────────────────────────────────────────

def _year_feature(records_qs, month_from, month_to, day_to):
    """
    指定した月/日の範囲の特徴量ベクトルを返す。
    month_from=1, month_to=2, day_to=28 なら 1/1〜2/28 のデータを使う。
    返り値: (mean_temp, total_precip, total_sunshine) または None
    """
    # 1/1 から month_to/day_to まで
    qs = records_qs.filter(
        Q(date__month__lt=month_to) |
        Q(date__month=month_to, date__day__lte=day_to)
    )
    agg = qs.aggregate(
        mean_temp=Avg('temp_mean'),
        total_precip=Sum('precip_mm'),
        total_sunshine=Sum('sunshine_h'),
    )
    if any(v is None for v in agg.values()):
        return None
    return (agg['mean_temp'], agg['total_precip'], agg['total_sunshine'])


class WeatherSimilarityView(APIView):
    """
    GET /api/weather/similarity/?year=2026

    今年の 1/1〜今日 の気象パターンと過去年を比較し、
    最も似た上位3年とその年の残期間の実績データを返す。
    開花・収穫予測の参考情報として使う。
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        target_year = int(request.query_params.get('year', datetime.date.today().year))
        today = datetime.date.today()

        # 比較基準日（今年の場合は昨日まで、過去年は12/31まで）
        if target_year == today.year:
            compare_end = today - datetime.timedelta(days=1)
        else:
            compare_end = datetime.date(target_year, 12, 31)

        limit_month = compare_end.month
        limit_day   = compare_end.day

        # 対象年の特徴量
        target_qs = WeatherRecord.objects.filter(date__year=target_year)
        target_feat = _year_feature(target_qs, 1, limit_month, limit_day)
        if target_feat is None:
            return Response({'error': f'{target_year} 年のデータが不足しています'}, status=status.HTTP_400_BAD_REQUEST)

        # 全年リスト（対象年を除く）
        all_years = (
            WeatherRecord.objects
            .exclude(date__year=target_year)
            .values_list('date__year', flat=True)
            .distinct()
            .order_by('date__year')
        )

        year_features = {}
        for yr in all_years:
            qs = WeatherRecord.objects.filter(date__year=yr)
            feat = _year_feature(qs, 1, limit_month, limit_day)
            if feat is not None:
                year_features[yr] = feat

        if not year_features:
            return Response({'error': '比較可能な過去データがありません'}, status=status.HTTP_404_NOT_FOUND)

        # 正規化: 各特徴量の平均・標準偏差
        all_feats = list(year_features.values()) + [target_feat]
        n_features = 3
        feat_means = [mean(f[i] for f in all_feats) for i in range(n_features)]
        feat_stds  = []
        for i in range(n_features):
            vals = [f[i] for f in all_feats]
            sd = stdev(vals) if len(vals) > 1 else 1.0
            feat_stds.append(sd if sd > 0 else 1.0)

        def normalize(feat):
            return tuple((feat[i] - feat_means[i]) / feat_stds[i] for i in range(n_features))

        target_norm = normalize(target_feat)

        distances = {}
        for yr, feat in year_features.items():
            norm = normalize(feat)
            dist = math.sqrt(sum((target_norm[i] - norm[i]) ** 2 for i in range(n_features)))
            distances[yr] = dist

        top3 = sorted(distances.items(), key=lambda x: x[1])[:3]

        # 類似年それぞれの月別統計を返す
        result_years = []
        for yr, dist in top3:
            qs = WeatherRecord.objects.filter(date__year=yr)
            monthly = []
            for month in range(1, 13):
                mqs = qs.filter(date__month=month)
                agg = mqs.aggregate(
                    temp_mean_avg=Avg('temp_mean'),
                    temp_max_avg=Avg('temp_max'),
                    precip_total=Sum('precip_mm'),
                    sunshine_total=Sum('sunshine_h'),
                )
                hot_days   = mqs.filter(temp_max__gte=35).count()
                storm_days = mqs.filter(wind_max__gte=10, precip_mm__gte=30).count()
                monthly.append({
                    'month': month,
                    **{k: (round(v, 1) if v is not None else None) for k, v in agg.items()},
                    'hot_days': hot_days,
                    'storm_days': storm_days,
                })

            feat = year_features[yr]
            result_years.append({
                'year': yr,
                'distance': round(dist, 3),
                'features': {
                    'mean_temp': round(feat[0], 2),
                    'total_precip': round(feat[1], 1),
                    'total_sunshine': round(feat[2], 1),
                },
                'monthly': monthly,
            })

        target_feat_dict = {
            'mean_temp': round(target_feat[0], 2),
            'total_precip': round(target_feat[1], 1),
            'total_sunshine': round(target_feat[2], 1),
        }

        return Response({
            'target_year': target_year,
            'comparison_period': f'1/1〜{limit_month}/{limit_day}',
            'target_features': target_feat_dict,
            'similar_years': result_years,
        })
