import uuid
from django.test import Client
from django.urls import reverse
from .tests import InventoryFixture
from .test_control import ControlFixture
from .models import StockCount, StockAdjustment


class ControlAdminTests(ControlFixture, InventoryFixture):
    def setUp(self):
        self.client.force_login(self.actor)

    def test_movement_command_key_is_generated_and_hidden(self):
        form = self.client.get(reverse("admin:inventory_stockmovement_add")).context["adminform"].form
        self.assertTrue(form.fields["idempotency_key"].widget.is_hidden)
        self.assertIsInstance(form.fields["idempotency_key"].initial(), uuid.UUID)

    def test_adjustment_service_and_signed_stock_snapshot(self):
        url = reverse("admin:inventory_stockadjustment_add")
        response = self.client.get(url)
        token = response.context["adminform"].form.initial["revisions"]
        response = self.client.post(url, {"location": str(self.location.pk), "spare_part": str(self.part.pk), "quantity_delta": "2", "reason": "FOUND",
            "reference": "FOUND-ADMIN", "note": "Counted found stock", "command_key": str(uuid.uuid4()), "revisions": token, "_save": "Save"})
        self.assertEqual(response.status_code, 302)
        self.assertEqual(StockAdjustment.objects.get().quantity_delta, 2)

    def test_stale_adjustment_form_cannot_overwrite_new_stock(self):
        url = reverse("admin:inventory_stockadjustment_add")
        token = self.client.get(url).context["adminform"].form.initial["revisions"]
        self.receive()
        self.client.post(url, {"location": str(self.location.pk), "spare_part": str(self.part.pk), "quantity_delta": "1", "reason": "FOUND",
            "reference": "STALE", "note": "Old stock view", "command_key": str(uuid.uuid4()), "revisions": token, "_save": "Save"})
        self.assertFalse(StockAdjustment.objects.exists())

    def test_manual_count_variance_reason_not_offered(self):
        form = self.client.get(reverse("admin:inventory_stockadjustment_add")).context["adminform"].form
        self.assertNotIn("COUNT_VARIANCE", [key for key, _ in form.fields["reason"].choices])

    def test_create_count_through_admin(self):
        response = self.client.post(reverse("admin:inventory_stockcount_add"), {"location": str(self.location.pk), "spare_part": str(self.part.pk), "note": "Physical count", "_save": "Save"})
        self.assertEqual(response.status_code, 302)
        self.assertEqual(StockCount.objects.get().status, "DRAFT")

    def confirm(self, row, action, **extra):
        url = reverse("admin:inventory_stockcount_changelist")
        data = {"action": action, "_selected_action": str(row.pk)}
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, 200)
        return self.client.post(url, data | {"confirm_transition": "1", "transition_revision": response.context_data["transition_revision"]} | extra)

    def test_count_start_record_and_reconcile(self):
        self.receive(3)
        row = self.draft_count()
        self.confirm(row, "start_selected")
        row.refresh_from_db()
        self.assertEqual(row.status, "COUNTING")
        url = reverse("admin:inventory_stockcount_change", args=[row.pk])
        token = self.client.get(url).context["adminform"].form.initial["revision"]
        response = self.client.post(url, {"counted_quantity": "2", "note": "One missing", "revision": token, "_save": "Save"})
        self.assertEqual(response.status_code, 302)
        self.confirm(row, "reconcile_selected")
        row.refresh_from_db()
        self.assertEqual(row.status, "RECONCILED")
        self.assertEqual(self.balance(), 2)

    def test_count_stale_record_form_rejected(self):
        row = self.start_count()
        url = reverse("admin:inventory_stockcount_change", args=[row.pk])
        token = self.client.get(url).context["adminform"].form.initial["revision"]
        self.record_count(row, 0)
        self.client.post(url, {"counted_quantity": "9", "note": "Stale", "revision": token, "_save": "Save"})
        row.refresh_from_db()
        self.assertEqual(row.counted_quantity, 0)

    def test_count_cancel_requires_reason(self):
        row = self.start_count()
        self.confirm(row, "cancel_selected", reason="")
        row.refresh_from_db()
        self.assertEqual(row.status, "COUNTING")
        self.confirm(row, "cancel_selected", reason="Interrupted")
        row.refresh_from_db()
        self.assertEqual(row.status, "CANCELLED")

    def test_terminal_count_and_adjustment_readonly(self):
        count = self.reconcile(self.record_count(self.start_count(), 0))
        adjustment = self.adjust(1)
        for name, row in (("stockcount", count), ("stockadjustment", adjustment)):
            self.assertEqual(self.client.post(reverse(f"admin:inventory_{name}_change", args=[row.pk]), {"note": "Rewrite"}).status_code, 403)
            self.assertEqual(self.client.post(reverse(f"admin:inventory_{name}_delete", args=[row.pk]), {"post": "yes"}).status_code, 403)

    def test_control_forms_require_csrf(self):
        client = Client(enforce_csrf_checks=True)
        client.force_login(self.actor)
        for name in ("stockcount", "stockadjustment"):
            self.assertEqual(client.post(reverse(f"admin:inventory_{name}_add"), {}).status_code, 403)
