from decimal import Decimal

from django.contrib.auth import get_user_model
from django.test import TestCase
from rest_framework.test import APIClient

from apps.fields.models import Field
from apps.materials.models import Material, StockTransaction
from apps.materials.stock_service import create_reserves_for_plan
from apps.plans.models import Crop, Variety

from .models import FertilizationEntry, FertilizationPlan, Fertilizer


class FertilizationPlanMergeTests(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.user = get_user_model().objects.create_user(
            username='merge-user',
            password='secret12345',
        )
        self.client.force_authenticate(user=self.user)

        crop = Crop.objects.create(name='水稲')
        self.variety = Variety.objects.create(crop=crop, name='たちはるか特栽')
        self.field_a = Field.objects.create(
            name='足川北上',
            address='高知県高岡郡',
            area_tan='1.2000',
            area_m2=1200,
            owner_name='吉田',
            group_name='北',
            display_order=1,
        )
        self.field_b = Field.objects.create(
            name='足川南',
            address='高知県高岡郡',
            area_tan='0.8000',
            area_m2=800,
            owner_name='吉田',
            group_name='南',
            display_order=2,
        )
        material_a = Material.objects.create(
            name='高度化成14号',
            material_type=Material.MaterialType.FERTILIZER,
        )
        material_b = Material.objects.create(
            name='分げつ一発',
            material_type=Material.MaterialType.FERTILIZER,
        )
        self.fertilizer_a = Fertilizer.objects.create(name='高度化成14号', material=material_a)
        self.fertilizer_b = Fertilizer.objects.create(name='分げつ一発', material=material_b)

    def test_merge_into_moves_entries_and_deletes_empty_source_plan(self):
        target_plan = FertilizationPlan.objects.create(
            name='2026年度 たちはるか特栽 元肥',
            year=2026,
            variety=self.variety,
            calc_settings=[{'fertilizer_id': self.fertilizer_a.id, 'method': 'per_tan', 'param': '1.2'}],
        )
        source_plan = FertilizationPlan.objects.create(
            name='2026年度 たちはるか特栽 施肥計画（品種変更移動）',
            year=2026,
            variety=self.variety,
            calc_settings=[{'fertilizer_id': self.fertilizer_b.id, 'method': 'per_tan', 'param': '0.8'}],
        )
        target_entry = FertilizationEntry.objects.create(
            plan=target_plan,
            field=self.field_a,
            fertilizer=self.fertilizer_a,
            bags='3.00',
            actual_bags='1.0000',
        )
        source_entry = FertilizationEntry.objects.create(
            plan=source_plan,
            field=self.field_b,
            fertilizer=self.fertilizer_b,
            bags='2.00',
            actual_bags='2.0000',
        )
        create_reserves_for_plan(target_plan)
        create_reserves_for_plan(source_plan)

        response = self.client.post(
            f'/api/fertilizer/plans/{source_plan.id}/merge_into/',
            {'target_plan_id': target_plan.id},
            format='json',
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['moved_entry_count'], 1)
        self.assertTrue(response.data['deleted_source_plan'])

        source_entry.refresh_from_db()
        self.assertEqual(source_entry.plan_id, target_plan.id)
        self.assertFalse(FertilizationPlan.objects.filter(id=source_plan.id).exists())

        target_plan.refresh_from_db()
        self.assertEqual(
            target_plan.calc_settings,
            [
                {'fertilizer_id': self.fertilizer_a.id, 'method': 'per_tan', 'param': '1.2'},
                {'fertilizer_id': self.fertilizer_b.id, 'method': 'per_tan', 'param': '0.8'},
            ],
        )
        reserves = list(
            StockTransaction.objects.filter(
                fertilization_plan=target_plan,
                transaction_type=StockTransaction.TransactionType.RESERVE,
            ).order_by('material__name')
        )
        self.assertEqual(len(reserves), 2)
        self.assertEqual(
            {(reserve.material_id, reserve.quantity) for reserve in reserves},
            {
                (self.fertilizer_a.material_id, Decimal(str(target_entry.bags))),
                (self.fertilizer_b.material_id, Decimal(str(source_entry.bags))),
            },
        )

    def test_merge_into_stops_on_field_fertilizer_conflict(self):
        target_plan = FertilizationPlan.objects.create(
            name='2026年度 たちはるか特栽 元肥',
            year=2026,
            variety=self.variety,
        )
        source_plan = FertilizationPlan.objects.create(
            name='2026年度 たちはるか特栽 施肥計画（品種変更移動）',
            year=2026,
            variety=self.variety,
        )
        FertilizationEntry.objects.create(
            plan=target_plan,
            field=self.field_a,
            fertilizer=self.fertilizer_a,
            bags='3.00',
        )
        source_entry = FertilizationEntry.objects.create(
            plan=source_plan,
            field=self.field_a,
            fertilizer=self.fertilizer_a,
            bags='2.00',
        )

        response = self.client.post(
            f'/api/fertilizer/plans/{source_plan.id}/merge_into/',
            {'target_plan_id': target_plan.id},
            format='json',
        )

        self.assertEqual(response.status_code, 409)
        self.assertEqual(len(response.data['conflicts']), 1)
        source_entry.refresh_from_db()
        self.assertEqual(source_entry.plan_id, source_plan.id)
        self.assertTrue(FertilizationPlan.objects.filter(id=source_plan.id).exists())
