from unittest.mock import patch

from django.test import TestCase

from orders.services import OrderService


class OrderServiceTests(TestCase):
    def test_record_digistore_payment_requires_order_id(self):
        result = OrderService.record_digistore_payment({})
        self.assertEqual(result, {"ok": False, "error": "missing_order_id"})

    def test_purchase_detail_to_ipn_payload_maps_core_fields(self):
        purchase = {
            "id": "DS123",
            "amount_brutto": "71.97",
            "currency": "EUR",
            "buyer": {
                "email": "buyer@example.com",
                "first_name": "Ada",
                "last_name": "Lovelace",
                "street": "Main St 1",
                "city": "Berlin",
                "state": "BE",
                "zipcode": "10115",
                "country": "DE",
            },
            "items": [
                {"product_id": "111", "quantity": "2"},
                {"product_id": "222", "quantity": 1},
            ],
        }

        ipn = OrderService.purchase_detail_to_ipn_payload(purchase)

        self.assertEqual(ipn["event"], "on_payment")
        self.assertEqual(ipn["order_id"], "DS123")
        self.assertEqual(ipn["email"], "buyer@example.com")
        self.assertEqual(ipn["item_count"], "2")
        self.assertEqual(ipn["product_id"], "111")
        self.assertEqual(ipn["quantity"], "2")
        self.assertEqual(ipn["product_id_2"], "222")
        self.assertEqual(ipn["quantity_2"], "1")

    def test_should_skip_purchase_sync_for_refunded_local_status(self):
        from orders.models import Order

        order = Order(status="refunded", digistore_order_id="DS-R")
        skip, reason = OrderService.should_skip_purchase_sync(local_order=order)
        self.assertTrue(skip)
        self.assertEqual(reason, "local_status_refunded")

    def test_should_skip_purchase_sync_for_cancelled_digistore_status(self):
        skip, reason = OrderService.should_skip_purchase_sync(
            purchase={"billing_status": "cancelled"}
        )
        self.assertTrue(skip)
        self.assertEqual(reason, "digistore_billing_cancelled")

    def test_should_skip_purchase_sync_for_refund_transaction(self):
        skip, reason = OrderService.should_skip_purchase_sync(
            purchase={
                "billing_status": "completed",
                "transactions": [{"type": "refund", "amount": "-10"}],
            }
        )
        self.assertTrue(skip)
        self.assertEqual(reason, "digistore_transaction_refund")

    def test_only_skip_products_detects_693295_only_order(self):
        ipn = {
            "item_count": "1",
            "product_id": "693295",
            "quantity": "1",
        }
        self.assertTrue(OrderService._only_skip_products(ipn))

    def test_only_skip_products_false_when_mixed_items(self):
        ipn = {
            "item_count": "2",
            "product_id": "693295",
            "quantity": "1",
            "product_id_2": "675431",
            "quantity_2": "1",
        }
        self.assertFalse(OrderService._only_skip_products(ipn))

    def test_purchase_detail_to_ipn_payload_maps_item_amounts(self):
        purchase = {
            "id": "DS200",
            "amount": "39.98",
            "currency": "EUR",
            "items": [
                {
                    "product_id": "111",
                    "quantity": "2",
                    "amounts": {
                        "first": {
                            "total_brutto_amount": 28.16,
                            "total_netto_amount": 25.6,
                            "unit_brutto_amount": 14.08,
                        }
                    },
                }
            ],
        }
        ipn = OrderService.purchase_detail_to_ipn_payload(purchase)
        self.assertEqual(ipn["product_txn_amount"], "28.16")
        self.assertEqual(ipn["product_amount"], "14.08")

    def test_resolve_ipn_prefers_stored_priced_payload(self):
        from orders.models import Order

        priced_ipn = {
            "order_id": "DS300",
            "item_count": "1",
            "product_id": "111",
            "quantity": "1",
            "product_txn_amount": "19.99",
        }
        stripped = {
            "order_id": "DS300",
            "item_count": "1",
            "product_id": "111",
            "quantity": "1",
        }
        order = Order(digistore_order_id="DS300", raw_payload=priced_ipn)
        resolved = OrderService._resolve_ipn_for_sync(
            "DS300",
            ipn_data=stripped,
            purchase=None,
            local_order=order,
        )
        self.assertEqual(resolved["product_txn_amount"], "19.99")

    def test_sync_order_to_shopify_skips_when_shopify_id_exists(self):
        from orders.models import Order

        Order.objects.create(
            digistore_order_id="DS400",
            shopify_order_id="999",
            status="paid",
        )
        result = OrderService.sync_order_to_shopify(
            "DS400",
            fetch_purchase=False,
        )
        self.assertEqual(result["result"], "skipped_already_synced")
        self.assertEqual(result["shopify_order_id"], "999")


class SyncTrackingToDigistoreTests(TestCase):
    @patch("products.services.shopify_service.ShopifyService.get_order_fulfillment_tracking")
    @patch("products.services.digistore_service.DigistoreService.update_delivery")
    @patch("products.services.digistore_service.DigistoreService.list_deliveries_for_purchase")
    def test_sync_tracking_skips_shopify_fetch_when_override_provided(
        self,
        list_deliveries_mock,
        update_delivery_mock,
        graphql_mock,
    ):
        from orders.models import Order

        Order.objects.create(
            shopify_order_id="12890799898959",
            digistore_order_id="DS-TRACK",
            status="paid",
        )
        list_deliveries_mock.return_value = [
            {
                "id": 17328355,
                "type": "request",
                "quantity_total": 1,
                "quantity_delivered": 0,
            }
        ]
        update_delivery_mock.return_value = {
            "result": "success",
            "data": {"is_modified": "Y"},
        }

        result = OrderService.sync_tracking_to_digistore(
            shopify_order_id="12890799898959",
            tracking_id="1Z999AA10123456784",
            parcel_service="UPS",
            tracking_url="https://www.ups.com/track?tracknum=1Z999AA10123456784",
            notify_via_email=False,
        )

        graphql_mock.assert_not_called()
        self.assertTrue(result["ok"])
        self.assertEqual(result["tracking"]["parcel_service"], "ups")
        update_delivery_mock.assert_called_once()
        kwargs = update_delivery_mock.call_args.kwargs
        self.assertEqual(kwargs["tracking_id"], "1Z999AA10123456784")
        self.assertFalse(kwargs["notify_via_email"])
