package ru.forbion.f7cloud.feature.tasks

import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import java.time.LocalDate
import java.time.format.DateTimeFormatter

/**
 * Семантика умных списков Задач по веб-Tasks Nextcloud (mail/070 п.2).
 * Опорная дата фиксирована — тесты детерминированы, от сети/Android не зависят.
 */
class TasksSmartListsTest {

    private val today: LocalDate = LocalDate.of(2026, 7, 12)

    private fun stamp(date: LocalDate?): String =
        date?.format(DateTimeFormatter.BASIC_ISO_DATE).orEmpty()

    private fun task(
        uid: String = "u",
        summary: String = "t",
        priority: Int = 0,
        status: String = "NEEDS-ACTION",
        percent: Int = 0,
        due: LocalDate? = null,
        start: LocalDate? = null,
    ) = TaskItem(
        uid = uid,
        href = "/$uid.ics",
        etag = "",
        summary = summary,
        status = status,
        due = stamp(due),
        dueRaw = stamp(due),
        priority = priority,
        percentComplete = percent,
        description = "",
        listHref = "/list/",
        listName = "Список",
        startRaw = stamp(start),
    )

    private fun matches(key: SmartListKey, task: TaskItem) =
        TasksSmartLists.matches(key, task, today)

    // --- Важное (priority 1..4, незавершённые) ---

    @Test
    fun important_highPriorityIncomplete_matches() {
        assertTrue(matches(SmartListKey.IMPORTANT, task(priority = 1)))
        assertTrue(matches(SmartListKey.IMPORTANT, task(priority = 4)))
    }

    @Test
    fun important_mediumLowOrNone_excluded() {
        assertFalse(matches(SmartListKey.IMPORTANT, task(priority = 0)))
        assertFalse(matches(SmartListKey.IMPORTANT, task(priority = 5)))
        assertFalse(matches(SmartListKey.IMPORTANT, task(priority = 9)))
    }

    @Test
    fun important_completedHighPriority_excluded() {
        assertFalse(matches(SmartListKey.IMPORTANT, task(priority = 1, status = "COMPLETED")))
    }

    // --- Сегодня (start|due <= today, есть дата, незавершённые) ---

    @Test
    fun today_dueTodayOrOverdue_matches() {
        assertTrue(matches(SmartListKey.TODAY, task(due = today)))
        assertTrue(matches(SmartListKey.TODAY, task(due = today.minusDays(1))))
    }

    @Test
    fun today_dueFuture_excluded() {
        assertFalse(matches(SmartListKey.TODAY, task(due = today.plusDays(1))))
    }

    @Test
    fun today_startTodayNoDue_matches() {
        assertTrue(matches(SmartListKey.TODAY, task(start = today)))
    }

    @Test
    fun today_noDates_excluded() {
        assertFalse(matches(SmartListKey.TODAY, task()))
    }

    @Test
    fun today_completedDueToday_excluded() {
        assertFalse(matches(SmartListKey.TODAY, task(due = today, status = "COMPLETED")))
    }

    // --- На неделе (start|due <= today+6) ---

    @Test
    fun week_withinHorizon_matches() {
        assertTrue(matches(SmartListKey.WEEK, task(due = today.plusDays(6))))
        assertTrue(matches(SmartListKey.WEEK, task(due = today.minusDays(3))))
    }

    @Test
    fun week_beyondHorizon_excluded() {
        assertFalse(matches(SmartListKey.WEEK, task(due = today.plusDays(7))))
    }

    // --- Все (все незавершённые, включая будущий start/due) ---

    @Test
    fun all_incompleteRegardlessOfDates_matches() {
        assertTrue(matches(SmartListKey.ALL, task()))
        assertTrue(matches(SmartListKey.ALL, task(start = today.plusDays(30))))
        assertTrue(matches(SmartListKey.ALL, task(due = today.plusDays(30))))
    }

    @Test
    fun all_completed_excluded() {
        assertFalse(matches(SmartListKey.ALL, task(status = "COMPLETED")))
    }

    // --- Текущие (start не в будущем или без start; незавершённые) ---

    @Test
    fun current_noStart_matches() {
        assertTrue(matches(SmartListKey.CURRENT, task()))
    }

    @Test
    fun current_startPastOrToday_matches() {
        assertTrue(matches(SmartListKey.CURRENT, task(start = today)))
        assertTrue(matches(SmartListKey.CURRENT, task(start = today.minusDays(5))))
    }

    @Test
    fun current_futureStart_excluded() {
        assertFalse(matches(SmartListKey.CURRENT, task(start = today.plusDays(1))))
    }

    @Test
    fun current_vs_all_futureStartTask() {
        // Ключевое отличие: задача с будущим start есть в «Все», но нет в «Текущие».
        val future = task(start = today.plusDays(3))
        assertTrue(matches(SmartListKey.ALL, future))
        assertFalse(matches(SmartListKey.CURRENT, future))
    }

    // --- Завершенные ---

    @Test
    fun completed_byStatus_matches() {
        assertTrue(matches(SmartListKey.COMPLETED, task(status = "COMPLETED")))
    }

    @Test
    fun completed_byPercent100_matches() {
        assertTrue(matches(SmartListKey.COMPLETED, task(percent = 100)))
    }

    @Test
    fun completed_incomplete_excluded() {
        assertFalse(matches(SmartListKey.COMPLETED, task()))
    }

    // --- filter/count по набору ---

    @Test
    fun filterAndCount_overMixedSet() {
        // a: Важное+Сегодня+Неделя+Все+Текущие. b: Все+Текущие (будущий due, старт не в будущем).
        // c: только Все (будущий start исключает из Текущие). d: Завершенные.
        val tasks = listOf(
            task(uid = "a", priority = 2, due = today),
            task(uid = "b", due = today.plusDays(10)),
            task(uid = "c", start = today.plusDays(2)),
            task(uid = "d", status = "COMPLETED", priority = 1),
        )
        assertEquals(listOf("a"), TasksSmartLists.filter(SmartListKey.IMPORTANT, tasks, today).map { it.uid })
        assertEquals(listOf("a"), TasksSmartLists.filter(SmartListKey.TODAY, tasks, today).map { it.uid })
        assertEquals(3, TasksSmartLists.count(SmartListKey.ALL, tasks, today))
        assertEquals(listOf("a", "b"), TasksSmartLists.filter(SmartListKey.CURRENT, tasks, today).map { it.uid })
        assertEquals(1, TasksSmartLists.count(SmartListKey.COMPLETED, tasks, today))
    }

    // --- Видимость (3-state: Скрыта/Видима/Авто) ---

    @Test
    fun visibility_defaultModeIsAuto() {
        val v = SmartListVisibility.DEFAULT
        SmartListKey.entries.forEach { assertEquals(SmartListMode.AUTO, v.mode(it)) }
    }

    @Test
    fun visibility_setAndClearMode() {
        val v = SmartListVisibility.DEFAULT
            .withMode(SmartListKey.COMPLETED, SmartListMode.HIDDEN)
            .withMode(SmartListKey.ALL, SmartListMode.VISIBLE)
        assertEquals(SmartListMode.HIDDEN, v.mode(SmartListKey.COMPLETED))
        assertEquals(SmartListMode.VISIBLE, v.mode(SmartListKey.ALL))
        // Возврат в дефолт (AUTO) — режим не хранится (компактный персист).
        val cleared = v.withMode(SmartListKey.ALL, SmartListMode.AUTO)
        assertEquals(SmartListMode.AUTO, cleared.mode(SmartListKey.ALL))
        assertEquals(setOf(SmartListKey.COMPLETED), cleared.explicitModes().keys)
    }

    @Test
    fun visibleKeys_autoShowsOnlyNonEmpty() {
        val counts = mapOf(
            SmartListKey.IMPORTANT to 2,
            SmartListKey.TODAY to 0,
            SmartListKey.WEEK to 0,
            SmartListKey.ALL to 5,
            SmartListKey.CURRENT to 5,
            SmartListKey.COMPLETED to 0,
        )
        // Всё по умолчанию AUTO → показываем только непустые, в порядке enum.
        assertEquals(
            listOf(SmartListKey.IMPORTANT, SmartListKey.ALL, SmartListKey.CURRENT),
            TasksSmartLists.visibleKeys(SmartListVisibility.DEFAULT, counts),
        )
    }

    @Test
    fun visibleKeys_visibleAlwaysHiddenNever() {
        val counts = SmartListKey.entries.associateWith { 0 }
        val v = SmartListVisibility.DEFAULT
            .withMode(SmartListKey.ALL, SmartListMode.VISIBLE) // видима даже при 0
            .withMode(SmartListKey.CURRENT, SmartListMode.HIDDEN)
        val visible = TasksSmartLists.visibleKeys(v, counts)
        assertEquals(listOf(SmartListKey.ALL), visible) // остальные AUTO+0 скрыты, CURRENT HIDDEN
    }

    @Test
    fun counts_overMixedSet() {
        val tasks = listOf(
            task(uid = "a", priority = 2, due = today),
            task(uid = "b", status = "COMPLETED"),
            task(uid = "c"),
        )
        val counts = TasksSmartLists.counts(tasks, today)
        assertEquals(1, counts[SmartListKey.IMPORTANT])
        assertEquals(2, counts[SmartListKey.ALL]) // a, c незавершённые
        assertEquals(1, counts[SmartListKey.COMPLETED])
        assertEquals(2, counts[SmartListKey.CURRENT]) // a, c (без будущего старта)
    }
}
