from django.contrib.auth import get_user_model
from django.test import TestCase
from rest_framework.test import APIRequestFactory, force_authenticate
from decimal import Decimal

from apps.fertilizer.models import FertilizationEntry, FertilizationPlan, Fertilizer
from apps.fields.models import Field
from apps.materials.models import Material, StockTransaction
from apps.materials.stock_service import create_reserves_for_plan
from .models import (
    Crop,
    Plan,
    PlanVarietyChange,
    RiceTransplantEntry,
    RiceTransplantPlan,
    Variety,
)
from .serializers import PlanSerializer
from .views import PlanViewSet


class PlanVarietyChangeTests(TestCase):
    def setUp(self):
        self.factory = APIRequestFactory()
        self.user = get_user_model().objects.create_user(
            username='tester',
            password='secret12345',
        )
        self.crop = Crop.objects.create(name='水稲')
        self.old_variety = Variety.objects.create(crop=self.crop, name='にこまる')
        self.new_variety = Variety.objects.create(crop=self.crop, name='たちはるか特栽')
        self.field = Field.objects.create(
            name='足川北上',
            address='高知県高岡郡',
            area_tan='1.2000',
            area_m2=1200,
            owner_name='吉田',
            group_name='北',
            display_order=1,
        )
        self.plan = Plan.objects.create(
            field=self.field,
            year=2026,
            crop=self.crop,
            variety=self.old_variety,
            notes='',
        )
        self.other_field = Field.objects.create(
            name='足川南',
            address='高知県高岡郡',
            area_tan='0.8000',
            area_m2=800,
            owner_name='吉田',
            group_name='南',
            display_order=2,
        )

    def test_serializer_update_creates_history_when_variety_changes(self):
        serializer = PlanSerializer(
            instance=self.plan,
            data={'variety': self.new_variety.id},
            partial=True,
        )
        self.assertTrue(serializer.is_valid(), serializer.errors)
        serializer.save()

        self.plan.refresh_from_db()
        self.assertEqual(self.plan.variety_id, self.new_variety.id)

        change = PlanVarietyChange.objects.get(plan=self.plan)
        self.assertEqual(change.field_id, self.field.id)
        self.assertEqual(change.year, 2026)
        self.assertEqual(change.old_variety_id, self.old_variety.id)
        self.assertEqual(change.new_variety_id, self.new_variety.id)
        self.assertEqual(change.fertilizer_moved_entry_count, 0)

    def test_serializer_update_does_not_create_history_without_variety_change(self):
        serializer = PlanSerializer(
            instance=self.plan,
            data={'notes': 'メモ更新'},
            partial=True,
        )
        self.assertTrue(serializer.is_valid(), serializer.errors)
        serializer.save()

        self.plan.refresh_from_db()
        self.assertEqual(self.plan.notes, 'メモ更新')
        self.assertFalse(PlanVarietyChange.objects.exists())

    def test_bulk_update_creates_history_for_existing_plan(self):
        view = PlanViewSet.as_view({'post': 'bulk_update'})
        request = self.factory.post(
            '/api/plans/bulk_update/',
            {
                'field_ids': [self.field.id],
                'year': 2026,
                'crop': self.crop.id,
                'variety': self.new_variety.id,
            },
            format='json',
        )
        force_authenticate(request, user=self.user)

        response = view(request)

        self.assertEqual(response.status_code, 200)
        self.plan.refresh_from_db()
        self.assertEqual(self.plan.variety_id, self.new_variety.id)

        change = PlanVarietyChange.objects.get(plan=self.plan)
        self.assertEqual(change.old_variety_id, self.old_variety.id)
        self.assertEqual(change.new_variety_id, self.new_variety.id)

    def test_serializer_update_moves_all_fertilizer_entries_for_target_field(self):
        material_target = Material.objects.create(
            name='高度化成14号',
            material_type=Material.MaterialType.FERTILIZER,
        )
        material_spread = Material.objects.create(
            name='分げつ一発',
            material_type=Material.MaterialType.FERTILIZER,
        )
        fertilizer_target = Fertilizer.objects.create(
            name='高度化成14号',
            material=material_target,
        )
        fertilizer_spread = Fertilizer.objects.create(
            name='分げつ一発',
            material=material_spread,
        )
        old_fertilization_plan = FertilizationPlan.objects.create(
            name='2026年度 にこまる 元肥',
            year=2026,
            variety=self.old_variety,
            calc_settings=[{'fertilizer_id': fertilizer_target.id, 'method': 'per_tan', 'param': '1.0'}],
        )
        target_entry = FertilizationEntry.objects.create(
            plan=old_fertilization_plan,
            field=self.field,
            fertilizer=fertilizer_target,
            bags='4.00',
            actual_bags=None,
        )
        spread_entry = FertilizationEntry.objects.create(
            plan=old_fertilization_plan,
            field=self.field,
            fertilizer=fertilizer_spread,
            bags='3.00',
            actual_bags='1.0000',
        )
        untouched_entry = FertilizationEntry.objects.create(
            plan=old_fertilization_plan,
            field=self.other_field,
            fertilizer=fertilizer_target,
            bags='2.00',
            actual_bags=None,
        )
        create_reserves_for_plan(old_fertilization_plan)

        serializer = PlanSerializer(
            instance=self.plan,
            data={'variety': self.new_variety.id},
            partial=True,
        )
        self.assertTrue(serializer.is_valid(), serializer.errors)
        serializer.save()

        change = PlanVarietyChange.objects.get(plan=self.plan)
        self.assertEqual(change.fertilizer_moved_entry_count, 2)

        old_fertilization_plan.refresh_from_db()
        new_plan = FertilizationPlan.objects.exclude(id=old_fertilization_plan.id).get(
            year=2026,
            variety=self.new_variety,
        )
        self.assertEqual(
            new_plan.name,
            f'2026年度 {self.new_variety.name} 施肥計画（品種変更移動）',
        )
        self.assertEqual(new_plan.calc_settings, old_fertilization_plan.calc_settings)

        target_entry.refresh_from_db()
        spread_entry.refresh_from_db()
        untouched_entry.refresh_from_db()

        self.assertEqual(target_entry.plan_id, new_plan.id)
        self.assertEqual(spread_entry.plan_id, new_plan.id)
        self.assertEqual(untouched_entry.plan_id, old_fertilization_plan.id)

        old_reserves = list(
            StockTransaction.objects.filter(
                fertilization_plan=old_fertilization_plan,
                transaction_type=StockTransaction.TransactionType.RESERVE,
            ).order_by('material__name')
        )
        new_reserves = list(
            StockTransaction.objects.filter(
                fertilization_plan=new_plan,
                transaction_type=StockTransaction.TransactionType.RESERVE,
            ).order_by('material__name')
        )
        self.assertEqual(len(old_reserves), 1)
        self.assertEqual(len(new_reserves), 2)
        self.assertEqual(
            {(reserve.material_id, reserve.quantity) for reserve in old_reserves},
            {
                (material_target.id, untouched_entry.bags),
            },
        )
        self.assertEqual(
            {(reserve.material_id, reserve.quantity) for reserve in new_reserves},
            {
                (material_target.id, target_entry.bags),
                (material_spread.id, spread_entry.bags),
            },
        )

    def test_serializer_update_moves_rice_transplant_entries_for_target_field(self):
        old_rice_plan = RiceTransplantPlan.objects.create(
            name='2026年度 にこまる 田植え計画',
            year=2026,
            variety=self.old_variety,
            default_seed_grams_per_box='200.00',
            seedling_boxes_per_tan='12.00',
            notes='旧計画メモ',
        )
        target_entry = RiceTransplantEntry.objects.create(
            plan=old_rice_plan,
            field=self.field,
            installed_seedling_boxes='14.40',
            seed_grams_per_box='200.00',
        )
        other_entry = RiceTransplantEntry.objects.create(
            plan=old_rice_plan,
            field=self.other_field,
            installed_seedling_boxes='9.60',
            seed_grams_per_box='200.00',
        )

        serializer = PlanSerializer(
            instance=self.plan,
            data={'variety': self.new_variety.id},
            partial=True,
        )
        self.assertTrue(serializer.is_valid(), serializer.errors)
        serializer.save()

        target_entry.refresh_from_db()
        other_entry.refresh_from_db()

        new_rice_plan = RiceTransplantPlan.objects.exclude(id=old_rice_plan.id).get(
            year=2026,
            variety=self.new_variety,
        )
        self.assertEqual(
            new_rice_plan.name,
            f'2026年度 {self.new_variety.name} 田植え計画（品種変更移動）',
        )
        self.assertEqual(new_rice_plan.default_seed_grams_per_box, Decimal('200.00'))
        self.assertEqual(new_rice_plan.seedling_boxes_per_tan, Decimal('12.00'))
        self.assertEqual(new_rice_plan.notes, old_rice_plan.notes)
        self.assertEqual(target_entry.plan_id, new_rice_plan.id)
        self.assertEqual(other_entry.plan_id, old_rice_plan.id)
