"""TDD 테스트 — iptc_tagger.py IPTC/XMP 메타데이터 삽입 유틸리티.

테스트 범위:
- make_xmp_packet(): XMP 패킷 생성 (내용, 형식, trainedAlgorithmicMedia 포함)
- encode_iptc_iim(): IPTC IIM 바이트 인코딩
- tag_image(): PNG/JPEG 파일 태깅
"""

import sys
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
from PIL import Image

sys.path.insert(0, str(Path(__file__).parent))

from iptc_tagger import encode_iptc_iim, make_xmp_packet, tag_image


# ─────────────────────────────────────────────────────────────────────────────
# Fixtures
# ─────────────────────────────────────────────────────────────────────────────


@pytest.fixture
def sample_png(tmp_path: Path) -> Path:
    """테스트용 작은 PNG 이미지 생성."""
    img = Image.new("RGB", (100, 100), color=(255, 0, 0))
    p = tmp_path / "test.png"
    img.save(str(p), format="PNG")
    return p


@pytest.fixture
def sample_jpeg(tmp_path: Path) -> Path:
    """테스트용 작은 JPEG 이미지 생성."""
    img = Image.new("RGB", (100, 100), color=(0, 255, 0))
    p = tmp_path / "test.jpg"
    img.save(str(p), format="JPEG")
    return p


# ─────────────────────────────────────────────────────────────────────────────
# TestMakeXmpPacket
# ─────────────────────────────────────────────────────────────────────────────


class TestMakeXmpPacket:
    def test_contains_trained_algorithmic_media(self) -> None:
        """make_xmp_packet() 반환값에 'trainedAlgorithmicMedia' 포함."""
        result = make_xmp_packet()
        assert "trainedAlgorithmicMedia" in result

    def test_contains_xmp_iptc_ext_namespace(self) -> None:
        """반환값에 'Iptc4xmpExt:DigitalSourceType' 포함."""
        result = make_xmp_packet()
        assert "Iptc4xmpExt:DigitalSourceType" in result

    def test_contains_title_when_provided(self) -> None:
        """make_xmp_packet(title='테스트') 반환값에 '테스트' 포함."""
        result = make_xmp_packet(title="테스트")
        assert "테스트" in result

    def test_contains_keyword_in_output(self) -> None:
        """make_xmp_packet(keywords=['AI-generated']) 반환값에 'AI-generated' 포함."""
        result = make_xmp_packet(keywords=["AI-generated"])
        assert "AI-generated" in result

    def test_returns_string(self) -> None:
        """반환값이 str 타입."""
        result = make_xmp_packet()
        assert isinstance(result, str)

    def test_xpacket_wrapping(self) -> None:
        """반환값이 '<?xpacket' 로 시작."""
        result = make_xmp_packet()
        assert result.startswith("<?xpacket")


# ─────────────────────────────────────────────────────────────────────────────
# TestEncodeIptcIim
# ─────────────────────────────────────────────────────────────────────────────


class TestEncodeIptcIim:
    def test_returns_bytes(self) -> None:
        """encode_iptc_iim() 반환값이 bytes 타입."""
        result = encode_iptc_iim()
        assert isinstance(result, bytes)

    def test_contains_iptc_marker(self) -> None:
        """반환값에 IPTC 마커 b'\\x1c' 포함."""
        result = encode_iptc_iim()
        assert b"\x1c" in result

    def test_with_title_contains_record2_dataset5(self) -> None:
        """title 있으면 record=2, dataset=5 (b'\\x1c\\x02\\x05') 포함."""
        result = encode_iptc_iim(title="테스트 타이틀")
        assert b"\x1c\x02\x05" in result

    def test_with_keyword_contains_record2_dataset25(self) -> None:
        """keyword 있으면 record=2, dataset=25 (b'\\x1c\\x02\\x19') 포함."""
        result = encode_iptc_iim(keywords=["AI-generated"])
        assert b"\x1c\x02\x19" in result


# ─────────────────────────────────────────────────────────────────────────────
# TestTagImagePng
# ─────────────────────────────────────────────────────────────────────────────


class TestTagImagePng:
    def test_returns_same_path_png(self, sample_png: Path) -> None:
        """PNG 태깅 시 입력 경로와 동일한 경로 반환."""
        result = tag_image(sample_png, title="테스트", keywords=["AI-generated"])
        assert result == sample_png

    def test_png_tagged_file_exists(self, sample_png: Path) -> None:
        """태깅 후 파일이 존재함."""
        tag_image(sample_png)
        assert sample_png.exists()

    def test_png_is_readable_after_tagging(self, sample_png: Path) -> None:
        """태깅 후 PIL Image.open()으로 열 수 있음."""
        tag_image(sample_png, title="읽기 테스트")
        img = Image.open(sample_png)
        assert img is not None

    def test_png_metadata_contains_xmp(self, sample_png: Path) -> None:
        """태깅된 PNG의 info에 XMP 관련 키 존재."""
        tag_image(sample_png, title="XMP 테스트", keywords=["AI-generated"])
        img = Image.open(sample_png)
        # PIL은 PNG XMP를 'XML:com.adobe.xmp' 또는 'xmp' 키로 노출
        xmp_keys = {k for k in img.info if "xmp" in k.lower() or "xml" in k.lower()}
        assert len(xmp_keys) > 0


# ─────────────────────────────────────────────────────────────────────────────
# TestTagImageJpeg
# ─────────────────────────────────────────────────────────────────────────────


class TestTagImageJpeg:
    def test_returns_same_path_jpeg(self, sample_jpeg: Path) -> None:
        """JPEG 태깅 시 동일 경로 반환."""
        result = tag_image(sample_jpeg, title="JPEG 테스트")
        assert result == sample_jpeg

    def test_jpeg_tagged_file_exists(self, sample_jpeg: Path) -> None:
        """태깅 후 파일 존재."""
        tag_image(sample_jpeg)
        assert sample_jpeg.exists()


# ─────────────────────────────────────────────────────────────────────────────
# TestTagImageEdgeCases
# ─────────────────────────────────────────────────────────────────────────────


class TestTagImageEdgeCases:
    def test_nonexistent_file_raises_exception(self, tmp_path: Path) -> None:
        """존재하지 않는 파일 전달 시 예외 발생."""
        nonexistent = tmp_path / "ghost.png"
        with pytest.raises(Exception):
            tag_image(nonexistent)

    def test_default_keywords_include_ai_generated(self, sample_png: Path) -> None:
        """keywords=None 전달 시 'AI-generated' 자동 포함 (XMP 패킷 확인)."""
        xmp = make_xmp_packet(keywords=None)
        assert "AI-generated" in xmp

    def test_returns_path_type(self, sample_png: Path) -> None:
        """반환값이 Path 타입."""
        result = tag_image(sample_png)
        assert isinstance(result, Path)
