# tests/integration/api/v1/test_vendor_endpoints.py
import pytest


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendors
class TestVendorsAPI:
    def test_create_vendor_success(self, client, auth_headers):
        """Test creating a new vendor successfully"""
        vendor_data = {
            "vendor_code": "NEWVENDOR001",
            "name": "New Vendor",
            "description": "A new test vendor ",
        }

        response = client.post("/api/v1/vendor", headers=auth_headers, json=vendor_data)

        assert response.status_code == 200
        data = response.json()
        assert data["vendor_code"] == "NEWVENDOR001"
        assert data["name"] == "New Vendor"
        assert data["is_active"] is True

    def test_create_vendor_duplicate_code_returns_conflict(
        self, client, auth_headers, test_vendor
    ):
        """Test creating vendor with duplicate code returns VendorAlreadyExistsException"""
        vendor_data = {
            "vendor_code": test_vendor.vendor_code,
            "name": "Different Name",
            "description": "Different description",
        }

        response = client.post("/api/v1/vendor", headers=auth_headers, json=vendor_data)

        assert response.status_code == 409
        data = response.json()
        assert data["error_code"] == "VENDOR_ALREADY_EXISTS"
        assert data["status_code"] == 409
        assert test_vendor.vendor_code in data["message"]
        assert data["details"]["vendor_code"] == test_vendor.vendor_code

    def test_create_vendor_missing_vendor_code_validation_error(
        self, client, auth_headers
    ):
        """Test creating vendor without vendor_code returns ValidationException"""
        vendor_data = {
            "name": "Vendor without Code",
            "description": "Missing vendor code",
        }

        response = client.post("/api/v1/vendor", headers=auth_headers, json=vendor_data)

        assert response.status_code == 422
        data = response.json()
        assert data["error_code"] == "VALIDATION_ERROR"
        assert data["status_code"] == 422
        assert "Request validation failed" in data["message"]
        assert "validation_errors" in data["details"]

    def test_create_vendor_empty_vendor_name_validation_error(
        self, client, auth_headers
    ):
        """Test creating vendor with empty name returns VendorValidationException"""
        vendor_data = {
            "vendor_code": "EMPTYNAME",
            "name": "",  # Empty vendor name
            "description": "Vendor with empty name",
        }

        response = client.post("/api/v1/vendor", headers=auth_headers, json=vendor_data)

        assert response.status_code == 422
        data = response.json()
        assert data["error_code"] == "INVALID_VENDOR_DATA"
        assert data["status_code"] == 422
        assert "Vendor name is required" in data["message"]
        assert data["details"]["field"] == "name"

    def test_create_vendor_max_vendors_reached_business_logic_error(
        self, client, auth_headers, db, test_user
    ):
        """Test creating vendor when max vendors reached returns MaxVendorsReachedException"""
        # This test would require creating the maximum allowed vendors first
        # The exact implementation depends on your business rules

        # For now, we'll test the structure of what the error should look like
        # In a real scenario, you'd create max_vendors number of vendors first

        # Assuming max vendors is enforced at service level
        # This test validates the expected response structure
        # Implementation depends on your max_vendors business logic

    def test_get_vendors_success(self, client, auth_headers, test_vendor):
        """Test getting vendors list successfully"""
        response = client.get("/api/v1/vendor", headers=auth_headers)

        assert response.status_code == 200
        data = response.json()
        assert data["total"] >= 1
        assert len(data["vendors"]) >= 1

        # Find our test vendor
        test_vendor_found = any(
            s["vendor_code"] == test_vendor.vendor_code for s in data["vendors"]
        )
        assert test_vendor_found

    def test_get_vendors_with_filters(self, client, auth_headers, test_vendor):
        """Test getting vendors with filtering options"""
        # Test active_only filter
        response = client.get("/api/v1/vendor?active_only=true", headers=auth_headers)
        assert response.status_code == 200
        data = response.json()
        for vendor in data["vendors"]:
            assert vendor["is_active"] is True

        # Test verified_only filter
        response = client.get("/api/v1/vendor?verified_only=true", headers=auth_headers)
        assert response.status_code == 200
        # Response should only contain verified vendors

    def test_get_vendor_by_code_success(self, client, auth_headers, test_vendor):
        """Test getting specific vendor successfully"""
        response = client.get(
            f"/api/v1/vendor/{test_vendor.vendor_code}", headers=auth_headers
        )

        assert response.status_code == 200
        data = response.json()
        assert data["vendor_code"] == test_vendor.vendor_code
        assert data["name"] == test_vendor.name

    def test_get_vendor_by_code_not_found(self, client, auth_headers):
        """Test getting nonexistent vendor returns VendorNotFoundException"""
        response = client.get("/api/v1/vendor/NONEXISTENT", headers=auth_headers)

        assert response.status_code == 404
        data = response.json()
        assert data["error_code"] == "VENDOR_NOT_FOUND"
        assert data["status_code"] == 404
        assert "NONEXISTENT" in data["message"]
        assert data["details"]["resource_type"] == "Vendor"
        assert data["details"]["identifier"] == "NONEXISTENT"

    def test_get_vendor_unauthorized_access(
        self, client, auth_headers, test_vendor, other_user, db
    ):
        """Test accessing vendor owned by another user returns UnauthorizedVendorAccessException"""
        # Change vendor owner to other user AND make it unverified/inactive
        # so that non-owner users cannot access it
        test_vendor.owner_user_id = other_user.id
        test_vendor.is_verified = False  # Make it not publicly accessible
        db.commit()

        response = client.get(
            f"/api/v1/vendor/{test_vendor.vendor_code}", headers=auth_headers
        )

        assert response.status_code == 403
        data = response.json()
        assert data["error_code"] == "UNAUTHORIZED_VENDOR_ACCESS"
        assert data["status_code"] == 403
        assert test_vendor.vendor_code in data["message"]
        assert data["details"]["vendor_code"] == test_vendor.vendor_code

    def test_get_vendor_unauthorized_access_with_inactive_vendor(
        self, client, auth_headers, inactive_vendor
    ):
        """Test accessing inactive vendor owned by another user returns UnauthorizedVendorAccessException"""
        # inactive_vendor fixture already creates an unverified, inactive vendor owned by other_user
        response = client.get(
            f"/api/v1/vendor/{inactive_vendor.vendor_code}", headers=auth_headers
        )

        assert response.status_code == 403
        data = response.json()
        assert data["error_code"] == "UNAUTHORIZED_VENDOR_ACCESS"
        assert data["status_code"] == 403
        assert inactive_vendor.vendor_code in data["message"]
        assert data["details"]["vendor_code"] == inactive_vendor.vendor_code

    def test_get_vendor_public_access_allowed(
        self, client, auth_headers, verified_vendor
    ):
        """Test accessing verified vendor owned by another user is allowed (public access)"""
        # verified_vendor fixture creates a verified, active vendor owned by other_user
        # This should allow public access per your business logic
        response = client.get(
            f"/api/v1/vendor/{verified_vendor.vendor_code}", headers=auth_headers
        )

        assert response.status_code == 200
        data = response.json()
        assert data["vendor_code"] == verified_vendor.vendor_code
        assert data["name"] == verified_vendor.name

    def test_add_product_to_vendor_success(
        self, client, auth_headers, test_vendor, unique_product
    ):
        """Test adding product to vendor successfully"""
        product_data = {
            "marketplace_product_id": unique_product.marketplace_product_id,  # Use string marketplace_product_id, not database id
            "price": 29.99,
            "is_active": True,
            "is_featured": False,
        }

        response = client.post(
            f"/api/v1/vendor/{test_vendor.vendor_code}/products",
            headers=auth_headers,
            json=product_data,
        )

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

        # The response structure contains nested product data
        assert data["vendor_id"] == test_vendor.id
        assert data["price"] == 29.99
        assert data["is_active"] is True
        assert data["is_featured"] is False

        # MarketplaceProduct details are nested in the 'marketplace_product' field
        assert "marketplace_product" in data
        assert (
            data["marketplace_product"]["marketplace_product_id"]
            == unique_product.marketplace_product_id
        )
        assert data["marketplace_product"]["id"] == unique_product.id

    def test_add_product_to_vendor_already_exists_conflict(
        self, client, auth_headers, test_vendor, test_product
    ):
        """Test adding product that already exists in vendor returns ProductAlreadyExistsException"""
        # test_product fixture already creates a relationship, get the marketplace_product_id string
        existing_product = test_product.marketplace_product

        product_data = {
            "marketplace_product_id": existing_product.marketplace_product_id,  # Use string marketplace_product_id
            "price": 29.99,
        }

        response = client.post(
            f"/api/v1/vendor/{test_vendor.vendor_code}/products",
            headers=auth_headers,
            json=product_data,
        )

        assert response.status_code == 409
        data = response.json()
        assert data["error_code"] == "PRODUCT_ALREADY_EXISTS"
        assert data["status_code"] == 409
        assert test_vendor.vendor_code in data["message"]
        assert existing_product.marketplace_product_id in data["message"]

    def test_add_nonexistent_product_to_vendor_not_found(
        self, client, auth_headers, test_vendor
    ):
        """Test adding nonexistent product to vendor returns MarketplaceProductNotFoundException"""
        product_data = {
            "marketplace_product_id": "NONEXISTENT_PRODUCT",  # Use string marketplace_product_id that doesn't exist
            "price": 29.99,
        }

        response = client.post(
            f"/api/v1/vendor/{test_vendor.vendor_code}/products",
            headers=auth_headers,
            json=product_data,
        )

        assert response.status_code == 404
        data = response.json()
        assert data["error_code"] == "PRODUCT_NOT_FOUND"
        assert data["status_code"] == 404
        assert "NONEXISTENT_PRODUCT" in data["message"]

    def test_get_products_success(
        self, client, auth_headers, test_vendor, test_product
    ):
        """Test getting vendor products successfully"""
        response = client.get(
            f"/api/v1/vendor/{test_vendor.vendor_code}/products", headers=auth_headers
        )

        assert response.status_code == 200
        data = response.json()
        assert data["total"] >= 1
        assert len(data["products"]) >= 1
        assert "vendor" in data
        assert data["vendor"]["vendor_code"] == test_vendor.vendor_code

    def test_get_products_with_filters(self, client, auth_headers, test_vendor):
        """Test getting vendor products with filtering"""
        # Test active_only filter
        response = client.get(
            f"/api/v1/vendor/{test_vendor.vendor_code}/products?active_only=true",
            headers=auth_headers,
        )
        assert response.status_code == 200

        # Test featured_only filter
        response = client.get(
            f"/api/v1/vendor/{test_vendor.vendor_code}/products?featured_only=true",
            headers=auth_headers,
        )
        assert response.status_code == 200

    def test_get_products_from_nonexistent_vendor_not_found(self, client, auth_headers):
        """Test getting products from nonexistent vendor returns VendorNotFoundException"""
        response = client.get(
            "/api/v1/vendor/NONEXISTENT/products", headers=auth_headers
        )

        assert response.status_code == 404
        data = response.json()
        assert data["error_code"] == "VENDOR_NOT_FOUND"
        assert data["status_code"] == 404
        assert "NONEXISTENT" in data["message"]

    def test_vendor_not_active_business_logic_error(
        self, client, auth_headers, test_vendor, db
    ):
        """Test accessing inactive vendor returns VendorNotActiveException (if enforced)"""
        # Set vendor to inactive
        test_vendor.is_active = False
        db.commit()

        # Depending on your business logic, this might return an error
        response = client.get(
            f"/api/v1/vendor/{test_vendor.vendor_code}", headers=auth_headers
        )

        # If your service enforces active vendor requirement
        if response.status_code == 400:
            data = response.json()
            assert data["error_code"] == "VENDOR_NOT_ACTIVE"
            assert data["status_code"] == 400
            assert test_vendor.vendor_code in data["message"]

    def test_vendor_not_verified_business_logic_error(
        self, client, auth_headers, test_vendor, db
    ):
        """Test operations requiring verification returns VendorNotVerifiedException (if enforced)"""
        # Set vendor to unverified
        test_vendor.is_verified = False
        db.commit()

        # Test adding products (might require verification)
        product_data = {
            "marketplace_product_id": 1,
            "price": 29.99,
        }

        response = client.post(
            f"/api/v1/vendor/{test_vendor.vendor_code}/products",
            headers=auth_headers,
            json=product_data,
        )

        # If your service requires verification for adding products
        if response.status_code == 400:
            data = response.json()
            assert data["error_code"] == "VENDOR_NOT_VERIFIED"
            assert data["status_code"] == 400
            assert test_vendor.vendor_code in data["message"]

    def test_get_vendor_without_auth_returns_invalid_token(self, client):
        """Test that vendor endpoints require authentication returns InvalidTokenException"""
        response = client.get("/api/v1/vendor")

        assert response.status_code == 401
        data = response.json()
        assert data["error_code"] == "INVALID_TOKEN"
        assert data["status_code"] == 401

    def test_pagination_validation_errors(self, client, auth_headers):
        """Test pagination parameter validation"""
        # Test negative skip
        response = client.get("/api/v1/vendor?skip=-1", headers=auth_headers)
        assert response.status_code == 422
        data = response.json()
        assert data["error_code"] == "VALIDATION_ERROR"

        # Test zero limit
        response = client.get("/api/v1/vendor?limit=0", headers=auth_headers)
        assert response.status_code == 422
        data = response.json()
        assert data["error_code"] == "VALIDATION_ERROR"

        # Test excessive limit
        response = client.get("/api/v1/vendor?limit=10000", headers=auth_headers)
        assert response.status_code == 422
        data = response.json()
        assert data["error_code"] == "VALIDATION_ERROR"

    def test_exception_structure_consistency(self, client, auth_headers):
        """Test that all vendor exceptions follow the consistent WizamartException structure"""
        # Test with a known error case
        response = client.get("/api/v1/vendor/NONEXISTENT", headers=auth_headers)

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

        # Verify exception structure matches WizamartException.to_dict()
        required_fields = ["error_code", "message", "status_code"]
        for field in required_fields:
            assert field in data, f"Missing required field: {field}"

        assert isinstance(data["error_code"], str)
        assert isinstance(data["message"], str)
        assert isinstance(data["status_code"], int)

        # Details field should be present for domain-specific exceptions
        if "details" in data:
            assert isinstance(data["details"], dict)
