"""Tests for compose_hero.py"""
import io
import json
import pathlib
import sys

import pytest
from PIL import Image

sys.path.insert(0, str(pathlib.Path(__file__).parents[1]))

from compose_hero import compose_hero, build_gradient_background, _ensure_fonts

FIXTURES = pathlib.Path(__file__).parents[1] / "fixtures" / "brief_prada2.json"


@pytest.fixture
def brief():
    return json.loads(FIXTURES.read_text(encoding="utf-8"))


@pytest.fixture
def synthetic_poster(tmp_path):
    """A 400×600 solid-red JPEG — simulates a movie poster."""
    img = Image.new("RGB", (400, 600), color=(180, 20, 20))
    path = tmp_path / "poster.jpg"
    img.save(path, "JPEG")
    return str(path)


class TestBuildGradientBackground:
    def test_output_size(self):
        img = build_gradient_background(1200, 800)
        assert img.size == (1200, 800)

    def test_output_mode(self):
        img = build_gradient_background(1200, 800)
        assert img.mode == "RGB"

    def test_top_left_is_dark(self):
        img = build_gradient_background(1200, 800)
        r, g, b = img.getpixel((0, 0))
        assert r < 50 and g < 50 and b < 50  # near black

    def test_bottom_right_is_light_purple(self):
        img = build_gradient_background(1200, 800)
        r, g, b = img.getpixel((1199, 799))
        # soft purple (#eeeaf4) = (238, 234, 244)
        assert r > 180 and b > 180


class TestComposeHeroOutputSpec:
    """The hero image must always meet these specs regardless of background mode."""

    @pytest.fixture
    def output_gradient(self, brief, synthetic_poster, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "gradient"
        out = tmp_path / "hero.jpg"
        compose_hero(brief, lang="fr", output_path=str(out))
        return out

    def test_output_file_exists(self, output_gradient):
        assert output_gradient.exists()

    def test_output_is_jpeg(self, output_gradient):
        img = Image.open(output_gradient)
        assert img.format == "JPEG"

    def test_output_dimensions(self, output_gradient):
        img = Image.open(output_gradient)
        assert img.size == (1200, 800)

    def test_jpeg_quality_approx_80(self, output_gradient):
        assert output_gradient.stat().st_size < 500_000

    def test_poster_is_on_right_half(self, output_gradient, brief):
        """The right half of the image should contain pixels from the red poster."""
        img = Image.open(output_gradient).convert("RGB")
        right_pixels = [img.getpixel((x, 400)) for x in range(800, 1200, 50)]
        avg_red = sum(p[0] for p in right_pixels) / len(right_pixels)
        assert avg_red > 100  # poster pixels bleed into this zone


class TestComposeHeroSolidBackground:
    def test_solid_black_top_left(self, brief, synthetic_poster, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "solid"
        brief["background_color"] = "#000000"
        out = tmp_path / "hero_solid.jpg"
        compose_hero(brief, lang="fr", output_path=str(out))
        img = Image.open(out).convert("RGB")
        r, g, b = img.getpixel((20, 20))
        assert r < 30 and g < 30 and b < 30


class TestComposeHeroBlurredBackground:
    def test_blurred_output_exists(self, brief, synthetic_poster, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "blurred"
        out = tmp_path / "hero_blurred.jpg"
        compose_hero(brief, lang="fr", output_path=str(out))
        assert out.exists()
        img = Image.open(out)
        assert img.size == (1200, 800)


class TestComposeHeroAiBackground:
    def test_ai_background_path_used(self, brief, synthetic_poster, tmp_path):
        # Simulate a pre-generated AI background image
        ai_bg = Image.new("RGB", (1400, 900), color=(50, 0, 100))
        ai_bg_path = tmp_path / "ai_bg.png"
        ai_bg.save(ai_bg_path)

        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "ai_generated"
        brief["ai_background_path"] = str(ai_bg_path)
        out = tmp_path / "hero_ai.jpg"
        compose_hero(brief, lang="fr", output_path=str(out))
        img = Image.open(out)
        assert img.size == (1200, 800)

    def test_missing_ai_path_raises(self, brief, synthetic_poster, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "ai_generated"
        brief["ai_background_path"] = None
        out = tmp_path / "hero_ai.jpg"
        with pytest.raises(ValueError, match="ai_background_path"):
            compose_hero(brief, lang="fr", output_path=str(out))


class TestComposeHeroNLText:
    def test_nl_output_exists(self, brief, synthetic_poster, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "gradient"
        out = tmp_path / "hero_nl.jpg"
        compose_hero(brief, lang="nl", output_path=str(out))
        assert out.exists()


class TestComposeHeroImageBackground:
    """The 'image' background mode must load a PNG and center-crop/resize to 1200×800."""

    @pytest.fixture
    def synthetic_background_png(self, tmp_path):
        """A 1400×900 solid-blue PNG — simulates the Proximus background asset."""
        img = Image.new("RGB", (1400, 900), color=(10, 0, 80))
        path = tmp_path / "background.png"
        img.save(path, "PNG")
        return str(path)

    def test_image_background_output_exists(self, brief, synthetic_poster, synthetic_background_png, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "image"
        brief["background_image_path"] = synthetic_background_png
        out = tmp_path / "hero_image_bg.jpg"
        compose_hero(brief, lang="fr", output_path=str(out))
        assert out.exists()

    def test_image_background_output_is_jpeg(self, brief, synthetic_poster, synthetic_background_png, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "image"
        brief["background_image_path"] = synthetic_background_png
        out = tmp_path / "hero_image_bg.jpg"
        compose_hero(brief, lang="fr", output_path=str(out))
        img = Image.open(out)
        assert img.format == "JPEG"

    def test_image_background_output_dimensions(self, brief, synthetic_poster, synthetic_background_png, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "image"
        brief["background_image_path"] = synthetic_background_png
        out = tmp_path / "hero_image_bg.jpg"
        compose_hero(brief, lang="fr", output_path=str(out))
        img = Image.open(out)
        assert img.size == (1200, 800)

    def test_missing_image_path_raises(self, brief, synthetic_poster, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "image"
        brief["background_image_path"] = None
        out = tmp_path / "hero_image_bg.jpg"
        with pytest.raises(ValueError, match="background_image_path"):
            compose_hero(brief, lang="fr", output_path=str(out))

    def test_nl_image_background_works(self, brief, synthetic_poster, synthetic_background_png, tmp_path):
        brief["poster_path"] = synthetic_poster
        brief["background_type"] = "image"
        brief["background_image_path"] = synthetic_background_png
        out = tmp_path / "hero_nl_image_bg.jpg"
        compose_hero(brief, lang="nl", output_path=str(out))
        assert out.exists()
        img = Image.open(out)
        assert img.size == (1200, 800)
