from decimal import Decimal, InvalidOperation

from django.http import HttpResponse
from django.template.loader import render_to_string
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from weasyprint import HTML

from apps.fields.models import Field
from apps.plans.models import Plan, Variety
from .models import Fertilizer, FertilizationPlan
from .serializers import (
    FertilizerSerializer,
    FertilizationPlanSerializer,
    FertilizationPlanWriteSerializer,
)


class FertilizerViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated]
    queryset = Fertilizer.objects.all()
    serializer_class = FertilizerSerializer


class FertilizationPlanViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        qs = FertilizationPlan.objects.select_related('variety', 'variety__crop').prefetch_related(
            'entries', 'entries__field', 'entries__fertilizer'
        )
        year = self.request.query_params.get('year')
        if year:
            qs = qs.filter(year=year)
        return qs

    def get_serializer_class(self):
        if self.action in ['create', 'update', 'partial_update']:
            return FertilizationPlanWriteSerializer
        return FertilizationPlanSerializer

    @action(detail=True, methods=['get'])
    def pdf(self, request, pk=None):
        plan = self.get_object()
        entries = plan.entries.select_related('field', 'fertilizer').order_by(
            'field__display_order', 'field__id', 'fertilizer__name'
        )

        # 圃場・肥料の一覧を整理
        fields_map = {}
        fertilizers_map = {}
        for entry in entries:
            fields_map[entry.field_id] = entry.field
            fertilizers_map[entry.fertilizer_id] = entry.fertilizer

        fields = sorted(fields_map.values(), key=lambda f: (f.display_order, f.id))
        fertilizers = sorted(fertilizers_map.values(), key=lambda f: f.name)

        # マトリクスデータ生成
        matrix = {}
        for entry in entries:
            matrix[(entry.field_id, entry.fertilizer_id)] = entry.bags

        rows = []
        for field in fields:
            cells = [matrix.get((field.id, fert.id), '') for fert in fertilizers]
            total = sum(v for v in cells if v != '')
            rows.append({
                'field': field,
                'cells': cells,
                'total': total,
            })

        # 肥料ごとの合計
        fert_totals = []
        for fert in fertilizers:
            total = sum(
                matrix.get((field.id, fert.id), Decimal('0'))
                for field in fields
            )
            fert_totals.append(total)

        context = {
            'plan': plan,
            'fertilizers': fertilizers,
            'rows': rows,
            'fert_totals': fert_totals,
            'grand_total': sum(fert_totals),
        }
        html_string = render_to_string('fertilizer/pdf.html', context)
        pdf_file = HTML(string=html_string).write_pdf()
        response = HttpResponse(pdf_file, content_type='application/pdf')
        response['Content-Disposition'] = f'attachment; filename="fertilization_{plan.year}_{plan.id}.pdf"'
        return response


class CandidateFieldsView(APIView):
    """作付け計画から圃場候補を返す"""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        year = request.query_params.get('year')
        variety_id = request.query_params.get('variety_id')
        if not year or not variety_id:
            return Response({'error': 'year と variety_id が必要です'}, status=status.HTTP_400_BAD_REQUEST)

        field_ids = Plan.objects.filter(
            year=year, variety_id=variety_id
        ).values_list('field_id', flat=True)

        fields = Field.objects.filter(id__in=field_ids).order_by('display_order', 'id')
        data = [
            {
                'id': f.id,
                'name': f.name,
                'area_tan': str(f.area_tan),
                'area_m2': f.area_m2,
                'group_name': f.group_name,
            }
            for f in fields
        ]
        return Response(data)


class CalculateView(APIView):
    """自動計算（保存しない）"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        method = request.data.get('method')   # 'nitrogen' | 'even' | 'per_tan'
        param = request.data.get('param')     # 数値パラメータ
        fertilizer_id = request.data.get('fertilizer_id')
        field_ids = request.data.get('field_ids', [])

        if not method or param is None or not field_ids:
            return Response({'error': 'method, param, field_ids が必要です'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            param = Decimal(str(param))
        except InvalidOperation:
            return Response({'error': 'param は数値で指定してください'}, status=status.HTTP_400_BAD_REQUEST)

        fields = Field.objects.filter(id__in=field_ids)
        if not fields.exists():
            return Response({'error': '圃場が見つかりません'}, status=status.HTTP_400_BAD_REQUEST)

        results = []

        if method == 'per_tan':
            # 反当袋数配分: S = Sa × A
            for field in fields:
                area = Decimal(str(field.area_tan))
                bags = (param * area).quantize(Decimal('0.01'))
                results.append({'field_id': field.id, 'bags': float(bags)})

        elif method == 'even':
            # 在庫/指定数量均等配分: S = (SS / Sum(A)) × A
            total_area = sum(Decimal(str(f.area_tan)) for f in fields)
            if total_area == 0:
                return Response({'error': '圃場の面積が0です'}, status=status.HTTP_400_BAD_REQUEST)
            for field in fields:
                area = Decimal(str(field.area_tan))
                bags = (param * area / total_area).quantize(Decimal('0.01'))
                results.append({'field_id': field.id, 'bags': float(bags)})

        elif method == 'nitrogen':
            # 反当チッソ成分量配分: S = (Nr / (C × Nd/100)) × A
            if not fertilizer_id:
                return Response({'error': 'nitrogen 方式には fertilizer_id が必要です'}, status=status.HTTP_400_BAD_REQUEST)
            try:
                fertilizer = Fertilizer.objects.get(id=fertilizer_id)
            except Fertilizer.DoesNotExist:
                return Response({'error': '肥料が見つかりません'}, status=status.HTTP_404_NOT_FOUND)
            if not fertilizer.capacity_kg or not fertilizer.nitrogen_pct:
                return Response(
                    {'error': 'この肥料には1袋重量(kg)と窒素含有率(%)の登録が必要です'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            c = Decimal(str(fertilizer.capacity_kg))
            nd = Decimal(str(fertilizer.nitrogen_pct))
            # 1袋あたりの窒素量 (kg)
            nc = c * nd / Decimal('100')
            if nc == 0:
                return Response({'error': '窒素含有量が0のため計算できません'}, status=status.HTTP_400_BAD_REQUEST)
            for field in fields:
                area = Decimal(str(field.area_tan))
                bags = (param / nc * area).quantize(Decimal('0.01'))
                results.append({'field_id': field.id, 'bags': float(bags)})

        else:
            return Response({'error': 'method は nitrogen / even / per_tan のいずれかです'}, status=status.HTTP_400_BAD_REQUEST)

        return Response(results)
