"""Tests for v24.0 compliance features.

Tests cover:
- Flagged audience checks
- Flagged conversion checks
- Lookalike spec validation
- v24.0 schema validation
"""
from __future__ import annotations

from unittest.mock import AsyncMock

import pytest

from src.schemas import (
    CheckFlaggedAudienceRequest,
    CheckFlaggedConversionRequest,
    LookalikeSpecV24,
    CampaignCreateV24,
    AdSetCreateV24,
    CatalogBatchV24,
)
from src.tools.audiences import (
    check_flagged_audience_handler,
    validate_lookalike_spec_handler,
)
from src.tools.conversion import check_flagged_conversion_handler


# =============================================================================
# Fixtures
# =============================================================================

@pytest.fixture
def mock_client():
    """Create a mock MetaClient."""
    client = AsyncMock()
    client.get = AsyncMock()
    return client


# =============================================================================
# Schema Validation Tests
# =============================================================================

class TestV24Schemas:
    """Tests for v24.0 compliance schemas."""

    def test_check_flagged_audience_request_valid(self):
        """Test valid CheckFlaggedAudienceRequest."""
        request = CheckFlaggedAudienceRequest(audience_id="123456789")
        assert request.audience_id == "123456789"
        assert "is_flagged" in request.fields

    def test_check_flagged_conversion_request_valid(self):
        """Test valid CheckFlaggedConversionRequest."""
        request = CheckFlaggedConversionRequest(custom_conversion_id="987654321")
        assert request.custom_conversion_id == "987654321"
        assert "is_flagged" in request.fields

    def test_lookalike_spec_v24_valid(self):
        """Test valid LookalikeSpecV24."""
        spec = LookalikeSpecV24(
            origin_audience_id="123",
            ratio=0.05,
            country="US",
            type="similarity"
        )
        assert spec.ratio == 0.05
        assert spec.type == "similarity"

    def test_lookalike_spec_v24_ratio_bounds(self):
        """Test LookalikeSpecV24 ratio bounds (0.01-0.20)."""
        # Valid lower bound
        spec = LookalikeSpecV24(
            origin_audience_id="123",
            ratio=0.01,
            country="US"
        )
        assert spec.ratio == 0.01
        
        # Valid upper bound
        spec = LookalikeSpecV24(
            origin_audience_id="123",
            ratio=0.20,
            country="US"
        )
        assert spec.ratio == 0.20

    def test_lookalike_spec_v24_ratio_too_low(self):
        """Test LookalikeSpecV24 rejects ratio below 0.01."""
        with pytest.raises(ValueError):
            LookalikeSpecV24(
                origin_audience_id="123",
                ratio=0.001,
                country="US"
            )

    def test_lookalike_spec_v24_ratio_too_high(self):
        """Test LookalikeSpecV24 rejects ratio above 0.20."""
        with pytest.raises(ValueError):
            LookalikeSpecV24(
                origin_audience_id="123",
                ratio=0.25,
                country="US"
            )

    def test_campaign_create_v24_budget_sharing(self):
        """Test CampaignCreateV24 includes is_adset_budget_sharing_enabled."""
        campaign = CampaignCreateV24(
            ad_account_id="act_123",
            name="Test Campaign",
            objective="OUTCOME_TRAFFIC",
            is_adset_budget_sharing_enabled=True
        )
        assert campaign.is_adset_budget_sharing_enabled is True

    def test_adset_create_v24_placement_soft_opt_out(self):
        """Test AdSetCreateV24 includes placement_soft_opt_out."""
        adset = AdSetCreateV24(
            ad_account_id="act_123",
            campaign_id="campaign_123",
            name="Test AdSet",
            optimization_goal="REACH",
            targeting={"geo_locations": {"countries": ["US"]}},
            placement_soft_opt_out=["instagram_stream"]
        )
        assert adset.placement_soft_opt_out == ["instagram_stream"]

    def test_catalog_batch_v24_allow_upsert(self):
        """Test CatalogBatchV24 includes allow_upsert."""
        batch = CatalogBatchV24(
            product_catalog_id="catalog_123",
            requests=[{"method": "CREATE", "data": {"id": "1"}}],
            allow_upsert=True
        )
        assert batch.allow_upsert is True


# =============================================================================
# Flagged Audience Check Tests
# =============================================================================

class TestCheckFlaggedAudience:
    """Tests for check_flagged_audience tool."""

    @pytest.mark.asyncio
    async def test_not_flagged_audience(self, mock_client):
        """Test checking a non-flagged audience."""
        mock_client.get.return_value = {
            "id": "123",
            "name": "Test Audience",
            "is_flagged": False,
            "operation_status": {"status": 200}
        }
        
        result = await check_flagged_audience_handler(
            mock_client,
            {"audience_id": "123"}
        )
        
        assert result.ok is True
        assert result.data["is_flagged"] is False
        assert result.data["can_update"] is True
        assert result.data["resolution_steps"] is None

    @pytest.mark.asyncio
    async def test_flagged_audience(self, mock_client):
        """Test checking a flagged audience."""
        mock_client.get.return_value = {
            "id": "123",
            "name": "Flagged Audience",
            "is_flagged": True,
            "flagged_reason": "Policy violation",
            "operation_status": {"status": 400}
        }
        
        result = await check_flagged_audience_handler(
            mock_client,
            {"audience_id": "123"}
        )
        
        assert result.ok is True
        assert result.data["is_flagged"] is True
        assert result.data["can_update"] is False
        assert result.data["resolution_steps"] is not None
        assert len(result.data["resolution_steps"]) > 0

    @pytest.mark.asyncio
    async def test_flagged_audience_includes_v24_note(self, mock_client):
        """Test that response includes v24.0 note."""
        mock_client.get.return_value = {
            "id": "123",
            "name": "Test Audience",
            "is_flagged": False,
        }
        
        result = await check_flagged_audience_handler(
            mock_client,
            {"audience_id": "123"}
        )
        
        assert "v24_note" in result.data
        assert "v24.0" in result.data["v24_note"]


# =============================================================================
# Flagged Conversion Check Tests
# =============================================================================

class TestCheckFlaggedConversion:
    """Tests for check_flagged_conversion tool."""

    @pytest.mark.asyncio
    async def test_not_flagged_conversion(self, mock_client):
        """Test checking a non-flagged conversion."""
        mock_client.get.return_value = {
            "id": "456",
            "name": "Test Conversion",
            "is_flagged": False,
        }
        
        result = await check_flagged_conversion_handler(
            mock_client,
            {"custom_conversion_id": "456"}
        )
        
        assert result.ok is True
        assert result.data["is_flagged"] is False
        assert result.data["can_update"] is True

    @pytest.mark.asyncio
    async def test_flagged_conversion(self, mock_client):
        """Test checking a flagged conversion."""
        mock_client.get.return_value = {
            "id": "456",
            "name": "Flagged Conversion",
            "is_flagged": True,
            "flagged_reason": "Invalid event",
        }
        
        result = await check_flagged_conversion_handler(
            mock_client,
            {"custom_conversion_id": "456"}
        )
        
        assert result.ok is True
        assert result.data["is_flagged"] is True
        assert result.data["can_update"] is False
        assert "resolution_url" in result.data
        assert "resolution_steps" in result.data

    @pytest.mark.asyncio
    async def test_flagged_conversion_includes_help_url(self, mock_client):
        """Test that flagged conversion includes resolution URL."""
        mock_client.get.return_value = {
            "id": "456",
            "name": "Flagged Conversion",
            "is_flagged": True,
        }
        
        result = await check_flagged_conversion_handler(
            mock_client,
            {"custom_conversion_id": "456"}
        )
        
        assert "facebook.com/business/help" in result.data["resolution_url"]


# =============================================================================
# Lookalike Spec Validation Tests
# =============================================================================

class TestValidateLookalikeSpec:
    """Tests for validate_lookalike_spec tool."""

    @pytest.mark.asyncio
    async def test_valid_spec(self, mock_client):
        """Test validating a valid lookalike spec."""
        mock_client.get.return_value = {
            "id": "origin_123",
            "name": "Origin Audience",
            "is_flagged": False,
            "approximate_count": 5000,
        }
        
        result = await validate_lookalike_spec_handler(
            mock_client,
            {
                "ad_account_id": "act_123",
                "lookalike_spec": {
                    "origin_audience_id": "origin_123",
                    "ratio": 0.05,
                    "country": "US",
                    "type": "similarity"
                }
            }
        )
        
        assert result.ok is True
        assert result.data["is_valid"] is True
        assert len(result.data["errors"]) == 0

    @pytest.mark.asyncio
    async def test_flagged_origin_audience(self, mock_client):
        """Test validation fails for flagged origin audience."""
        mock_client.get.return_value = {
            "id": "origin_123",
            "name": "Flagged Origin",
            "is_flagged": True,
            "approximate_count": 5000,
        }
        
        result = await validate_lookalike_spec_handler(
            mock_client,
            {
                "ad_account_id": "act_123",
                "lookalike_spec": {
                    "origin_audience_id": "origin_123",
                    "ratio": 0.05,
                    "country": "US",
                    "type": "similarity"
                }
            }
        )
        
        assert result.ok is True
        assert result.data["is_valid"] is False
        assert result.data["origin_audience_valid"] is False
        assert any("flagged" in e.lower() for e in result.data["errors"])

    @pytest.mark.asyncio
    async def test_low_audience_count_warning(self, mock_client):
        """Test warning for low origin audience count."""
        mock_client.get.return_value = {
            "id": "origin_123",
            "name": "Small Audience",
            "is_flagged": False,
            "approximate_count": 50,  # Too small
        }
        
        result = await validate_lookalike_spec_handler(
            mock_client,
            {
                "ad_account_id": "act_123",
                "lookalike_spec": {
                    "origin_audience_id": "origin_123",
                    "ratio": 0.05,
                    "country": "US",
                    "type": "similarity"
                }
            }
        )
        
        assert result.ok is True
        assert len(result.data["warnings"]) > 0
        assert any("low count" in w.lower() for w in result.data["warnings"])

    @pytest.mark.asyncio
    async def test_includes_v24_note(self, mock_client):
        """Test that response includes v24.0 note."""
        mock_client.get.return_value = {
            "id": "origin_123",
            "name": "Origin Audience",
            "is_flagged": False,
            "approximate_count": 5000,
        }
        
        result = await validate_lookalike_spec_handler(
            mock_client,
            {
                "ad_account_id": "act_123",
                "lookalike_spec": {
                    "origin_audience_id": "origin_123",
                    "ratio": 0.05,
                    "country": "US",
                    "type": "similarity"
                }
            }
        )
        
        assert "v24_note" in result.data
        assert "v24.0" in result.data["v24_note"]
