package com.swoosh.microblog.data.model

import org.junit.Assert.*
import org.junit.Test

class PostStatsTest {

    // --- Word count tests ---

    @Test
    fun `countWords returns 0 for empty string`() {
        assertEquals(0, PostStats.countWords(""))
    }

    @Test
    fun `countWords returns 0 for whitespace only`() {
        assertEquals(0, PostStats.countWords("   "))
    }

    @Test
    fun `countWords returns 0 for tabs and newlines only`() {
        assertEquals(0, PostStats.countWords("\t\n\r\n"))
    }

    @Test
    fun `countWords counts single word`() {
        assertEquals(1, PostStats.countWords("hello"))
    }

    @Test
    fun `countWords counts multiple words`() {
        assertEquals(5, PostStats.countWords("the quick brown fox jumps"))
    }

    @Test
    fun `countWords handles multiple spaces between words`() {
        assertEquals(3, PostStats.countWords("hello    world    foo"))
    }

    @Test
    fun `countWords handles newlines between words`() {
        assertEquals(3, PostStats.countWords("hello\nworld\nfoo"))
    }

    @Test
    fun `countWords handles tabs between words`() {
        assertEquals(2, PostStats.countWords("hello\tworld"))
    }

    @Test
    fun `countWords handles mixed whitespace`() {
        assertEquals(4, PostStats.countWords("  hello\n\tworld   foo\nbar  "))
    }

    @Test
    fun `countWords handles leading and trailing whitespace`() {
        assertEquals(2, PostStats.countWords("  hello world  "))
    }

    @Test
    fun `countWords handles unicode characters`() {
        assertEquals(2, PostStats.countWords("caf\u00E9 latt\u00E9"))
    }

    @Test
    fun `countWords handles emoji as word`() {
        assertEquals(3, PostStats.countWords("hello \uD83D\uDE00 world"))
    }

    @Test
    fun `countWords handles punctuation attached to words`() {
        assertEquals(4, PostStats.countWords("hello, world! foo. bar?"))
    }

    @Test
    fun `countWords handles long text`() {
        val text = (1..200).joinToString(" ") { "word$it" }
        assertEquals(200, PostStats.countWords(text))
    }

    @Test
    fun `countWords handles single character`() {
        assertEquals(1, PostStats.countWords("a"))
    }

    @Test
    fun `countWords handles hyphenated words as single word`() {
        assertEquals(1, PostStats.countWords("well-known"))
    }

    // --- Reading time estimation tests ---

    @Test
    fun `estimateReadingTime returns 0 for 0 words`() {
        assertEquals(0, PostStats.estimateReadingTime(0))
    }

    @Test
    fun `estimateReadingTime returns 0 for negative words`() {
        assertEquals(0, PostStats.estimateReadingTime(-5))
    }

    @Test
    fun `estimateReadingTime returns 1 for small word count`() {
        assertEquals(1, PostStats.estimateReadingTime(50))
    }

    @Test
    fun `estimateReadingTime returns 1 for 199 words`() {
        assertEquals(1, PostStats.estimateReadingTime(199))
    }

    @Test
    fun `estimateReadingTime returns 1 for exactly 200 words`() {
        assertEquals(1, PostStats.estimateReadingTime(200))
    }

    @Test
    fun `estimateReadingTime returns 2 for 400 words`() {
        assertEquals(2, PostStats.estimateReadingTime(400))
    }

    @Test
    fun `estimateReadingTime returns 5 for 1000 words`() {
        assertEquals(5, PostStats.estimateReadingTime(1000))
    }

    @Test
    fun `estimateReadingTime returns 1 for 1 word`() {
        assertEquals(1, PostStats.estimateReadingTime(1))
    }

    // --- PostStats creation tests ---

    @Test
    fun `fromContent creates correct stats for normal text`() {
        val stats = PostStats.fromContent("hello world foo bar baz")
        assertEquals(5, stats.wordCount)
        assertEquals(23, stats.charCount)
        assertEquals(1, stats.readingTimeMinutes)
        assertFalse(stats.hasImage)
        assertFalse(stats.hasLink)
    }

    @Test
    fun `fromContent creates correct stats for empty text`() {
        val stats = PostStats.fromContent("")
        assertEquals(0, stats.wordCount)
        assertEquals(0, stats.charCount)
        assertEquals(0, stats.readingTimeMinutes)
    }

    @Test
    fun `fromContent sets hasImage correctly`() {
        val stats = PostStats.fromContent("hello", hasImage = true)
        assertTrue(stats.hasImage)
        assertFalse(stats.hasLink)
    }

    @Test
    fun `fromContent sets hasLink correctly`() {
        val stats = PostStats.fromContent("hello", hasLink = true)
        assertFalse(stats.hasImage)
        assertTrue(stats.hasLink)
    }

    @Test
    fun `fromContent sets both hasImage and hasLink`() {
        val stats = PostStats.fromContent("hello", hasImage = true, hasLink = true)
        assertTrue(stats.hasImage)
        assertTrue(stats.hasLink)
    }

    @Test
    fun `fromFeedPost calculates from text content`() {
        val post = FeedPost(
            title = "Test",
            textContent = "This is a test post with some words",
            htmlContent = null,
            imageUrl = "https://example.com/img.jpg",
            linkUrl = "https://example.com",
            linkTitle = "Example",
            linkDescription = null,
            linkImageUrl = null,
            status = "published",
            publishedAt = null,
            createdAt = null,
            updatedAt = null
        )
        val stats = PostStats.fromFeedPost(post)
        assertEquals(8, stats.wordCount)
        assertTrue(stats.hasImage)
        assertTrue(stats.hasLink)
    }

    @Test
    fun `fromFeedPost with no image and no link`() {
        val post = FeedPost(
            title = "Test",
            textContent = "Just text",
            htmlContent = null,
            imageUrl = null,
            linkUrl = null,
            linkTitle = null,
            linkDescription = null,
            linkImageUrl = null,
            status = "draft",
            publishedAt = null,
            createdAt = null,
            updatedAt = null
        )
        val stats = PostStats.fromFeedPost(post)
        assertEquals(2, stats.wordCount)
        assertFalse(stats.hasImage)
        assertFalse(stats.hasLink)
    }

    // --- Format reading time tests ---

    @Test
    fun `formatReadingTime returns empty string for 0 minutes`() {
        assertEquals("", PostStats.formatReadingTime(0))
    }

    @Test
    fun `formatReadingTime returns singular for 1 minute`() {
        assertEquals("1 min read", PostStats.formatReadingTime(1))
    }

    @Test
    fun `formatReadingTime returns plural for 2 minutes`() {
        assertEquals("2 min read", PostStats.formatReadingTime(2))
    }

    @Test
    fun `formatReadingTime handles large values`() {
        assertEquals("10 min read", PostStats.formatReadingTime(10))
    }

    @Test
    fun `formatReadingTime returns empty for negative`() {
        assertEquals("", PostStats.formatReadingTime(-1))
    }

    // --- formatComposerStats tests ---

    @Test
    fun `formatComposerStats for empty text`() {
        assertEquals("0 chars", PostStats.formatComposerStats(""))
    }

    @Test
    fun `formatComposerStats for single word`() {
        val result = PostStats.formatComposerStats("hello")
        assertTrue(result.contains("5 chars"))
        assertTrue(result.contains("1 words"))
        assertTrue(result.contains("~1 min read"))
    }

    @Test
    fun `formatComposerStats for multiple words`() {
        val result = PostStats.formatComposerStats("hello world foo")
        assertTrue(result.contains("15 chars"))
        assertTrue(result.contains("3 words"))
    }

    @Test
    fun `formatComposerStats uses dot separator`() {
        val result = PostStats.formatComposerStats("hello world")
        assertTrue(result.contains(" · "))
    }

    // --- Character count edge cases ---

    @Test
    fun `charCount for empty string is 0`() {
        val stats = PostStats.fromContent("")
        assertEquals(0, stats.charCount)
    }

    @Test
    fun `charCount for unicode string`() {
        val stats = PostStats.fromContent("cafe\u0301")
        // "cafe" + combining accent = 5 chars in Kotlin
        assertEquals(5, stats.charCount)
    }

    @Test
    fun `charCount for emoji string`() {
        val text = "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02"
        val stats = PostStats.fromContent(text)
        // Each emoji is 2 chars (surrogate pair) in Kotlin
        assertEquals(6, stats.charCount)
    }

    @Test
    fun `charCount for string with only spaces`() {
        val stats = PostStats.fromContent("     ")
        assertEquals(5, stats.charCount)
    }

    @Test
    fun `charCount for string with newlines`() {
        val stats = PostStats.fromContent("hello\nworld")
        assertEquals(11, stats.charCount)
    }

    @Test
    fun `charCount matches string length`() {
        val text = "A test string with 35 characters!!"
        val stats = PostStats.fromContent(text)
        assertEquals(text.length, stats.charCount)
    }

    // --- PostStats data class tests ---

    @Test
    fun `PostStats equality`() {
        val a = PostStats(10, 50, 1, false, false)
        val b = PostStats(10, 50, 1, false, false)
        assertEquals(a, b)
    }

    @Test
    fun `PostStats inequality`() {
        val a = PostStats(10, 50, 1, false, false)
        val b = PostStats(10, 50, 1, true, false)
        assertNotEquals(a, b)
    }

    @Test
    fun `PostStats copy`() {
        val original = PostStats(10, 50, 1, false, false)
        val copied = original.copy(hasImage = true)
        assertEquals(10, copied.wordCount)
        assertTrue(copied.hasImage)
    }
}
