from rights_filter.analysis.preprocessing import ImagePayload, build_external_derivative
from rights_filter.domain.records import EvidenceSource
from rights_filter.integrations.cloud_vision_web_detection import (
    CloudVisionWebDetectionAdapter,
    FakeWebDetectionClient,
    GoogleVisionRestClient,
)
from rights_filter.integrations.external_policy import ExternalApiPolicy


def test_disabled_or_unapproved_policy_skips_outbound_call():
    client = FakeWebDetectionClient()
    adapter = CloudVisionWebDetectionAdapter(client)
    policy = ExternalApiPolicy(disabled=True, compliance_approved=True)
    derivative = build_external_derivative(ImagePayload(b"image", 100, 100, {}))

    evidence = adapter.detect("submission-1", derivative, policy)

    assert evidence[0].source == EvidenceSource.EXTERNAL_SKIPPED
    assert client.calls == []


def test_approved_policy_sends_only_derivative_and_maps_web_evidence():
    client = FakeWebDetectionClient(
        response={
            "web_entities": [{"description": "아이유", "score": 0.92}],
            "full_matching_images": [{"url": "https://example.com/image.jpg"}],
            "pages_with_matching_images": [{"url": "https://example.com/page"}],
            "best_guess_labels": [{"label": "IU photo"}],
        }
    )
    adapter = CloudVisionWebDetectionAdapter(client)
    policy = ExternalApiPolicy(
        compliance_approved=True,
        metadata_logging_accepted=True,
        allow_online_sync=True,
        daily_limit=10,
    )
    original = ImagePayload(b"image", 3000, 2000, {"EXIF": "secret"})
    derivative = build_external_derivative(original, max_side=1600)

    evidence = adapter.detect("submission-2", derivative, policy)

    assert client.calls == [derivative]
    assert all(item.source == EvidenceSource.WEB_DETECTION for item in evidence)
    assert any(item.data.get("entity") == "아이유" for item in evidence)
    assert any(item.data.get("url") == "https://example.com/image.jpg" for item in evidence)


def test_google_image_search_results_are_mapped_as_operator_evidence():
    client = FakeWebDetectionClient(
        response={
            "partial_matching_images": [{"url": "https://example.com/partial.jpg", "score": 0.77}],
            "visually_similar_images": [{"url": "https://example.com/similar.jpg", "score": 0.64}],
            "pages_with_matching_images": [
                {
                    "url": "https://example.com/article",
                    "score": 0.82,
                    "page_title": "Celebrity photo source",
                }
            ],
        }
    )
    adapter = CloudVisionWebDetectionAdapter(client)
    policy = ExternalApiPolicy(
        compliance_approved=True,
        metadata_logging_accepted=True,
        allow_online_sync=True,
    )

    evidence = adapter.detect(
        "submission-search",
        build_external_derivative(ImagePayload(b"image", 100, 100, {})),
        policy,
    )

    matches = {item.data.get("match"): item for item in evidence}
    assert matches["partial"].data["image_url"] == "https://example.com/partial.jpg"
    assert matches["visual"].reason == "Google visually similar image found"
    assert matches["page"].data["page_title"] == "Celebrity photo source"
    assert all(item.data["provider"] == "google" for item in evidence)


def test_image_match_evidence_preserves_url_variants_as_compare_candidates():
    client = FakeWebDetectionClient(
        response={
            "full_matching_images": [
                {
                    "image_url": "https://cdn.example.com/full.webp",
                    "thumbnail_url": "https://cdn.example.com/full-thumb.webp",
                    "score": 0.88,
                }
            ],
            "partial_matching_images": [
                {"contentUrl": "https://cdn.example.com/partial.png"}
            ],
            "visually_similar_images": [
                {"src": "https://cdn.example.com/visual.jpg"}
            ],
        }
    )
    adapter = CloudVisionWebDetectionAdapter(client)
    policy = ExternalApiPolicy(
        compliance_approved=True,
        metadata_logging_accepted=True,
        allow_online_sync=True,
    )

    evidence = adapter.detect(
        "submission-search",
        build_external_derivative(ImagePayload(b"image", 100, 100, {})),
        policy,
    )

    matches = {item.data.get("match"): item for item in evidence}
    assert matches["full"].data["image_url"] == "https://cdn.example.com/full.webp"
    assert matches["full"].data["thumbnail_url"] == "https://cdn.example.com/full-thumb.webp"
    assert matches["partial"].data["image_url"] == "https://cdn.example.com/partial.png"
    assert matches["visual"].data["image_url"] == "https://cdn.example.com/visual.jpg"


def test_google_rest_client_preserves_image_candidate_url_variants_from_raw_matches():
    class FakeTransport:
        def request_json(self, method, url, headers=None, payload=None, timeout=15):
            return {
                "responses": [
                    {
                        "webDetection": {
                            "fullMatchingImages": [
                                {
                                    "imageUrl": "https://cdn.example.com/full.webp",
                                    "thumbnailUrl": "https://cdn.example.com/full-thumb.webp",
                                }
                            ],
                            "partialMatchingImages": [
                                {"contentUrl": "https://cdn.example.com/partial.png"}
                            ],
                            "visuallySimilarImages": [
                                {"src": "https://cdn.example.com/visual.jpg"}
                            ],
                        }
                    }
                ]
            }

    client = GoogleVisionRestClient("google-key", transport=FakeTransport())

    result = client.detect_web(ImagePayload(b"image", 100, 100, {}))

    assert result["full_matching_images"][0]["url"] == "https://cdn.example.com/full.webp"
    assert result["full_matching_images"][0]["thumbnail_url"] == "https://cdn.example.com/full-thumb.webp"
    assert result["partial_matching_images"][0]["url"] == "https://cdn.example.com/partial.png"
    assert result["visually_similar_images"][0]["url"] == "https://cdn.example.com/visual.jpg"


def test_page_evidence_preserves_nested_image_candidates():
    client = FakeWebDetectionClient(
        response={
            "pages_with_matching_images": [
                {
                    "url": "https://example.com/article",
                    "score": 0.82,
                    "page_title": "Celebrity photo source",
                    "page_image_urls": ["https://cdn.example.com/already-known.jpg"],
                    "full_matching_images": [{"url": "https://cdn.example.com/full.jpg"}],
                    "partial_matching_images": [{"url": "https://cdn.example.com/partial.png"}],
                    "thumbnail_url": "https://cdn.example.com/thumb.webp",
                }
            ],
        }
    )
    adapter = CloudVisionWebDetectionAdapter(client)
    policy = ExternalApiPolicy(
        compliance_approved=True,
        metadata_logging_accepted=True,
        allow_online_sync=True,
    )

    evidence = adapter.detect(
        "submission-search",
        build_external_derivative(ImagePayload(b"image", 100, 100, {})),
        policy,
    )

    page = evidence[0]
    assert page.data["page_image_urls"] == [
        "https://cdn.example.com/already-known.jpg",
        "https://cdn.example.com/full.jpg",
        "https://cdn.example.com/partial.png",
        "https://cdn.example.com/thumb.webp",
    ]


def test_google_rest_client_preserves_page_image_candidates_from_raw_web_pages():
    class FakeTransport:
        def request_json(self, method, url, headers=None, payload=None, timeout=15):
            return {
                "responses": [
                    {
                        "webDetection": {
                            "pagesWithMatchingImages": [
                                {
                                    "url": "https://example.com/article",
                                    "pageTitle": "Celebrity photo source",
                                    "fullMatchingImages": [
                                        {"url": "https://cdn.example.com/full.jpg"}
                                    ],
                                    "partialMatchingImages": [
                                        {"url": "https://cdn.example.com/partial.png"}
                                    ],
                                    "visuallySimilarImages": [
                                        {"url": "https://cdn.example.com/visual.webp"}
                                    ],
                                    "thumbnailUrl": "https://cdn.example.com/thumb.webp",
                                }
                            ]
                        }
                    }
                ]
            }

    client = GoogleVisionRestClient("google-key", transport=FakeTransport())

    result = client.detect_web(ImagePayload(b"image", 100, 100, {}))

    assert result["pages_with_matching_images"][0]["page_image_urls"] == [
        "https://cdn.example.com/full.jpg",
        "https://cdn.example.com/partial.png",
        "https://cdn.example.com/visual.webp",
        "https://cdn.example.com/thumb.webp",
    ]


def test_best_guess_labels_are_mapped_as_weak_non_contributing_hints():
    client = FakeWebDetectionClient(
        response={"best_guess_labels": [{"label": "gentleman"}]}
    )
    adapter = CloudVisionWebDetectionAdapter(client)
    policy = ExternalApiPolicy(
        compliance_approved=True,
        metadata_logging_accepted=True,
        allow_online_sync=True,
    )

    evidence = adapter.detect(
        "submission-weak",
        build_external_derivative(ImagePayload(b"image", 100, 100, {})),
        policy,
    )

    assert evidence[0].reason == "Google weak label gentleman"
    assert evidence[0].confidence == 0.0
    assert evidence[0].data["weak_hint"] is True


def test_quota_exhaustion_records_failure_reason():
    client = FakeWebDetectionClient()
    adapter = CloudVisionWebDetectionAdapter(client)
    policy = ExternalApiPolicy(
        compliance_approved=True,
        metadata_logging_accepted=True,
        allow_online_sync=True,
        daily_limit=0,
    )
    derivative = build_external_derivative(ImagePayload(b"image", 100, 100, {}))

    evidence = adapter.detect("submission-3", derivative, policy)

    assert evidence[0].source == EvidenceSource.EXTERNAL_SKIPPED
    assert "usage limit" in evidence[0].reason
    assert client.calls == []
