"""Tests for ThumbnailSession and ThumbnailResult."""

import dataclasses
import os
import tempfile
import unittest
from unittest.mock import patch

from PIL import Image


def _make_jpeg(path: str, width: int = 1920, height: int = 1080) -> str:
    img = Image.new("RGB", (width, height), color=(100, 150, 200))
    img.save(path, "JPEG")
    return path


class TestThumbnailResult(unittest.TestCase):
    """ThumbnailResult dataclass has expected fields and defaults."""

    def test_fields_and_defaults(self):
        from video_thumbnail_creator.session import ThumbnailResult

        result = ThumbnailResult(poster_path="/tmp/poster.jpg")
        self.assertEqual(result.poster_path, "/tmp/poster.jpg")
        self.assertIsNone(result.fanart_path)
        self.assertEqual(result.frame_index, -1)
        self.assertEqual(result.crop_position, "center")
        self.assertEqual(result.reasoning, "")
        self.assertEqual(result.format, "poster")
        self.assertEqual(result.source, "frame")

    def test_is_dataclass(self):
        from video_thumbnail_creator.session import ThumbnailResult

        self.assertTrue(dataclasses.is_dataclass(ThumbnailResult))

    def test_all_fields_settable(self):
        from video_thumbnail_creator.session import ThumbnailResult

        result = ThumbnailResult(
            poster_path="/out/poster.jpg",
            fanart_path="/out/fanart.jpg",
            frame_index=5,
            crop_position="center-left",
            reasoning="AI chose this",
            format="landscape",
            source="image",
        )
        self.assertEqual(result.frame_index, 5)
        self.assertEqual(result.crop_position, "center-left")
        self.assertEqual(result.source, "image")


class TestThumbnailSessionImageInput(unittest.TestCase):
    """ThumbnailSession works correctly with an image input (no ffmpeg needed)."""

    def setUp(self):
        self.tmpdir = tempfile.mkdtemp()
        self.jpeg_path = os.path.join(self.tmpdir, "photo.jpg")
        _make_jpeg(self.jpeg_path, 1920, 1080)

    def tearDown(self):
        import shutil
        shutil.rmtree(self.tmpdir, ignore_errors=True)

    def _make_session(self):
        from video_thumbnail_creator.session import ThumbnailSession

        with patch("video_thumbnail_creator.image_converter.is_wide_gamut_image", return_value=False):
            return ThumbnailSession(self.jpeg_path)

    def test_highres_path_is_set(self):
        session = self._make_session()
        try:
            self.assertIsNotNone(session.highres_path)
            self.assertTrue(os.path.isfile(session.highres_path))
        finally:
            session.cleanup()

    def test_mosaic_path_is_none(self):
        session = self._make_session()
        try:
            self.assertIsNone(session.mosaic_path)
        finally:
            session.cleanup()

    def test_frame_paths_is_empty(self):
        session = self._make_session()
        try:
            self.assertEqual(session.frame_paths, [])
        finally:
            session.cleanup()

    def test_frame_selected_is_true(self):
        session = self._make_session()
        try:
            self.assertTrue(session._frame_selected)
        finally:
            session.cleanup()

    def test_video_properties_populated(self):
        session = self._make_session()
        try:
            props = session.video_properties
            self.assertIn("width", props)
            self.assertIn("height", props)
            self.assertIn("is_4k", props)
            self.assertIn("is_hdr", props)
            self.assertIn("is_hd", props)
            self.assertEqual(props["width"], 1920)
            self.assertEqual(props["height"], 1080)
            self.assertFalse(props["is_4k"])
            self.assertFalse(props["is_hdr"])
            self.assertTrue(props["is_hd"])
        finally:
            session.cleanup()

    def test_compose_landscape_produces_output(self):
        from video_thumbnail_creator.session import ThumbnailSession

        output_path = os.path.join(self.tmpdir, "output.jpg")
        with patch("video_thumbnail_creator.image_converter.is_wide_gamut_image", return_value=False):
            with ThumbnailSession(self.jpeg_path) as session:
                result = session.compose(
                    format="landscape",
                    output_path=output_path,
                )
        self.assertTrue(os.path.isfile(output_path))
        self.assertEqual(result.source, "image")
        self.assertEqual(result.frame_index, -1)

    def test_compose_poster_produces_output(self):
        from video_thumbnail_creator.session import ThumbnailSession

        output_path = os.path.join(self.tmpdir, "poster.jpg")
        with patch("video_thumbnail_creator.image_converter.is_wide_gamut_image", return_value=False):
            with ThumbnailSession(self.jpeg_path) as session:
                result = session.compose(
                    format="poster",
                    crop_position="center",
                    output_path=output_path,
                )
        self.assertTrue(os.path.isfile(output_path))
        self.assertEqual(result.format, "poster")

    def test_compose_landscape_with_title(self):
        from video_thumbnail_creator.session import ThumbnailSession

        output_path = os.path.join(self.tmpdir, "landscape.jpg")
        with patch("video_thumbnail_creator.image_converter.is_wide_gamut_image", return_value=False):
            with ThumbnailSession(self.jpeg_path) as session:
                result = session.compose(
                    format="landscape",
                    overlay_title="My Film",
                    output_path=output_path,
                )
        self.assertTrue(os.path.isfile(output_path))

    def test_context_manager_cleanup(self):
        from video_thumbnail_creator.session import ThumbnailSession

        with patch("video_thumbnail_creator.image_converter.is_wide_gamut_image", return_value=False):
            with ThumbnailSession(self.jpeg_path) as session:
                tmpdir = session._tmpdir
                self.assertTrue(os.path.isdir(tmpdir))

        self.assertFalse(os.path.isdir(tmpdir))

    def test_manual_cleanup(self):
        session = self._make_session()
        tmpdir = session._tmpdir
        self.assertTrue(os.path.isdir(tmpdir))
        session.cleanup()
        self.assertFalse(os.path.isdir(tmpdir))

    def test_cleanup_safe_to_call_twice(self):
        session = self._make_session()
        session.cleanup()
        session.cleanup()  # must not raise

    def test_missing_input_raises(self):
        from video_thumbnail_creator.session import ThumbnailSession

        with self.assertRaises(FileNotFoundError):
            ThumbnailSession("/nonexistent/photo.jpg")


class TestThumbnailSessionImageErrors(unittest.TestCase):
    """Error conditions for image input (operations that require a video)."""

    def setUp(self):
        self.tmpdir = tempfile.mkdtemp()
        self.jpeg_path = os.path.join(self.tmpdir, "photo.jpg")
        _make_jpeg(self.jpeg_path)

    def tearDown(self):
        import shutil
        shutil.rmtree(self.tmpdir, ignore_errors=True)

    def _make_session(self):
        from video_thumbnail_creator.session import ThumbnailSession

        with patch("video_thumbnail_creator.image_converter.is_wide_gamut_image", return_value=False):
            return ThumbnailSession(self.jpeg_path)

    def test_suggest_frame_raises_for_image(self):
        session = self._make_session()
        try:
            with self.assertRaises(RuntimeError):
                session.suggest_frame()
        finally:
            session.cleanup()

    def test_select_frame_raises_for_image(self):
        session = self._make_session()
        try:
            with self.assertRaises(RuntimeError):
                session.select_frame(0)
        finally:
            session.cleanup()


class TestThumbnailSessionVideoErrors(unittest.TestCase):
    """Error conditions for video input before frame is selected."""

    def test_compose_raises_without_frame_selected(self):
        """compose() raises RuntimeError if no frame has been selected."""
        from video_thumbnail_creator.session import ThumbnailSession

        tmpdir = tempfile.mkdtemp()
        try:
            fake_video = os.path.join(tmpdir, "video.mp4")
            # Create a dummy file to pass the existence check
            open(fake_video, "wb").close()

            with patch("video_thumbnail_creator.session.is_image_file", return_value=False), \
                 patch("video_thumbnail_creator.session.get_video_properties", return_value={"width": 1920, "height": 1080, "is_4k": False, "is_hdr": False, "is_hd": True, "fps": 0.0, "is_hfr": False}), \
                 patch("video_thumbnail_creator.session.extract_frames", return_value=[]), \
                 patch("video_thumbnail_creator.session.create_mosaic", return_value="/tmp/mosaic.jpg"):
                session = ThumbnailSession(fake_video)

            try:
                with self.assertRaises(RuntimeError) as ctx:
                    session.compose(output_path=os.path.join(tmpdir, "out.jpg"))
                self.assertIn("select_frame", str(ctx.exception))
            finally:
                session.cleanup()
        finally:
            import shutil
            shutil.rmtree(tmpdir, ignore_errors=True)

    def test_select_frame_invalid_index(self):
        """select_frame() raises ValueError for index outside 0–19."""
        from video_thumbnail_creator.session import ThumbnailSession

        tmpdir = tempfile.mkdtemp()
        try:
            fake_video = os.path.join(tmpdir, "video.mp4")
            open(fake_video, "wb").close()

            with patch("video_thumbnail_creator.session.is_image_file", return_value=False), \
                 patch("video_thumbnail_creator.session.get_video_properties", return_value={"width": 1920, "height": 1080, "is_4k": False, "is_hdr": False, "is_hd": True, "fps": 0.0, "is_hfr": False}), \
                 patch("video_thumbnail_creator.session.extract_frames", return_value=[]), \
                 patch("video_thumbnail_creator.session.create_mosaic", return_value="/tmp/mosaic.jpg"):
                session = ThumbnailSession(fake_video)

            try:
                with self.assertRaises(ValueError):
                    session.select_frame(20)
                with self.assertRaises(ValueError):
                    session.select_frame(-1)
            finally:
                session.cleanup()
        finally:
            import shutil
            shutil.rmtree(tmpdir, ignore_errors=True)


class TestThumbnailSessionExports(unittest.TestCase):
    """ThumbnailSession and ThumbnailResult are exported from the package."""

    def test_exported_from_package(self):
        from video_thumbnail_creator import ThumbnailSession, ThumbnailResult

        self.assertTrue(callable(ThumbnailSession))
        self.assertTrue(dataclasses.is_dataclass(ThumbnailResult))


if __name__ == "__main__":
    unittest.main()
