from unittest.mock import MagicMock, patch

import pytest

from db.image_urls import normalize_external_image_url, prepare_image_urls
from shopify.image_upload import fetch_and_stage_image, is_invalid_image_url_error, stage_image_bytes
from shopify.media_sync import _create_media


def test_normalize_external_image_url_strips_csv_quotes_and_protocol():
    assert normalize_external_image_url('"https://cdn.example/a.jpg"') == "https://cdn.example/a.jpg"
    assert normalize_external_image_url("//cdn.example/a.jpg") == "https://cdn.example/a.jpg"


def test_normalize_external_image_url_encodes_spaces_and_parentheses_in_path():
    raw = (
        "https://pub-3aedd5936a7e4bea95f3267214a86554.r2.dev/"
        "hs-images/TB-Standard Line-HD(CA)/CA-BBC42-CLOSED.png"
    )
    encoded = normalize_external_image_url(raw)
    assert encoded == (
        "https://pub-3aedd5936a7e4bea95f3267214a86554.r2.dev/"
        "hs-images/TB-Standard%20Line-HD%28CA%29/CA-BBC42-CLOSED.png"
    )
    assert " " not in encoded
    assert "(" not in encoded


def test_normalize_external_image_url_idempotent_for_encoded_paths():
    encoded = "https://cdn.example/hs-images/TB-Standard%20Line-HD%28CA%29/file.png"
    assert normalize_external_image_url(encoded) == encoded


def test_prepare_image_urls_dedupes():
    assert prepare_image_urls(
        ["https://cdn.example/a.jpg", "https://cdn.example/a.jpg", ""]
    ) == ["https://cdn.example/a.jpg"]


def test_is_invalid_image_url_error():
    assert is_invalid_image_url_error(RuntimeError("productCreateMedia userErrors: Image URL is invalid"))
    assert not is_invalid_image_url_error(RuntimeError("timeout"))


def test_stage_image_bytes_posts_to_shopify_target():
    client = MagicMock()
    client.graphql.return_value = {
        "stagedUploadsCreate": {
            "stagedTargets": [
                {
                    "url": "https://upload.shopify.com/target",
                    "resourceUrl": "https://shopify.staged/resource.jpg",
                    "parameters": [{"name": "key", "value": "abc"}],
                }
            ],
            "userErrors": [],
        }
    }
    with patch("shopify.image_upload.requests.post") as post:
        post.return_value = MagicMock(status_code=200, raise_for_status=MagicMock())
        resource = stage_image_bytes(
            client,
            b"binary",
            filename="render.jpg",
            mime_type="image/jpeg",
        )
    assert resource == "https://shopify.staged/resource.jpg"
    post.assert_called_once()


def test_create_media_falls_back_to_staged_upload_on_invalid_url():
    client = MagicMock()
    client.graphql.side_effect = [
        {"productCreateMedia": {"media": [], "mediaUserErrors": [{"message": "Image URL is invalid"}]}},
        {"productCreateMedia": {"media": [{"id": "1"}], "mediaUserErrors": []}},
    ]
    with patch("shopify.media_sync.fetch_and_stage_image", return_value="https://shopify.staged/x.jpg"):
        uploaded, errors = _create_media(
            client,
            "gid://shopify/Product/1",
            ["https://r2.example/render.jpg"],
            alt="SKU-1",
        )

    assert uploaded == 1
    assert errors == []
    assert client.graphql.call_count == 2


def test_fetch_and_stage_image_downloads_r2_bytes():
    client = MagicMock()
    client.graphql.return_value = {
        "stagedUploadsCreate": {
            "stagedTargets": [
                {
                    "url": "https://upload.shopify.com/target",
                    "resourceUrl": "https://shopify.staged/resource.jpg",
                    "parameters": [],
                }
            ],
            "userErrors": [],
        }
    }
    with patch("shopify.image_upload.requests.get") as get:
        get.return_value = MagicMock(
            status_code=200,
            content=b"img",
            headers={"Content-Type": "image/png"},
            raise_for_status=MagicMock(),
        )
        with patch("shopify.image_upload.requests.post") as post:
            post.return_value = MagicMock(status_code=200, raise_for_status=MagicMock())
            resource = fetch_and_stage_image(client, "https://r2.example/bucket/render.png")

    assert resource == "https://shopify.staged/resource.jpg"
    get.assert_called_once_with("https://r2.example/bucket/render.png", timeout=120, allow_redirects=True)
