# tests/integration/api/v1/storefront/test_orders.py
"""Integration tests for shop orders API endpoints.

Tests the /api/v1/storefront/orders/* endpoints.
All endpoints require customer JWT authentication with vendor context.
"""

from datetime import UTC, datetime, timedelta
from decimal import Decimal
from unittest.mock import patch, MagicMock

import pytest
from jose import jwt

from app.modules.customers.models.customer import Customer
from app.modules.orders.models import Invoice, InvoiceStatus, VendorInvoiceSettings
from app.modules.orders.models import Order, OrderItem


@pytest.fixture
def shop_customer(db, test_vendor):
    """Create a test customer for shop API tests."""
    from middleware.auth import AuthManager
    auth_manager = AuthManager()
    customer = Customer(
        vendor_id=test_vendor.id,
        email="shopcustomer@example.com",
        hashed_password=auth_manager.hash_password("testpass123"),
        first_name="Shop",
        last_name="Customer",
        customer_number="SHOP001",
        is_active=True,
    )
    db.add(customer)
    db.commit()
    db.refresh(customer)
    return customer


@pytest.fixture
def shop_customer_token(shop_customer, test_vendor):
    """Create JWT token for shop customer."""
    from middleware.auth import AuthManager
    auth_manager = AuthManager()

    expires_delta = timedelta(minutes=auth_manager.token_expire_minutes)
    expire = datetime.now(UTC) + expires_delta

    payload = {
        "sub": str(shop_customer.id),
        "email": shop_customer.email,
        "vendor_id": test_vendor.id,
        "type": "customer",
        "exp": expire,
        "iat": datetime.now(UTC),
    }

    token = jwt.encode(
        payload, auth_manager.secret_key, algorithm=auth_manager.algorithm
    )
    return token


@pytest.fixture
def shop_customer_headers(shop_customer_token):
    """Get authentication headers for shop customer."""
    return {"Authorization": f"Bearer {shop_customer_token}"}


@pytest.fixture
def shop_order(db, test_vendor, shop_customer):
    """Create a test order for shop customer."""
    order = Order(
        vendor_id=test_vendor.id,
        customer_id=shop_customer.id,
        order_number="SHOP-ORD-001",
        status="pending",
        channel="direct",
        order_date=datetime.now(UTC),
        subtotal_cents=10000,
        tax_amount_cents=1700,
        shipping_amount_cents=500,
        total_amount_cents=12200,
        currency="EUR",
        customer_email=shop_customer.email,
        customer_first_name=shop_customer.first_name,
        customer_last_name=shop_customer.last_name,
        ship_first_name=shop_customer.first_name,
        ship_last_name=shop_customer.last_name,
        ship_address_line_1="123 Shop St",
        ship_city="Luxembourg",
        ship_postal_code="L-1234",
        ship_country_iso="LU",
        bill_first_name=shop_customer.first_name,
        bill_last_name=shop_customer.last_name,
        bill_address_line_1="123 Shop St",
        bill_city="Luxembourg",
        bill_postal_code="L-1234",
        bill_country_iso="LU",
        # VAT fields
        vat_regime="domestic",
        vat_rate=Decimal("17.00"),
        vat_rate_label="Luxembourg VAT 17.00%",
        vat_destination_country=None,
    )
    db.add(order)
    db.commit()
    db.refresh(order)
    return order


@pytest.fixture
def shop_order_processing(db, test_vendor, shop_customer):
    """Create a test order with processing status (eligible for invoice)."""
    order = Order(
        vendor_id=test_vendor.id,
        customer_id=shop_customer.id,
        order_number="SHOP-ORD-002",
        status="processing",
        channel="direct",
        order_date=datetime.now(UTC),
        subtotal_cents=20000,
        tax_amount_cents=3400,
        shipping_amount_cents=500,
        total_amount_cents=23900,
        currency="EUR",
        customer_email=shop_customer.email,
        customer_first_name=shop_customer.first_name,
        customer_last_name=shop_customer.last_name,
        ship_first_name=shop_customer.first_name,
        ship_last_name=shop_customer.last_name,
        ship_address_line_1="456 Shop Ave",
        ship_city="Luxembourg",
        ship_postal_code="L-5678",
        ship_country_iso="LU",
        bill_first_name=shop_customer.first_name,
        bill_last_name=shop_customer.last_name,
        bill_address_line_1="456 Shop Ave",
        bill_city="Luxembourg",
        bill_postal_code="L-5678",
        bill_country_iso="LU",
        # VAT fields
        vat_regime="domestic",
        vat_rate=Decimal("17.00"),
        vat_rate_label="Luxembourg VAT 17.00%",
    )
    db.add(order)
    db.flush()

    # Add order item
    item = OrderItem(
        order_id=order.id,
        product_id=1,
        product_sku="TEST-SKU-001",
        product_name="Test Product",
        quantity=2,
        unit_price_cents=10000,
        total_price_cents=20000,
    )
    db.add(item)
    db.commit()
    db.refresh(order)
    return order


@pytest.fixture
def shop_invoice_settings(db, test_vendor):
    """Create invoice settings for the vendor."""
    settings = VendorInvoiceSettings(
        vendor_id=test_vendor.id,
        company_name="Shop Test Company S.A.",
        company_address="123 Business St",
        company_city="Luxembourg",
        company_postal_code="L-1234",
        company_country="LU",
        vat_number="LU12345678",
        invoice_prefix="INV",
        invoice_next_number=1,
        default_vat_rate=Decimal("17.00"),
    )
    db.add(settings)
    db.commit()
    db.refresh(settings)
    return settings


@pytest.fixture
def shop_order_with_invoice(db, test_vendor, shop_customer, shop_invoice_settings):
    """Create an order with an existing invoice."""
    order = Order(
        vendor_id=test_vendor.id,
        customer_id=shop_customer.id,
        order_number="SHOP-ORD-003",
        status="shipped",
        channel="direct",
        order_date=datetime.now(UTC),
        subtotal_cents=15000,
        tax_amount_cents=2550,
        shipping_amount_cents=500,
        total_amount_cents=18050,
        currency="EUR",
        customer_email=shop_customer.email,
        customer_first_name=shop_customer.first_name,
        customer_last_name=shop_customer.last_name,
        ship_first_name=shop_customer.first_name,
        ship_last_name=shop_customer.last_name,
        ship_address_line_1="789 Shop Blvd",
        ship_city="Luxembourg",
        ship_postal_code="L-9999",
        ship_country_iso="LU",
        bill_first_name=shop_customer.first_name,
        bill_last_name=shop_customer.last_name,
        bill_address_line_1="789 Shop Blvd",
        bill_city="Luxembourg",
        bill_postal_code="L-9999",
        bill_country_iso="LU",
        vat_regime="domestic",
        vat_rate=Decimal("17.00"),
    )
    db.add(order)
    db.flush()

    # Create invoice for this order
    invoice = Invoice(
        vendor_id=test_vendor.id,
        order_id=order.id,
        invoice_number="INV00001",
        invoice_date=datetime.now(UTC),
        status=InvoiceStatus.ISSUED.value,
        seller_details={"company_name": "Shop Test Company S.A."},
        buyer_details={"name": f"{shop_customer.first_name} {shop_customer.last_name}"},
        line_items=[],
        vat_rate=Decimal("17.00"),
        subtotal_cents=15000,
        vat_amount_cents=2550,
        total_cents=18050,
    )
    db.add(invoice)
    db.commit()
    db.refresh(order)
    db.refresh(invoice)
    return order, invoice


@pytest.fixture
def other_customer(db, test_vendor):
    """Create another customer for testing access controls."""
    from middleware.auth import AuthManager
    auth_manager = AuthManager()
    customer = Customer(
        vendor_id=test_vendor.id,
        email="othercustomer@example.com",
        hashed_password=auth_manager.hash_password("otherpass123"),
        first_name="Other",
        last_name="Customer",
        customer_number="OTHER001",
        is_active=True,
    )
    db.add(customer)
    db.commit()
    db.refresh(customer)
    return customer


@pytest.fixture
def other_customer_order(db, test_vendor, other_customer):
    """Create an order for another customer."""
    order = Order(
        vendor_id=test_vendor.id,
        customer_id=other_customer.id,
        order_number="OTHER-ORD-001",
        status="processing",
        channel="direct",
        order_date=datetime.now(UTC),
        subtotal_cents=5000,
        tax_amount_cents=850,
        total_amount_cents=5850,
        currency="EUR",
        customer_email=other_customer.email,
        customer_first_name=other_customer.first_name,
        customer_last_name=other_customer.last_name,
        ship_first_name=other_customer.first_name,
        ship_last_name=other_customer.last_name,
        ship_address_line_1="Other St",
        ship_city="Other City",
        ship_postal_code="00000",
        ship_country_iso="LU",
        bill_first_name=other_customer.first_name,
        bill_last_name=other_customer.last_name,
        bill_address_line_1="Other St",
        bill_city="Other City",
        bill_postal_code="00000",
        bill_country_iso="LU",
    )
    db.add(order)
    db.commit()
    db.refresh(order)
    return order


# Note: Shop API endpoints require vendor context from VendorContextMiddleware.
# In integration tests, we mock the middleware to inject the vendor.


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.shop
class TestShopOrdersListAPI:
    """Test shop orders list endpoint at /api/v1/storefront/orders."""

    def test_list_orders_requires_authentication(self, client, test_vendor):
        """Test that listing orders requires authentication."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            response = client.get("/api/v1/storefront/orders")
            # Without token, should get 401 or 403
            assert response.status_code in [401, 403]

    def test_list_orders_success(
        self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
    ):
        """Test listing customer orders successfully."""
        # Mock vendor context and customer auth
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            # Mock the dependency to return our customer
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    "/api/v1/storefront/orders",
                    headers=shop_customer_headers,
                )

                assert response.status_code == 200
                data = response.json()
                assert "orders" in data
                assert "total" in data

    def test_list_orders_empty(
        self, client, shop_customer_headers, test_vendor, shop_customer
    ):
        """Test listing orders when customer has none."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    "/api/v1/storefront/orders",
                    headers=shop_customer_headers,
                )

                assert response.status_code == 200
                data = response.json()
                assert data["total"] == 0


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.shop
class TestShopOrderDetailAPI:
    """Test shop order detail endpoint at /api/v1/storefront/orders/{order_id}."""

    def test_get_order_detail_success(
        self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
    ):
        """Test getting order details successfully."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    f"/api/v1/storefront/orders/{shop_order.id}",
                    headers=shop_customer_headers,
                )

                assert response.status_code == 200
                data = response.json()
                assert data["order_number"] == "SHOP-ORD-001"
                assert data["status"] == "pending"
                # Check VAT fields are present
                assert "vat_regime" in data
                assert "vat_rate" in data

    def test_get_order_detail_not_found(
        self, client, shop_customer_headers, test_vendor, shop_customer
    ):
        """Test getting non-existent order returns 404."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    "/api/v1/storefront/orders/99999",
                    headers=shop_customer_headers,
                )

                assert response.status_code == 404

    def test_get_order_detail_other_customer(
        self,
        client,
        shop_customer_headers,
        other_customer_order,
        test_vendor,
        shop_customer,
    ):
        """Test cannot access another customer's order."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    f"/api/v1/storefront/orders/{other_customer_order.id}",
                    headers=shop_customer_headers,
                )

                # Should return 404 (not 403) to prevent enumeration
                assert response.status_code == 404


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.shop
class TestShopOrderInvoiceDownloadAPI:
    """Test shop order invoice download at /api/v1/storefront/orders/{order_id}/invoice."""

    def test_download_invoice_pending_order_rejected(
        self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
    ):
        """Test cannot download invoice for pending orders."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    f"/api/v1/storefront/orders/{shop_order.id}/invoice",
                    headers=shop_customer_headers,
                )

                # Pending orders should not allow invoice download
                assert response.status_code == 422

    @pytest.mark.skip(reason="Requires PDF generation infrastructure")
    def test_download_invoice_processing_order_creates_invoice(
        self,
        client,
        shop_customer_headers,
        shop_order_processing,
        shop_invoice_settings,
        test_vendor,
        shop_customer,
    ):
        """Test downloading invoice for processing order creates it if needed."""
        # This test requires actual PDF generation which may not be available
        # in all environments. The logic is tested via:
        # 1. test_download_invoice_pending_order_rejected - validates status check
        # 2. Direct service tests for invoice creation
        pass

    @pytest.mark.skip(reason="Requires PDF generation infrastructure")
    def test_download_invoice_existing_invoice(
        self,
        client,
        shop_customer_headers,
        shop_order_with_invoice,
        test_vendor,
        shop_customer,
    ):
        """Test downloading invoice when one already exists."""
        # This test requires PDF file to exist on disk
        # The service layer handles invoice retrieval properly
        pass

    def test_download_invoice_other_customer(
        self,
        client,
        shop_customer_headers,
        other_customer_order,
        test_vendor,
        shop_customer,
    ):
        """Test cannot download invoice for another customer's order."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    f"/api/v1/storefront/orders/{other_customer_order.id}/invoice",
                    headers=shop_customer_headers,
                )

                # Should return 404 to prevent enumeration
                assert response.status_code == 404

    def test_download_invoice_not_found(
        self, client, shop_customer_headers, test_vendor, shop_customer
    ):
        """Test downloading invoice for non-existent order."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    "/api/v1/storefront/orders/99999/invoice",
                    headers=shop_customer_headers,
                )

                assert response.status_code == 404


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.shop
class TestShopOrderVATFields:
    """Test VAT fields in order responses."""

    def test_order_includes_vat_fields(
        self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
    ):
        """Test order response includes VAT fields."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    f"/api/v1/storefront/orders/{shop_order.id}",
                    headers=shop_customer_headers,
                )

                assert response.status_code == 200
                data = response.json()

                # Verify VAT fields
                assert data.get("vat_regime") == "domestic"
                assert data.get("vat_rate") == 17.0
                assert "Luxembourg VAT" in (data.get("vat_rate_label") or "")

    def test_order_list_includes_vat_fields(
        self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
    ):
        """Test order list includes VAT fields."""
        with patch("app.modules.orders.routes.api.storefront.getattr") as mock_getattr:
            mock_getattr.return_value = test_vendor
            with patch("app.api.deps._validate_customer_token") as mock_validate:
                mock_validate.return_value = shop_customer
                response = client.get(
                    "/api/v1/storefront/orders",
                    headers=shop_customer_headers,
                )

                assert response.status_code == 200
                data = response.json()

                if data["orders"]:
                    order = data["orders"][0]
                    assert "vat_regime" in order
                    assert "vat_rate" in order
