from pathlib import Path
import tempfile
from django.test import TestCase, override_settings
from django.contrib.auth.models import User
from django.utils import timezone

from viewer.models import Image as Im


class SpecialGalleriesTests(TestCase):
    def setUp(self):
        # prepare a temporary gallery root and files
        self.tmp_gallery = tempfile.TemporaryDirectory()
        self.gallery_root = Path(self.tmp_gallery.name)
        self.settings_override = override_settings(GALLERY_ROOT=self.gallery_root)
        self.settings_override.enable()

        self.user = User.objects.create_user("specuser", "s@example.com", "pw")

        # create files under gallery root
        now = timezone.now()
        a = self.gallery_root / "a.jpg"
        b = self.gallery_root / "b.jpg"
        c = self.gallery_root / "c.jpg"
        for p in (a, b, c):
            p.write_bytes(b"x")

        Im.objects.create(
            user=self.user, path=str(a), favorite=True, visits=5, last_visited=now
        )
        Im.objects.create(
            user=self.user, path=str(b), favorite=False, visits=10, last_visited=now
        )
        Im.objects.create(
            user=self.user, path=str(c), favorite=True, visits=2, last_visited=now
        )
        # client for view tests
        from django.test import Client

        self.client = Client()
        self.client.force_login(self.user)

    def tearDown(self):
        self.settings_override.disable()
        self.tmp_gallery.cleanup()

    def test_get_special_paths_filters_and_orders(self):
        from viewer.specials import get_special_paths

        favs = get_special_paths(self.user, "favorites")
        # favorites should include only those marked favorite (a and c)
        fav_names = sorted([p.name for p in favs])
        self.assertEqual(fav_names, ["a.jpg", "c.jpg"])

        most = get_special_paths(self.user, "most-visited")
        # most-visited should be ordered descending by visits, expect b.jpg first
        self.assertGreaterEqual(len(most), 1)
        self.assertEqual(most[0].name, "b.jpg")

    def test_favorites_directory_view_and_links(self):
        # Directory listing for favorites should show only favorite images
        resp = self.client.get("/gallery/favorites/")
        self.assertEqual(resp.status_code, 200)
        ctx = resp.context
        self.assertTrue(ctx.get("is_special"))
        names = [img["name"] for img in ctx.get("images")]
        self.assertEqual(sorted(names), ["a.jpg", "c.jpg"])
        # Back should be hidden (None) and home present
        self.assertIsNone(ctx.get("back_url"))
        self.assertIsNotNone(ctx.get("home_url"))
        self.assertTrue(str(ctx.get("home_url")).startswith("/gallery/"))
        # Breadcrumb should point to the special root
        breadcrumbs = ctx.get("breadcrumbs")
        self.assertEqual(breadcrumbs[0]["label"], "Favorites")
        self.assertTrue(breadcrumbs[0]["path"].startswith("/gallery/favorites/"))

    def test_favorites_image_view_prev_next_and_breadcrumbs(self):
        # Image view under special gallery should scope prev/next to favorites only
        resp = self.client.get("/gallery/favorites/a.jpg/")
        self.assertEqual(resp.status_code, 200)
        ctx = resp.context
        self.assertTrue(ctx.get("is_special"))
        # Breadcrumbs: first is special root (clickable), last is filename non-clickable
        crumbs = ctx.get("breadcrumbs")
        self.assertEqual(crumbs[0]["label"], "Favorites")
        self.assertIsNotNone(crumbs[0]["path"])
        self.assertEqual(crumbs[-1]["label"], "a.jpg")
        self.assertIsNone(crumbs[-1]["path"])
        # Prev should be None for first in alphabetical order among favorites
        self.assertIsNone(ctx.get("prev"))
        # Next should be c.jpg (since favorites are a.jpg and c.jpg sorted)
        self.assertIn("c.jpg", ctx.get("next"))

    def test_favorites_image_view_prev_next_urls_and_back_link(self):
        # Image view under favorites should produce prev/next URLs scoped to favorites
        resp = self.client.get("/gallery/favorites/a.jpg/")
        self.assertEqual(resp.status_code, 200)
        ctx = resp.context
        self.assertTrue(ctx.get("is_special"))

        crumbs = ctx.get("breadcrumbs")
        # back link for template should point to the special root
        self.assertTrue(crumbs[0]["path"].startswith("/gallery/favorites/"))

        # prev_url should be None for first favorite; next_url should be inside favorites
        self.assertIsNone(ctx.get("prev_url"))
        next_url = ctx.get("next_url")
        self.assertIsNotNone(next_url)
        self.assertTrue(next_url.startswith("/gallery/favorites/"))
        self.assertIn("c.jpg", next_url)

    def test_most_visited_image_view_prev_next_urls(self):
        # most-visited image view should scope prev/next URLs to the most-visited gallery
        resp = self.client.get("/gallery/most-visited/b.jpg/")
        self.assertEqual(resp.status_code, 200)
        ctx = resp.context
        self.assertTrue(ctx.get("is_special"))

        crumbs = ctx.get("breadcrumbs")
        self.assertTrue(crumbs[0]["path"].startswith("/gallery/most-visited/"))

        # b.jpg is the top most-visited image in fixtures => prev_url None, next_url points to a.jpg
        self.assertIsNone(ctx.get("prev_url"))
        next_url = ctx.get("next_url")
        self.assertIsNotNone(next_url)
        self.assertTrue(next_url.startswith("/gallery/most-visited/"))
        self.assertIn("a.jpg", next_url)

    def test_most_visited_and_recent_directory_views(self):
        # most-visited should list images ordered by visits desc
        resp = self.client.get("/gallery/most-visited/")
        self.assertEqual(resp.status_code, 200)
        names = [img["name"] for img in resp.context.get("images")]
        # b.jpg had visits=10, should be first
        self.assertEqual(names[0], "b.jpg")

        # recent: modify last_visited so that c is newest
        from viewer.models import Image as ImModel

        now = timezone.now()
        # update c to be most recent
        ImModel.objects.filter(path=str(self.gallery_root / "c.jpg")).update(
            last_visited=now
        )
        resp2 = self.client.get("/gallery/recent/")
        self.assertEqual(resp2.status_code, 200)
        recent_names = [img["name"] for img in resp2.context.get("images")]
        # c.jpg should be first in recent
        self.assertEqual(recent_names[0], "c.jpg")

    def test_missing_and_outside_paths_are_skipped(self):
        # Create an Image row pointing to a missing file inside gallery
        from viewer.models import Image as ImModel

        missing_path = self.gallery_root / "missing.jpg"
        ImModel.objects.create(
            user=self.user, path=str(missing_path), favorite=True, visits=1
        )

        # Create a file outside gallery root and an Image row pointing to it
        outside_tmp = tempfile.NamedTemporaryFile(delete=False)
        outside_tmp.write(b"x")
        outside_tmp.flush()
        outside_tmp.close()
        ImModel.objects.create(
            user=self.user, path=str(Path(outside_tmp.name)), favorite=True, visits=1
        )

        # favorites listing should still only include existing gallery files (a.jpg and c.jpg)
        resp = self.client.get("/gallery/favorites/")
        self.assertEqual(resp.status_code, 200)
        names = sorted([img["name"] for img in resp.context.get("images")])
        self.assertEqual(names, ["a.jpg", "c.jpg"])
