package com.swoosh.microblog.ui.feed

import com.swoosh.microblog.data.model.FeedPost
import com.swoosh.microblog.data.model.QueueStatus
import org.junit.Assert.*
import org.junit.Test

/**
 * Tests for swipe action logic, delete confirmation, and undo behavior.
 * These test the pure data/logic aspects that don't require Android framework.
 */
class SwipeActionsTest {

    private fun createLocalPost(
        localId: Long = 1L,
        ghostId: String? = null,
        content: String = "Test post content",
        status: String = "draft",
        isLocal: Boolean = true,
        queueStatus: QueueStatus = QueueStatus.NONE
    ) = FeedPost(
        localId = localId,
        ghostId = ghostId,
        title = content.take(60),
        textContent = content,
        htmlContent = null,
        imageUrl = null,
        linkUrl = null,
        linkTitle = null,
        linkDescription = null,
        linkImageUrl = null,
        status = status,
        publishedAt = null,
        createdAt = null,
        updatedAt = null,
        isLocal = isLocal,
        queueStatus = queueStatus
    )

    private fun createRemotePost(
        ghostId: String = "remote_1",
        content: String = "Remote post content",
        status: String = "published"
    ) = FeedPost(
        localId = null,
        ghostId = ghostId,
        title = content.take(60),
        textContent = content,
        htmlContent = "<p>$content</p>",
        imageUrl = null,
        linkUrl = null,
        linkTitle = null,
        linkDescription = null,
        linkImageUrl = null,
        status = status,
        publishedAt = "2024-01-15T10:00:00.000Z",
        createdAt = "2024-01-15T09:00:00.000Z",
        updatedAt = "2024-01-15T10:00:00.000Z",
        isLocal = false,
        queueStatus = QueueStatus.NONE
    )

    // --- Post identification for swipe actions ---

    @Test
    fun `local post has localId for undo support`() {
        val post = createLocalPost(localId = 42)
        assertTrue(post.isLocal)
        assertNotNull(post.localId)
        assertEquals(42L, post.localId)
    }

    @Test
    fun `remote post has ghostId for delete action`() {
        val post = createRemotePost(ghostId = "abc123")
        assertFalse(post.isLocal)
        assertNotNull(post.ghostId)
        assertEquals("abc123", post.ghostId)
    }

    @Test
    fun `local post can have both localId and ghostId`() {
        val post = createLocalPost(localId = 1, ghostId = "ghost_123")
        assertTrue(post.isLocal)
        assertNotNull(post.localId)
        assertNotNull(post.ghostId)
    }

    // --- Swipe action applicability ---

    @Test
    fun `edit action applies to local draft posts`() {
        val post = createLocalPost(status = "draft")
        // Edit is always available - post has data to populate composer
        assertTrue(post.textContent.isNotEmpty())
    }

    @Test
    fun `edit action applies to remote published posts`() {
        val post = createRemotePost(status = "published")
        assertTrue(post.textContent.isNotEmpty())
        assertNotNull(post.ghostId)
    }

    @Test
    fun `edit action applies to remote draft posts`() {
        val post = createRemotePost(ghostId = "draft_1", status = "draft")
        assertTrue(post.textContent.isNotEmpty())
    }

    @Test
    fun `edit action applies to scheduled posts`() {
        val post = createRemotePost(ghostId = "sched_1", status = "scheduled")
        assertEquals("scheduled", post.status)
    }

    @Test
    fun `delete action applies to local posts with localId`() {
        val post = createLocalPost(localId = 5)
        assertNotNull(post.localId)
        assertTrue(post.isLocal)
    }

    @Test
    fun `delete action applies to remote posts with ghostId`() {
        val post = createRemotePost(ghostId = "remote_5")
        assertNotNull(post.ghostId)
        assertFalse(post.isLocal)
    }

    // --- Undo eligibility ---

    @Test
    fun `local post is eligible for undo after delete`() {
        val post = createLocalPost(localId = 10)
        // Undo is supported for local posts (re-insert into Room)
        assertTrue(post.isLocal)
        assertNotNull(post.localId)
    }

    @Test
    fun `remote post is not eligible for undo after delete`() {
        val post = createRemotePost(ghostId = "remote_10")
        // Remote posts cannot be undeleted via Ghost API
        assertFalse(post.isLocal)
        assertNull(post.localId)
    }

    @Test
    fun `local post with ghostId - undo re-inserts local copy only`() {
        val post = createLocalPost(localId = 3, ghostId = "ghost_3", isLocal = true)
        // Even local posts with ghostId can be undone (re-insert local row)
        assertTrue(post.isLocal)
        assertNotNull(post.localId)
        assertNotNull(post.ghostId)
    }

    // --- SnackbarEvent data class ---

    @Test
    fun `SnackbarEvent with undo action has actionLabel`() {
        val event = SnackbarEvent(
            message = "Post deleted",
            actionLabel = "Undo",
            onAction = { /* restore */ }
        )
        assertEquals("Post deleted", event.message)
        assertEquals("Undo", event.actionLabel)
        assertNotNull(event.onAction)
    }

    @Test
    fun `SnackbarEvent without undo has null action`() {
        val event = SnackbarEvent(message = "Post deleted")
        assertEquals("Post deleted", event.message)
        assertNull(event.actionLabel)
        assertNull(event.onAction)
    }

    @Test
    fun `SnackbarEvent onAction callback is invocable`() {
        var undoCalled = false
        val event = SnackbarEvent(
            message = "Post deleted",
            actionLabel = "Undo",
            onAction = { undoCalled = true }
        )
        event.onAction?.invoke()
        assertTrue(undoCalled)
    }

    // --- Post card key generation ---

    @Test
    fun `post with ghostId uses ghostId as key`() {
        val post = createRemotePost(ghostId = "unique_ghost_id")
        val key = post.ghostId ?: "local_${post.localId}"
        assertEquals("unique_ghost_id", key)
    }

    @Test
    fun `local post without ghostId uses localId as key`() {
        val post = createLocalPost(localId = 42, ghostId = null)
        val key = post.ghostId ?: "local_${post.localId}"
        assertEquals("local_42", key)
    }

    // --- FeedUiState ---

    @Test
    fun `FeedUiState default values are correct`() {
        val state = FeedUiState()
        assertTrue(state.posts.isEmpty())
        assertFalse(state.isRefreshing)
        assertFalse(state.isLoadingMore)
        assertNull(state.error)
        assertFalse(state.isConnectionError)
    }

    @Test
    fun `FeedUiState posts can be filtered by isLocal`() {
        val posts = listOf(
            createLocalPost(localId = 1),
            createRemotePost(ghostId = "r1"),
            createLocalPost(localId = 2),
            createRemotePost(ghostId = "r2")
        )
        val state = FeedUiState(posts = posts)
        val localPosts = state.posts.filter { it.isLocal }
        val remotePosts = state.posts.filter { !it.isLocal }
        assertEquals(2, localPosts.size)
        assertEquals(2, remotePosts.size)
    }

    @Test
    fun `removing a post from list simulates delete`() {
        val posts = listOf(
            createRemotePost(ghostId = "r1", content = "First"),
            createRemotePost(ghostId = "r2", content = "Second"),
            createRemotePost(ghostId = "r3", content = "Third")
        )
        val postToDelete = posts[1]
        val afterDelete = posts.filter { it.ghostId != postToDelete.ghostId }
        assertEquals(2, afterDelete.size)
        assertEquals("r1", afterDelete[0].ghostId)
        assertEquals("r3", afterDelete[1].ghostId)
    }

    @Test
    fun `re-adding a post to list simulates undo`() {
        val originalPosts = listOf(
            createLocalPost(localId = 1, content = "First"),
            createLocalPost(localId = 2, content = "Second")
        )
        val deleted = originalPosts[0]
        val afterDelete = originalPosts.filter { it.localId != deleted.localId }
        assertEquals(1, afterDelete.size)

        // Simulate undo: re-add
        val afterUndo = listOf(deleted) + afterDelete
        assertEquals(2, afterUndo.size)
        assertEquals(1L, afterUndo[0].localId)
    }

    // --- Queue status interactions ---

    @Test
    fun `queued post can still be swiped for edit`() {
        val post = createLocalPost(
            localId = 1,
            queueStatus = QueueStatus.QUEUED_PUBLISH
        )
        assertTrue(post.queueStatus != QueueStatus.NONE)
        // Edit should still work for queued posts
        assertTrue(post.textContent.isNotEmpty())
    }

    @Test
    fun `uploading post can be swiped for delete`() {
        val post = createLocalPost(
            localId = 1,
            queueStatus = QueueStatus.UPLOADING
        )
        assertTrue(post.queueStatus == QueueStatus.UPLOADING)
        assertNotNull(post.localId)
    }

    @Test
    fun `failed upload post can be swiped for edit and delete`() {
        val post = createLocalPost(
            localId = 1,
            queueStatus = QueueStatus.FAILED
        )
        assertTrue(post.queueStatus == QueueStatus.FAILED)
        assertTrue(post.textContent.isNotEmpty())
        assertNotNull(post.localId)
    }

    // --- Post data preservation for edit ---

    @Test
    fun `post preserves all fields needed for edit`() {
        val post = FeedPost(
            localId = 5L,
            ghostId = "g5",
            title = "My Title",
            textContent = "Full content here",
            htmlContent = "<p>Full content here</p>",
            imageUrl = "https://example.com/image.jpg",
            linkUrl = "https://example.com",
            linkTitle = "Example",
            linkDescription = "An example link",
            linkImageUrl = "https://example.com/og.jpg",
            status = "published",
            publishedAt = "2024-01-15T10:00:00.000Z",
            createdAt = "2024-01-15T09:00:00.000Z",
            updatedAt = "2024-01-15T10:00:00.000Z",
            isLocal = false
        )
        // All fields needed by ComposerViewModel.loadForEdit
        assertEquals("Full content here", post.textContent)
        assertEquals("https://example.com/image.jpg", post.imageUrl)
        assertEquals("https://example.com", post.linkUrl)
        assertEquals("Example", post.linkTitle)
        assertEquals("An example link", post.linkDescription)
        assertEquals("https://example.com/og.jpg", post.linkImageUrl)
        assertEquals("2024-01-15T10:00:00.000Z", post.updatedAt)
    }

    @Test
    fun `post with empty content has minimal fields for edit`() {
        val post = createLocalPost(content = "")
        assertEquals("", post.textContent)
    }

    // --- Multiple deletes ---

    @Test
    fun `multiple sequential deletes reduce post count correctly`() {
        var posts = (1L..5L).map { createLocalPost(localId = it, content = "Post $it") }
        assertEquals(5, posts.size)

        posts = posts.filter { it.localId != 3L }
        assertEquals(4, posts.size)

        posts = posts.filter { it.localId != 1L }
        assertEquals(3, posts.size)

        val remaining = posts.map { it.localId }
        assertTrue(2L in remaining)
        assertTrue(4L in remaining)
        assertTrue(5L in remaining)
    }
}
