from django.contrib.auth import get_user_model
from django.utils import timezone
from rest_framework.test import APITestCase

from apps.boletas.models import Boleta
from apps.boletas.utils import build_folio
from apps.catalogos.models import Bodega, Contrato, ContratoVariedad, Productor, Variedad, Zafra
from apps.recepciones.models import PesoRecepcion, Recepcion
from apps.traspasos.models import AjusteTraspaso, Traspaso

User = get_user_model()


class TraspasosTests(APITestCase):
    def setUp(self):
        self.admin = User.objects.create_superuser(
            username="admin",
            email="admin@example.com",
            password="admin12345",
        )

        resp = self.client.post(
            "/auth/token/",
            {"username": "admin", "password": "admin12345"},
            format="json",
        )
        self.token = resp.data["access"]
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {self.token}")

        self.origen = Bodega.objects.create(codigo="B1", nombre="Bodega 1")
        self.destino = Bodega.objects.create(codigo="B2", nombre="Bodega 2")

        self.zafra = Zafra.objects.create(nombre="Z 2026", fecha_inicio="2026-01-01", fecha_fin="2026-12-31")
        self.variedad = Variedad.objects.create(nombre="V1")
        self.productor = Productor.objects.create(nombre="Prod 1", rfc="ABC123456T12")
        self.contrato = Contrato.objects.create(
            numero_contrato="C-1",
            productor=self.productor,
            zafra=self.zafra,
            activo=True,
        )
        ContratoVariedad.objects.create(contrato=self.contrato, variedad=self.variedad)
        self.boleta = Boleta.objects.create(
            zafra=self.zafra,
            productor=self.productor,
            contrato=self.contrato,
            folio=build_folio(None, self.productor.id, self.zafra.fecha_inicio.year, 1),
            estado=Boleta.ESTADO_CERRADA,
        )
        self.recepcion = Recepcion.objects.create(
            boleta=self.boleta,
            estado=Recepcion.ESTADO_CERRADA,
            bodega_destino=self.origen,
        )
        PesoRecepcion.objects.create(
            recepcion=self.recepcion,
            tipo=PesoRecepcion.TIPO_LLENO,
            valor=2000,
            es_automatico=True,
        )
        PesoRecepcion.objects.create(
            recepcion=self.recepcion,
            tipo=PesoRecepcion.TIPO_VACIO,
            valor=0,
            es_automatico=True,
        )

    def test_no_permite_origen_destino_iguales(self):
        resp = self.client.post(
            "/traspasos/",
            {
                "origen_id": self.origen.id,
                "destino_id": self.origen.id,
                "fecha_programada": timezone.now().isoformat(),
                "items": [
                    {
                        "variedad_id": self.variedad.id,
                        "boleta_referencia_id": self.boleta.id,
                        "kilos_programados": "1000.00",
                    }
                ],
            },
            format="json",
        )
        self.assertEqual(resp.status_code, 422)

    def test_crear_traspaso_sin_validar_stock_insuficiente(self):
        resp_ok = self.client.post(
            "/traspasos/",
            {
                "origen_id": self.origen.id,
                "destino_id": self.destino.id,
                "fecha_programada": timezone.now().isoformat(),
                "items": [
                    {
                        "variedad_id": self.variedad.id,
                        "boleta_referencia_id": self.boleta.id,
                        "kilos_programados": "1500.00",
                    }
                ],
            },
            format="json",
        )
        self.assertEqual(resp_ok.status_code, 201)

        resp_bad = self.client.post(
            "/traspasos/",
            {
                "origen_id": self.origen.id,
                "destino_id": self.destino.id,
                "fecha_programada": timezone.now().isoformat(),
                "items": [
                    {
                        "variedad_id": self.variedad.id,
                        "boleta_referencia_id": self.boleta.id,
                        "kilos_programados": "600.00",
                    }
                ],
            },
            format="json",
        )
        self.assertEqual(resp_bad.status_code, 201)

    def test_flujo_salida_y_llegada_sin_alerta(self):
        resp = self.client.post(
            "/traspasos/",
            {
                "origen_id": self.origen.id,
                "destino_id": self.destino.id,
                "fecha_programada": timezone.now().isoformat(),
                "items": [
                    {
                        "variedad_id": self.variedad.id,
                        "boleta_referencia_id": self.boleta.id,
                        "kilos_programados": "500.00",
                    }
                ],
            },
            format="json",
        )
        self.assertEqual(resp.status_code, 201)
        tid = resp.data["id"]

        resp_s = self.client.post(
            f"/traspasos/{tid}/registrar-salida/",
            {"peso_salida": "1000.00", "es_automatico": True},
            format="json",
        )
        self.assertEqual(resp_s.status_code, 200)
        self.assertEqual(resp_s.data["estado"], Traspaso.ESTADO_SALIDA_REGISTRADA)

        resp_l = self.client.post(
            f"/traspasos/{tid}/registrar-llegada/",
            {"peso_llegada": "1000.00"},
            format="json",
        )
        self.assertEqual(resp_l.status_code, 200)
        self.assertEqual(resp_l.data["estado"], Traspaso.ESTADO_COMPLETADO)

    def _crear_traspaso_para_alerta(self):
        resp = self.client.post(
            "/traspasos/",
            {
                "origen_id": self.origen.id,
                "destino_id": self.destino.id,
                "fecha_programada": timezone.now().isoformat(),
                "items": [
                    {
                        "variedad_id": self.variedad.id,
                        "boleta_referencia_id": self.boleta.id,
                        "kilos_programados": "200.00",
                    }
                ],
            },
            format="json",
        )
        tid = resp.data["id"]
        self.client.post(
            f"/traspasos/{tid}/registrar-salida/",
            {"peso_salida": "1000.00", "es_automatico": True},
            format="json",
        )
        resp_l = self.client.post(
            f"/traspasos/{tid}/registrar-llegada/",
            {"peso_llegada": "1100.00"},
            format="json",
        )
        self.assertEqual(resp_l.status_code, 409)
        return tid

    def test_alerta_y_aceptar(self):
        tid = self._crear_traspaso_para_alerta()
        resp = self.client.post(f"/traspasos/{tid}/aceptar/", {}, format="json")
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.data["estado"], Traspaso.ESTADO_COMPLETADO)

    def test_alerta_y_ajustar(self):
        tid = self._crear_traspaso_para_alerta()
        resp = self.client.post(
            f"/traspasos/{tid}/ajustar/",
            {"kilos_ajustados": "1950.00", "motivo": "merma aceptada"},
            format="json",
        )
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.data["estado"], Traspaso.ESTADO_AJUSTADO)
        self.assertEqual(AjusteTraspaso.objects.filter(traspaso_id=tid).count(), 1)

    def test_alerta_y_disputa(self):
        tid = self._crear_traspaso_para_alerta()
        resp = self.client.post(
            f"/traspasos/{tid}/disputa/",
            {"motivo": "peso inconsistente"},
            format="json",
        )
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.data["estado"], Traspaso.ESTADO_DISPUTA)
