package com.swoosh.microblog.data.model

import com.google.gson.Gson
import org.junit.Assert.*
import org.junit.Test

class FeaturedPostsTest {

    private val gson = Gson()

    // --- GhostPost featured field ---

    @Test
    fun `GhostPost default featured is false`() {
        val post = GhostPost()
        assertEquals(false, post.featured)
    }

    @Test
    fun `GhostPost featured can be set to true`() {
        val post = GhostPost(featured = true)
        assertTrue(post.featured!!)
    }

    @Test
    fun `GhostPost featured can be set to null`() {
        val post = GhostPost(featured = null)
        assertNull(post.featured)
    }

    @Test
    fun `GhostPost featured serializes to JSON correctly`() {
        val post = GhostPost(id = "abc", featured = true)
        val json = gson.toJson(post)
        assertTrue(json.contains("\"featured\":true"))
    }

    @Test
    fun `GhostPost featured false serializes correctly`() {
        val post = GhostPost(id = "abc", featured = false)
        val json = gson.toJson(post)
        assertTrue(json.contains("\"featured\":false"))
    }

    @Test
    fun `GhostPost deserializes featured true from JSON`() {
        val json = """{"id":"xyz","featured":true}"""
        val post = gson.fromJson(json, GhostPost::class.java)
        assertTrue(post.featured!!)
    }

    @Test
    fun `GhostPost deserializes featured false from JSON`() {
        val json = """{"id":"xyz","featured":false}"""
        val post = gson.fromJson(json, GhostPost::class.java)
        assertFalse(post.featured!!)
    }

    @Test
    fun `GhostPost deserializes missing featured field`() {
        val json = """{"id":"xyz"}"""
        val post = gson.fromJson(json, GhostPost::class.java)
        // Gson may set missing Boolean? to null or false depending on initialization
        // The important thing is it doesn't crash and treats it as not featured
        assertTrue(post.featured == null || post.featured == false)
    }

    @Test
    fun `GhostPost copy preserves featured`() {
        val post = GhostPost(featured = true)
        val copy = post.copy(id = "new")
        assertTrue(copy.featured!!)
    }

    // --- LocalPost featured field ---

    @Test
    fun `LocalPost default featured is false`() {
        val post = LocalPost()
        assertFalse(post.featured)
    }

    @Test
    fun `LocalPost featured can be set to true`() {
        val post = LocalPost(featured = true)
        assertTrue(post.featured)
    }

    @Test
    fun `LocalPost copy preserves featured`() {
        val post = LocalPost(featured = true)
        val copy = post.copy(title = "updated")
        assertTrue(copy.featured)
    }

    @Test
    fun `LocalPost copy can change featured`() {
        val post = LocalPost(featured = true)
        val copy = post.copy(featured = false)
        assertFalse(copy.featured)
    }

    // --- FeedPost featured field ---

    @Test
    fun `FeedPost default featured is false`() {
        val post = createFeedPost()
        assertFalse(post.featured)
    }

    @Test
    fun `FeedPost featured can be set to true`() {
        val post = createFeedPost(featured = true)
        assertTrue(post.featured)
    }

    @Test
    fun `FeedPost copy preserves featured`() {
        val post = createFeedPost(featured = true)
        val copy = post.copy(title = "updated")
        assertTrue(copy.featured)
    }

    @Test
    fun `FeedPost copy can toggle featured`() {
        val post = createFeedPost(featured = true)
        val toggled = post.copy(featured = !post.featured)
        assertFalse(toggled.featured)
    }

    // --- Feed ordering (pinned first) ---

    @Test
    fun `featured posts sort before non-featured posts`() {
        val regular = createFeedPost(ghostId = "1", featured = false, publishedAt = "2025-01-02T00:00:00Z")
        val pinned = createFeedPost(ghostId = "2", featured = true, publishedAt = "2025-01-01T00:00:00Z")

        val sorted = listOf(regular, pinned).sortedWith(
            compareByDescending<FeedPost> { it.featured }
                .thenByDescending { it.publishedAt ?: it.createdAt ?: "" }
        )

        assertTrue(sorted[0].featured)
        assertEquals("2", sorted[0].ghostId)
        assertFalse(sorted[1].featured)
        assertEquals("1", sorted[1].ghostId)
    }

    @Test
    fun `multiple featured posts sorted chronologically among themselves`() {
        val pinned1 = createFeedPost(ghostId = "1", featured = true, publishedAt = "2025-01-01T00:00:00Z")
        val pinned2 = createFeedPost(ghostId = "2", featured = true, publishedAt = "2025-01-03T00:00:00Z")
        val pinned3 = createFeedPost(ghostId = "3", featured = true, publishedAt = "2025-01-02T00:00:00Z")

        val sorted = listOf(pinned1, pinned2, pinned3).sortedWith(
            compareByDescending<FeedPost> { it.featured }
                .thenByDescending { it.publishedAt ?: it.createdAt ?: "" }
        )

        assertEquals("2", sorted[0].ghostId)
        assertEquals("3", sorted[1].ghostId)
        assertEquals("1", sorted[2].ghostId)
    }

    @Test
    fun `non-featured posts sorted chronologically among themselves`() {
        val post1 = createFeedPost(ghostId = "1", featured = false, publishedAt = "2025-01-01T00:00:00Z")
        val post2 = createFeedPost(ghostId = "2", featured = false, publishedAt = "2025-01-03T00:00:00Z")
        val post3 = createFeedPost(ghostId = "3", featured = false, publishedAt = "2025-01-02T00:00:00Z")

        val sorted = listOf(post1, post2, post3).sortedWith(
            compareByDescending<FeedPost> { it.featured }
                .thenByDescending { it.publishedAt ?: it.createdAt ?: "" }
        )

        assertEquals("2", sorted[0].ghostId)
        assertEquals("3", sorted[1].ghostId)
        assertEquals("1", sorted[2].ghostId)
    }

    @Test
    fun `mixed featured and non-featured posts sort correctly`() {
        val posts = listOf(
            createFeedPost(ghostId = "1", featured = false, publishedAt = "2025-01-05T00:00:00Z"),
            createFeedPost(ghostId = "2", featured = true, publishedAt = "2025-01-01T00:00:00Z"),
            createFeedPost(ghostId = "3", featured = false, publishedAt = "2025-01-04T00:00:00Z"),
            createFeedPost(ghostId = "4", featured = true, publishedAt = "2025-01-03T00:00:00Z")
        )

        val sorted = posts.sortedWith(
            compareByDescending<FeedPost> { it.featured }
                .thenByDescending { it.publishedAt ?: it.createdAt ?: "" }
        )

        // Featured first: 4 (Jan 3), 2 (Jan 1)
        assertEquals("4", sorted[0].ghostId)
        assertTrue(sorted[0].featured)
        assertEquals("2", sorted[1].ghostId)
        assertTrue(sorted[1].featured)
        // Then regular: 1 (Jan 5), 3 (Jan 4)
        assertEquals("1", sorted[2].ghostId)
        assertFalse(sorted[2].featured)
        assertEquals("3", sorted[3].ghostId)
        assertFalse(sorted[3].featured)
    }

    @Test
    fun `empty list sorts without error`() {
        val sorted = emptyList<FeedPost>().sortedWith(
            compareByDescending<FeedPost> { it.featured }
                .thenByDescending { it.publishedAt ?: it.createdAt ?: "" }
        )
        assertTrue(sorted.isEmpty())
    }

    @Test
    fun `single featured post list sorts correctly`() {
        val posts = listOf(createFeedPost(ghostId = "1", featured = true))
        val sorted = posts.sortedWith(
            compareByDescending<FeedPost> { it.featured }
                .thenByDescending { it.publishedAt ?: it.createdAt ?: "" }
        )
        assertEquals(1, sorted.size)
        assertTrue(sorted[0].featured)
    }

    @Test
    fun `partitioning posts into pinned and regular sections works`() {
        val posts = listOf(
            createFeedPost(ghostId = "1", featured = true),
            createFeedPost(ghostId = "2", featured = false),
            createFeedPost(ghostId = "3", featured = true),
            createFeedPost(ghostId = "4", featured = false)
        )

        val pinned = posts.filter { it.featured }
        val regular = posts.filter { !it.featured }

        assertEquals(2, pinned.size)
        assertEquals(2, regular.size)
        assertTrue(pinned.all { it.featured })
        assertTrue(regular.none { it.featured })
    }

    // --- Model mapping with featured ---

    @Test
    fun `PostWrapper with featured post serializes correctly`() {
        val wrapper = PostWrapper(listOf(GhostPost(title = "Test", featured = true, updated_at = "2025-01-01T00:00:00Z")))
        val json = gson.toJson(wrapper)
        assertTrue(json.contains("\"featured\":true"))
        assertTrue(json.contains("\"updated_at\":\"2025-01-01T00:00:00Z\""))
    }

    @Test
    fun `PostsResponse with featured posts deserializes correctly`() {
        val json = """{
            "posts": [
                {"id": "1", "title": "Pinned", "featured": true},
                {"id": "2", "title": "Regular", "featured": false}
            ],
            "meta": {"pagination": {"page": 1, "limit": 15, "pages": 1, "total": 2, "next": null, "prev": null}}
        }"""
        val response = gson.fromJson(json, PostsResponse::class.java)
        assertEquals(2, response.posts.size)
        assertTrue(response.posts[0].featured!!)
        assertFalse(response.posts[1].featured!!)
    }

    @Test
    fun `toggle featured API request body is correct`() {
        val post = GhostPost(featured = true, updated_at = "2025-01-01T00:00:00Z")
        val wrapper = PostWrapper(listOf(post))
        val json = gson.toJson(wrapper)
        assertTrue(json.contains("\"featured\":true"))
        assertTrue(json.contains("\"updated_at\""))
    }

    // --- Helper ---

    private fun createFeedPost(
        ghostId: String? = null,
        featured: Boolean = false,
        publishedAt: String? = null
    ) = FeedPost(
        ghostId = ghostId,
        title = "Test",
        textContent = "Content",
        htmlContent = null,
        imageUrl = null,
        linkUrl = null,
        linkTitle = null,
        linkDescription = null,
        linkImageUrl = null,
        status = "published",
        featured = featured,
        publishedAt = publishedAt,
        createdAt = null,
        updatedAt = null
    )
}
