"""
Tests for QR-Mark: AI-readable invisible watermarking.
"""
import json
import pytest
import numpy as np
from PIL import Image

from clawid.algorithms.qr_mark import embed_qr, detect_qr
from clawid import embed, detect


def _make_image(w=512, h=512, kind='gradient'):
    """Create a test image."""
    if kind == 'gradient':
        arr = np.zeros((h, w, 3), dtype=np.uint8)
        for i in range(h):
            arr[i, :] = [i * 255 // h, 100, 255 - i * 255 // h]
        return Image.fromarray(arr)
    elif kind == 'photo':
        # Realistic structured photo: smooth regions + moderate texture (not pure noise)
        # Real photos always have structure — pure noise is not a valid test case
        np.random.seed(42)
        arr = np.zeros((h, w, 3), dtype=np.uint8)
        # Sky-like top half, ground-like bottom half
        for i in range(h):
            base = [int(100 + 80 * i / h), int(130 - 40 * i / h), int(200 - 80 * i / h)]
            noise = np.random.randint(-20, 20, (w, 3))
            arr[i] = np.clip(np.array(base) + noise, 0, 255).astype(np.uint8)
        return Image.fromarray(arr)
    elif kind == 'solid':
        return Image.fromarray(np.full((h, w, 3), 128, dtype=np.uint8))


METADATA = {
    'uid': 'test_user',
    'platform': 'hawky.ai',
    'asset_id': 'test_asset_001',
    'clawid': 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
    'ts': 1700000000,
}


# ── embed / detect round-trip ─────────────────────────────────────────────────

class TestQRRoundTrip:
    def test_gradient_png(self, tmp_path):
        img = _make_image(kind='gradient')
        wm = embed_qr(img, METADATA)
        result = detect_qr(wm)
        assert result is not None
        assert result['uid'] == 'test_user'
        assert result['platform'] == 'hawky.ai'

    def test_photo_png(self, tmp_path):
        img = _make_image(kind='photo')
        wm = embed_qr(img, METADATA)
        result = detect_qr(wm)
        assert result is not None
        assert result['uid'] == 'test_user'

    def test_solid_color_png(self):
        img = _make_image(kind='solid')
        wm = embed_qr(img, METADATA)
        result = detect_qr(wm)
        assert result is not None

    def test_large_image(self):
        img = _make_image(w=1024, h=768, kind='gradient')
        wm = embed_qr(img, METADATA)
        result = detect_qr(wm)
        assert result is not None
        assert result['clawid'] == METADATA['clawid']

    def test_non_square_image(self):
        img = _make_image(w=800, h=600, kind='photo')
        wm = embed_qr(img, METADATA)
        result = detect_qr(wm)
        assert result is not None


# ── JPEG survival ────────────────────────────────────────────────────────────

class TestJPEGSurvival:
    @pytest.mark.parametrize("quality", [95, 85, 80, 75])
    def test_jpeg_gradient(self, tmp_path, quality):
        img = _make_image(kind='gradient')
        wm = embed_qr(img, METADATA)
        path = tmp_path / f"wm_q{quality}.jpg"
        wm.save(str(path), 'JPEG', quality=quality)
        result = detect_qr(Image.open(str(path)))
        assert result is not None, f"QR not detected after JPEG q={quality}"
        assert result['uid'] == 'test_user'

    @pytest.mark.parametrize("quality", [95, 85, 80])
    def test_jpeg_photo(self, tmp_path, quality):
        img = _make_image(kind='photo')
        wm = embed_qr(img, METADATA)
        path = tmp_path / f"photo_q{quality}.jpg"
        wm.save(str(path), 'JPEG', quality=quality)
        result = detect_qr(Image.open(str(path)))
        assert result is not None, f"QR not detected in photo after JPEG q={quality}"


# ── opacity variants ──────────────────────────────────────────────────────────

class TestOpacity:
    @pytest.mark.parametrize("opacity", [0.10, 0.15, 0.18, 0.25])
    def test_custom_opacity(self, opacity):
        img = _make_image(kind='gradient')
        wm = embed_qr(img, METADATA, opacity=opacity)
        result = detect_qr(wm)
        assert result is not None, f"Failed at opacity={opacity}"

    def test_image_unchanged_at_zero_opacity(self):
        """Sanity: at zero opacity, image should be identical to original."""
        img = _make_image(kind='solid')
        wm = embed_qr(img, METADATA, opacity=0.0)
        orig_arr = np.array(img)
        wm_arr = np.array(wm)
        assert np.array_equal(orig_arr, wm_arr)


# ── high-level API ────────────────────────────────────────────────────────────

class TestHighLevelAPI:
    def test_embed_algorithm_qr(self, tmp_path):
        img = _make_image()
        out = str(tmp_path / "out.png")
        meta = embed(img, out, {'uid': 'u1', 'platform': 'p1'}, algorithm='qr')
        assert 'clawid' in meta
        assert 'ts' in meta
        result = detect(out, algorithm='qr')
        assert result is not None
        assert result['uid'] == 'u1'

    def test_embed_algorithm_all(self, tmp_path):
        img = _make_image()
        out = str(tmp_path / "out.png")
        meta = embed(img, out, {'uid': 'u2', 'platform': 'p2'}, algorithm='all')
        # QR should be detectable
        r_qr = detect(out, algorithm='qr')
        assert r_qr is not None
        # DWT-QIM should also be detectable
        r_qim = detect(out, algorithm='qim')
        assert r_qim is not None

    def test_auto_detect_finds_qr_first(self, tmp_path):
        """Auto-detect should try QR first and return quickly."""
        img = _make_image()
        out = str(tmp_path / "out.png")
        embed(img, out, {'uid': 'u3'}, algorithm='qr')
        result = detect(out)  # algorithm='auto'
        assert result is not None
        assert result['uid'] == 'u3'

    def test_no_false_positive_on_unwatermarked(self, tmp_path):
        """Clean image should not be detected as watermarked."""
        img = _make_image()
        out = str(tmp_path / "clean.png")
        img.save(out)
        result = detect(out, algorithm='qr')
        assert result is None

    def test_metadata_round_trip_integrity(self, tmp_path):
        """All metadata fields survive the QR round-trip."""
        img = _make_image()
        out = str(tmp_path / "out.png")
        original_meta = {
            'uid': 'ashwath007',
            'platform': 'hawky.ai',
            'asset_id': 'gen_abc123',
        }
        embedded = embed(img, out, original_meta, algorithm='qr')
        detected = detect(out, algorithm='qr')
        assert detected['uid'] == original_meta['uid']
        assert detected['platform'] == original_meta['platform']
        assert detected['asset_id'] == original_meta['asset_id']
        assert detected['clawid'] == embedded['clawid']
        assert detected['ts'] == embedded['ts']
