import pytest
from django.contrib.auth import get_user_model
from model_bakery import baker
from blogs.models import Blogs
from products.models import Products, FeaturedProducts
from django.contrib.contenttypes.models import ContentType
import json
from unittest.mock import patch
from django.core.files.uploadedfile import SimpleUploadedFile

Users = get_user_model()


@pytest.mark.django_db()
class TestBlogsModel:
    def test_return_str_model(self):
        modified_body = json.dumps({"delta": "", "html": "Hello World"})
        blog = baker.make(Blogs, body=modified_body)

        assert str(blog) == blog.title


@pytest.mark.django_db()
class TestBlogsFetch:
    def setup_method(self):
        self.admin = baker.make(Users, is_staff=True)
        self.user = baker.make(Users, is_staff=False)

        modified_body = json.dumps({"delta": "", "html": "Hello World"})

        self.blog_1 = baker.make(Blogs, title="Blog 1", body=modified_body)
        self.blog_2 = baker.make(Blogs, title="Blog 2", body=modified_body)

    def test_fetch_blogs_by_any_user_return_200(self, authenticate_user):
        api_client = authenticate_user()

        response = api_client.get("/api/blogs/")

        assert response.status_code == 200
        assert response.data["count"] == 2

    def test_fetch_specific_blog_by_any_user_return_200(self, authenticate_user):
        api_client = authenticate_user(user=self.user)

        response = api_client.get(f"/api/blogs/{self.blog_1.id}/")

        assert response.status_code == 200
        assert response.data["id"] == str(self.blog_1.id)


@pytest.mark.django_db()
class TestBlogsCreate:

    def setup_method(self):
        self.copywriter = baker.make(Users, is_staff=True, role="Copywriter")
        self.web_admin = baker.make(Users, is_staff=True, role="Web Admin")

        self.user = baker.make(Users, is_staff=False)

        modified_body = json.dumps({"delta": "", "html": "Hello World"})

        self.blog_1 = baker.make(Blogs, title="Blog 1", body=modified_body)
        self.product = baker.make(
            Products, name="Sample Product", description=modified_body
        )

    def test_create_blog_by_non_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.user)

        data = {
            "title": "Test Blog",
            "body": json.dumps({"delta": "", "html": "New Body"}),
        }

        response = api_client.post("/api/blogs/", data=data)

        assert response.status_code == 403

    def test_create_blog_by_non_copywriter_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.web_admin)

        data = {
            "title": "Test Blog",
            "body": json.dumps({"delta": "", "html": "New Body"}),
        }

        response = api_client.post("/api/blogs/", data=data)

        assert response.status_code == 403

    def test_create_blog_by_admin_copywriter_with_no_banner_return_400(
        self, authenticate_user
    ):
        api_client = authenticate_user(user=self.copywriter)

        data = {
            "title": "Test Blog",
            "body": json.dumps({"delta": "", "html": "New Body"}),
        }

        response = api_client.post("/api/blogs/", data=data)

        assert response.status_code == 400
        assert (
            response.data["message"] == "Image banner is required in creating a blog."
        )

    def test_create_blog_by_admin_copywriter_with_existing_blog_title_return_400(
        self, authenticate_user
    ):
        api_client = authenticate_user(user=self.copywriter)

        data = {
            "title": "Blog 1",
            "date_created": "2025-05-08T00:00:00+08:00",
            "body": json.dumps({"delta": "", "html": "New Body"}),
            "image_banner": "http://sample.image.com/",
            "featured_products[]": str(self.product.id),
        }

        response = api_client.post("/api/blogs/", data=data)

        assert response.status_code == 400
        assert (
            response.data["message"]
            == f"Blog with title '{data['title']}' already exists.",
        )

    def test_create_blog_by_admin_copywriter_with_no_date_created_return_400(
        self, authenticate_user
    ):
        api_client = authenticate_user(user=self.copywriter)

        data = {
            "title": "Blog 1",
            "body": json.dumps({"delta": "", "html": "New Body"}),
            "image_banner": "http://sample.image.com/",
            "featured_products[]": str(self.product.id),
        }

        response = api_client.post("/api/blogs/", data=data)

        assert response.status_code == 400
        assert (
            response.data["message"]
            == "Date created is required in creating a blog.",
        )

    @patch("blogs.views.R2")
    def test_create_blog_by_admin_copywriter_return_201(
        self, mock_r2, authenticate_user
    ):
        api_client = authenticate_user(user=self.copywriter)

        mock_r2_instance = mock_r2.return_value
        mock_r2_instance.upload_fileobj.return_value = "/blogs_images/image.jpg"
        mock_image = SimpleUploadedFile(
            "image.jpg", b"mock-image-content", content_type="image/jpeg"
        )
        data = {
            "title": "Blog 2",
            "body": json.dumps({"delta": "", "html": "New Body 2"}),
            "date_created": "2025-05-08T00:00:00+08:00",
            "image_banner": mock_image,
            "featured_products[]": str(self.product.id),
        }

        response = api_client.post("/api/blogs/", data=data)

        assert response.status_code == 201
        assert response.data["id"] is not None
        assert len(response.data["featured_products"]) == 1


@pytest.mark.django_db()
class TestBlogsUpdate:
    def setup_method(self):
        self.copywriter = baker.make(Users, is_staff=True, role="Copywriter")
        self.web_admin = baker.make(Users, is_staff=True, role="Web Admin")

        self.user = baker.make(Users, is_staff=False)

        modified_body = json.dumps({"delta": "", "html": "Hello World"})

        self.product_1 = baker.make(
            Products, name="Sample Product 1", description=modified_body
        )
        self.product_2 = baker.make(
            Products, name="Sample Product 2", description=modified_body
        )

        self.blog_1 = baker.make(Blogs, title="Blog 1", body=modified_body)
        self.blog_2 = baker.make(Blogs, title="Blog 2", body=modified_body)
        self.blog_content_type = ContentType.objects.get_for_model(Blogs)

        FeaturedProducts.objects.create(
            model=self.blog_content_type,
            model_object_id=self.blog_1.id,
            product=self.product_1,
        )

    def test_update_blog_by_non_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.user)

        data = {
            "title": "Test Blog",
            "body": json.dumps({"delta": "", "html": "New Body"}),
        }

        response = api_client.put(f"/api/blogs/{self.blog_1.id}/", data=data)

        assert response.status_code == 403

    def test_update_blog_by_non_copywriter_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.web_admin)

        data = {
            "title": "Test Blog",
            "body": json.dumps({"delta": "", "html": "New Body"}),
        }

        response = api_client.put(f"/api/blogs/{self.blog_1.id}/", data=data)

        assert response.status_code == 403

    def test_update_blog_by_admin_copywriter_with_existing_blog_title_return_400(
        self, authenticate_user
    ):
        api_client = authenticate_user(user=self.copywriter)

        data = {
            "title": "Blog 2",
            "body": json.dumps({"delta": "", "html": "New Body"}),
            "image_banner": "http://sample.image.com/",
            "featured_products[]": str(self.product_1.id),
        }

        response = api_client.put(f"/api/blogs/{self.blog_1.id}/", data=data)

        assert response.status_code == 400
        assert (
            response.data["message"]
            == f"Blog with title '{data['title']}' already exists."
        )

    @patch("blogs.views.R2")
    def test_update_blog_by_admin_copywriter_with_replaced_featured_product_return_200(
        self, mock_r2, authenticate_user
    ):
        api_client = authenticate_user(user=self.copywriter)

        mock_r2_instance = mock_r2.return_value
        mock_r2_instance.upload_fileobj.return_value = "/blogs_images/image.jpg"
        mock_image = SimpleUploadedFile(
            "image.jpg", b"mock-image-content", content_type="image/jpeg"
        )

        data = {
            "title": "Updated Blog 1",
            "body": json.dumps({"delta": "", "html": "New Body"}),
            "date_created": "2025-05-08T00:00:00+08:00",
            "image_banner": mock_image,
            "featured_products[]": str(self.product_2.id),
        }

        response = api_client.put(f"/api/blogs/{self.blog_1.id}/", data=data)

        assert response.status_code == 200
        assert response.data["id"] is not None
        assert len(response.data["featured_products"]) == 1

        blog_featured_products = FeaturedProducts.objects.filter(
            model=self.blog_content_type, model_object_id=self.blog_1.id
        )

        assert len(blog_featured_products) == 1

    @patch("blogs.views.R2")
    def test_update_blog_by_admin_copywriter_with_updated_and_retained_featured_products_return_200(
        self, mock_r2, authenticate_user
    ):
        api_client = authenticate_user(user=self.copywriter)

        mock_r2_instance = mock_r2.return_value
        mock_r2_instance.upload_fileobj.return_value = "/blogs_images/image.jpg"
        mock_image = SimpleUploadedFile(
            "image.jpg", b"mock-image-content", content_type="image/jpeg"
        )

        data = {
            "title": "Updated Blog 1",
            "body": json.dumps({"delta": "", "html": "New Body"}),
            "image_banner": mock_image,
            "date_created": "2025-05-08T00:00:00+08:00",
            "featured_products[]": [str(self.product_1.id), str(self.product_2.id)],
        }

        response = api_client.put(f"/api/blogs/{self.blog_1.id}/", data=data)

        assert response.status_code == 200
        assert response.data["id"] is not None
        assert len(response.data["featured_products"]) == 2


@pytest.mark.django_db()
class TestBlogsArchive:
    def setup_method(self):
        self.copywriter = baker.make(Users, is_staff=True, role="Copywriter")
        self.web_admin = baker.make(Users, is_staff=True, role="Web Admin")

        self.user = baker.make(Users, is_staff=False)

        modified_body = json.dumps({"delta": "", "html": "Hello World"})

        self.product_1 = baker.make(
            Products, name="Sample Product 1", description=modified_body
        )

        self.blog_1 = baker.make(Blogs, title="Blog 1", body=modified_body)
        self.blog_content_type = ContentType.objects.get_for_model(Blogs)

        FeaturedProducts.objects.create(
            model=self.blog_content_type,
            model_object_id=self.blog_1.id,
            product=self.product_1,
        )

    def test_archive_blog_by_non_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.user)

        response = api_client.get(f"/api/blogs/{self.blog_1.id}/archive/")

        assert response.status_code == 403

    def test_archive_blog_by_non_copywriter_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.web_admin)

        response = api_client.get(f"/api/blogs/{self.blog_1.id}/archive/")

        assert response.status_code == 403

    def test_unarchive_blog_by_non_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.user)

        response = api_client.get(f"/api/blogs/{self.blog_1.id}/unarchive/")

        assert response.status_code == 403

    def test_unarchive_blog_by_non_copywriter_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.web_admin)

        response = api_client.get(f"/api/blogs/{self.blog_1.id}/unarchive/")

        assert response.status_code == 403


@pytest.mark.django_db()
class TestBlogsDelete:

    def setup_method(self):
        self.copywriter = baker.make(Users, is_staff=True, role="Copywriter")
        self.web_admin = baker.make(Users, is_staff=True, role="Web Admin")

        self.user = baker.make(Users, is_staff=False)

        modified_body = json.dumps({"delta": "", "html": "Hello World"})

        self.product_1 = baker.make(
            Products, name="Sample Product 1", description=modified_body
        )

        self.blog_1 = baker.make(Blogs, title="Blog 1", body=modified_body)
        self.blog_content_type = ContentType.objects.get_for_model(Blogs)

        FeaturedProducts.objects.create(
            model=self.blog_content_type,
            model_object_id=self.blog_1.id,
            product=self.product_1,
        )

    def test_delete_blog_by_non_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.user)

        response = api_client.delete(f"/api/blogs/{self.blog_1.id}/")

        assert response.status_code == 403

    def test_delete_blog_by_non_copywriter_admin_return_403(self, authenticate_user):
        api_client = authenticate_user(user=self.web_admin)

        response = api_client.delete(f"/api/blogs/{self.blog_1.id}/")

        assert response.status_code == 403

    def test_delete_blog_by_copywriter_admin_return_204(self, authenticate_user):
        api_client = authenticate_user(user=self.copywriter)

        response = api_client.delete(f"/api/blogs/{self.blog_1.id}/")

        assert response.status_code == 204
