package com.tt.honeyDue.testing

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

/**
 * Guarantees the fixtures consumed by the parity-gallery render deterministic,
 * self-consistent data on every run. These tests do not invoke Compose — they
 * assert structural invariants the snapshot tests rely on (unique ids,
 * non-empty lookups in empty state, reachable references between tasks and
 * residences, etc.).
 *
 * Failures here surface BEFORE the snapshot suite tries to record goldens
 * against inconsistent data — keeps the parity gallery honest.
 */
class FixtureDataManagerTest {

    // ==================== EMPTY STATE ====================

    @Test
    fun empty_hasNoResidences() {
        val dm = FixtureDataManager.empty()
        assertTrue(dm.residences.value.isEmpty(), "empty() must have no residences")
        assertNull(dm.myResidences.value, "empty() must have null myResidencesResponse")
    }

    @Test
    fun empty_hasNoTasksContractorsDocuments() {
        val dm = FixtureDataManager.empty()
        assertNull(dm.allTasks.value, "empty() must have null task kanban")
        assertTrue(dm.contractors.value.isEmpty(), "empty() must have no contractors")
        assertTrue(dm.documents.value.isEmpty(), "empty() must have no documents")
    }

    @Test
    fun empty_retainsLookupsForPickers() {
        // Even in empty state, form pickers need lookup data — otherwise the
        // "add first task" / "add first contractor" flows can't render dropdowns.
        val dm = FixtureDataManager.empty()
        assertTrue(dm.taskCategories.value.isNotEmpty(), "taskCategories must be populated in empty()")
        assertTrue(dm.taskPriorities.value.isNotEmpty(), "taskPriorities must be populated in empty()")
        assertTrue(dm.taskFrequencies.value.isNotEmpty(), "taskFrequencies must be populated in empty()")
        assertTrue(dm.residenceTypes.value.isNotEmpty(), "residenceTypes must be populated in empty()")
        assertTrue(dm.contractorSpecialties.value.isNotEmpty(), "contractorSpecialties must be populated in empty()")
    }

    @Test
    fun empty_providesFreeTierSubscription() {
        val dm = FixtureDataManager.empty()
        val sub = dm.subscription.value
        assertNotNull(sub, "empty() should still have a free-tier SubscriptionStatus")
        assertEquals("free", sub.tier)
    }

    // ==================== POPULATED STATE ====================

    @Test
    fun populated_hasExpectedResidenceCounts() {
        val dm = FixtureDataManager.populated()
        assertEquals(2, dm.residences.value.size, "populated() must have 2 residences")
        val myRes = dm.myResidences.value
        assertNotNull(myRes)
        assertEquals(2, myRes.residences.size)
    }

    @Test
    fun populated_hasKanbanBoardAndEightTasks() {
        val dm = FixtureDataManager.populated()
        val kanban = dm.allTasks.value
        assertNotNull(kanban, "populated() must have a kanban board")
        val allTasks = kanban.columns.flatMap { it.tasks }
        assertEquals(8, allTasks.size, "populated() must surface exactly 8 tasks across columns")
    }

    @Test
    fun populated_tasksAreDistributedAcrossExpectedColumns() {
        val dm = FixtureDataManager.populated()
        val kanban = dm.allTasks.value
        assertNotNull(kanban)
        val byColumn = kanban.columns.associate { it.name to it.count }
        // 2 overdue + 3 due-soon + 2 upcoming + 1 completed = 8
        assertEquals(2, byColumn["overdue_tasks"], "expected 2 overdue tasks")
        assertEquals(3, byColumn["due_soon_tasks"], "expected 3 due-soon tasks")
        assertEquals(2, byColumn["upcoming_tasks"], "expected 2 upcoming tasks")
        assertEquals(1, byColumn["completed_tasks"], "expected 1 completed task")
    }

    @Test
    fun populated_hasExpectedContractorsAndDocuments() {
        val dm = FixtureDataManager.populated()
        assertEquals(3, dm.contractors.value.size, "populated() must have 3 contractors")
        assertEquals(5, dm.documents.value.size, "populated() must have 5 documents")
    }

    @Test
    fun populated_providesPremiumSubscriptionAndUser() {
        val dm = FixtureDataManager.populated()
        val sub = dm.subscription.value
        assertNotNull(sub)
        assertEquals("pro", sub.tier, "populated() should render on the premium tier")
        val user = dm.currentUser.value
        assertNotNull(user, "populated() must have a current user for profile screens")
    }

    // ==================== STRUCTURAL INVARIANTS ====================

    @Test
    fun populatedIdsAreUniqueAcrossCollections() {
        val dm = FixtureDataManager.populated()
        val residenceIds = dm.residences.value.map { it.id }
        assertEquals(residenceIds.size, residenceIds.toSet().size, "residence ids must be unique")

        val allTasks = dm.allTasks.value?.columns?.flatMap { it.tasks }.orEmpty()
        val taskIds = allTasks.map { it.id }
        assertEquals(taskIds.size, taskIds.toSet().size, "task ids must be unique")

        val contractorIds = dm.contractors.value.map { it.id }
        assertEquals(contractorIds.size, contractorIds.toSet().size, "contractor ids must be unique")

        val documentIds = dm.documents.value.mapNotNull { it.id }
        assertEquals(documentIds.size, documentIds.toSet().size, "document ids must be unique")
    }

    @Test
    fun populatedTasksReferenceExistingResidences() {
        val dm = FixtureDataManager.populated()
        val residenceIds = dm.residences.value.map { it.id }.toSet()
        val allTasks = dm.allTasks.value?.columns?.flatMap { it.tasks }.orEmpty()
        allTasks.forEach { task ->
            assertTrue(
                residenceIds.contains(task.residenceId),
                "task id=${task.id} references residence id=${task.residenceId} which isn't in the fixture residence list",
            )
        }
    }

    @Test
    fun populatedDocumentsReferenceExistingResidences() {
        val dm = FixtureDataManager.populated()
        val residenceIds = dm.residences.value.map { it.id }.toSet()
        dm.documents.value.forEach { doc ->
            val rid = doc.residenceId ?: doc.residence
            assertTrue(
                residenceIds.contains(rid),
                "document id=${doc.id} references residence id=$rid which isn't in the fixture residence list",
            )
        }
    }

    @Test
    fun populatedLookupHelpersResolveIds() {
        // InMemoryDataManager.getTaskPriority/Category/Frequency must resolve
        // ids that appear on populated() tasks — otherwise task cards render
        // with null categories/priorities, which breaks the parity gallery.
        val dm = FixtureDataManager.populated()
        val allTasks = dm.allTasks.value?.columns?.flatMap { it.tasks }.orEmpty()
        allTasks.forEach { task ->
            task.categoryId?.let {
                assertNotNull(dm.getTaskCategory(it), "category id=$it must resolve on populated()")
            }
            task.priorityId?.let {
                assertNotNull(dm.getTaskPriority(it), "priority id=$it must resolve on populated()")
            }
            task.frequencyId?.let {
                assertNotNull(dm.getTaskFrequency(it), "frequency id=$it must resolve on populated()")
            }
        }
    }

    @Test
    fun populatedTotalSummaryMatchesTaskDistribution() {
        val dm = FixtureDataManager.populated()
        val summary = dm.totalSummary.value
        assertNotNull(summary)
        assertEquals(2, summary.totalResidences)
        assertEquals(8, summary.totalTasks)
        // 8 total - 1 completed = 7 pending (overdue + due_soon + upcoming)
        assertEquals(7, summary.totalPending)
        assertEquals(2, summary.totalOverdue)
    }

    @Test
    fun fixturesAreDeterministic() {
        // Two calls to populated() should produce identical data.
        val a = FixtureDataManager.populated()
        val b = FixtureDataManager.populated()
        assertEquals(a.residences.value.map { it.id }, b.residences.value.map { it.id })
        assertEquals(
            a.allTasks.value?.columns?.flatMap { it.tasks }?.map { it.id },
            b.allTasks.value?.columns?.flatMap { it.tasks }?.map { it.id },
        )
    }

    @Test
    fun fixedDateIsStable() {
        // The parity gallery's determinism depends on a fixed clock. If a
        // refactor accidentally swaps Fixtures.FIXED_DATE for Clock.System.now(),
        // snapshot tests will go red every day — catch it here first.
        assertEquals("2026-04-15", Fixtures.FIXED_DATE.toString())
    }
}
