from io import BytesIO
from pathlib import Path
from tempfile import TemporaryDirectory

from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
from rest_framework import status
from rest_framework.test import APITestCase
from PIL import Image

from .models import ContentBlock, NavigationItem, SiteSettings


class CMSPermissionTests(APITestCase):
    def setUp(self):
        self.staff = User.objects.create_user(
            username="admin@example.com",
            email="admin@example.com",
            password="StrongPass123!",
            is_staff=True,
        )
        self.member = User.objects.create_user(
            username="member@example.com",
            email="member@example.com",
            password="StrongPass123!",
        )
        ContentBlock.objects.create(
            key="published-block",
            page="Home",
            section="Published",
            title="Visible content",
            is_published=True,
        )
        ContentBlock.objects.create(
            key="draft-block",
            page="Home",
            section="Draft",
            title="Private draft",
            is_published=False,
        )

    def test_public_content_api_only_returns_published_blocks(self):
        response = self.client.get("/api/content/?page_size=100")
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        keys = {item["key"] for item in response.data["results"]}
        self.assertIn("published-block", keys)
        self.assertNotIn("draft-block", keys)

        page_response = self.client.get("/api/content/?page_name=Home&page_size=100")
        self.assertEqual(page_response.status_code, status.HTTP_200_OK)
        self.assertTrue(all(item["page"] == "Home" for item in page_response.data["results"]))

    def test_regular_user_cannot_create_content(self):
        self.client.force_authenticate(self.member)
        response = self.client.post("/api/content/", {
            "key": "blocked-write",
            "page": "Home",
            "section": "Blocked",
            "title": "Should not be saved",
        })
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertFalse(ContentBlock.objects.filter(key="blocked-write").exists())

    def test_staff_can_view_drafts_and_create_content(self):
        self.client.force_authenticate(self.staff)
        listing = self.client.get("/api/content/?page_size=100")
        keys = {item["key"] for item in listing.data["results"]}
        self.assertIn("draft-block", keys)

        response = self.client.post("/api/content/", {
            "key": "staff-created",
            "page": "Pricing",
            "section": "Hero",
            "title": "Plans built for traders",
            "is_published": True,
        }, format="json")
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertTrue(ContentBlock.objects.filter(key="staff-created").exists())

    def test_summary_requires_staff_and_returns_metrics(self):
        anonymous = self.client.get("/api/cms/summary/")
        self.assertIn(anonymous.status_code, [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN])

        self.client.force_authenticate(self.staff)
        response = self.client.get("/api/cms/summary/")
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data["content_blocks"], ContentBlock.objects.count())
        self.assertEqual(
            response.data["published_blocks"],
            ContentBlock.objects.filter(is_published=True).count(),
        )

    def test_public_can_read_only_active_global_navigation(self):
        NavigationItem.objects.create(
            label="Hidden link", url="/hidden", location="header_primary",
            order=99, is_active=False,
        )
        response = self.client.get("/api/navigation/?page_size=100")
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        labels = {item["label"] for item in response.data["results"]}
        self.assertIn("Home", labels)
        self.assertNotIn("Hidden link", labels)

    def test_only_staff_can_change_site_settings(self):
        settings = SiteSettings.objects.get(key="main")
        self.client.force_authenticate(self.member)
        blocked = self.client.patch(
            f"/api/site-settings/{settings.id}/",
            {"site_name": "Blocked"},
            format="json",
        )
        self.assertEqual(blocked.status_code, status.HTTP_403_FORBIDDEN)

        self.client.force_authenticate(self.staff)
        allowed = self.client.patch(
            f"/api/site-settings/{settings.id}/",
            {"site_name": "TheStarFX Updated"},
            format="json",
        )
        self.assertEqual(allowed.status_code, status.HTTP_200_OK)
        self.assertEqual(allowed.data["site_name"], "TheStarFX Updated")

    def test_staff_can_upload_and_remove_shared_logo(self):
        settings_record = SiteSettings.objects.get(key="main")
        buffer = BytesIO()
        Image.new("RGB", (64, 64), color=(30, 64, 175)).save(buffer, format="PNG")
        upload = SimpleUploadedFile("brand.png", buffer.getvalue(), content_type="image/png")

        with TemporaryDirectory() as temp_dir, self.settings(
            CMS_LOGO_ROOT=Path(temp_dir),
            CMS_LOGO_URL="/api/assets/logo/",
        ):
            self.client.force_authenticate(self.staff)
            response = self.client.post(
                f"/api/site-settings/{settings_record.id}/logo/",
                {"logo": upload},
                format="multipart",
            )
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertTrue(response.data["logo_url"].startswith("/api/assets/logo/site-logo-"))
            saved_name = Path(response.data["logo_url"]).name
            self.assertTrue((Path(temp_dir) / saved_name).exists())

            public_asset = self.client.get(response.data["logo_url"])
            self.assertEqual(public_asset.status_code, status.HTTP_200_OK)
            self.assertEqual(public_asset["Content-Type"], "image/png")
            public_asset.close()

            removed = self.client.delete(f"/api/site-settings/{settings_record.id}/logo/")
            self.assertEqual(removed.status_code, status.HTTP_200_OK)
            self.assertEqual(removed.data["logo_url"], "")
            self.assertFalse((Path(temp_dir) / saved_name).exists())
