package plus.rua.project

import kotlinx.datetime.LocalDate
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull

class ShiftPatternTest {

    private val anchor = LocalDate(2026, 5, 15)
    private val twoOnTwoOff = ShiftPattern(
        anchorDate = anchor,
        cycle = listOf(ShiftKind.WORK, ShiftKind.WORK, ShiftKind.OFF, ShiftKind.OFF)
    )

    // ---- kindAt: 锚点与同周期内 ----

    @Test
    fun kindAt_anchorDate_returnsFirstInCycle() {
        assertEquals(ShiftKind.WORK, twoOnTwoOff.kindAt(anchor))
    }

    @Test
    fun kindAt_oneAfterAnchor_returnsSecondInCycle() {
        assertEquals(ShiftKind.WORK, twoOnTwoOff.kindAt(LocalDate(2026, 5, 16)))
    }

    @Test
    fun kindAt_twoAfterAnchor_returnsThirdInCycle() {
        assertEquals(ShiftKind.OFF, twoOnTwoOff.kindAt(LocalDate(2026, 5, 17)))
    }

    @Test
    fun kindAt_threeAfterAnchor_returnsFourthInCycle() {
        assertEquals(ShiftKind.OFF, twoOnTwoOff.kindAt(LocalDate(2026, 5, 18)))
    }

    // ---- kindAt: 周期循环 ----

    @Test
    fun kindAt_fourAfterAnchor_wrapsToCycleStart() {
        // (5/19 - 5/15) % 4 = 0
        assertEquals(ShiftKind.WORK, twoOnTwoOff.kindAt(LocalDate(2026, 5, 19)))
    }

    @Test
    fun kindAt_eightAfterAnchor_wrapsTwice() {
        // (5/23 - 5/15) % 4 = 0
        assertEquals(ShiftKind.WORK, twoOnTwoOff.kindAt(LocalDate(2026, 5, 23)))
    }

    @Test
    fun kindAt_oneCycleLater_idx2_returnsOff() {
        // (5/21 - 5/15) % 4 = 2
        assertEquals(ShiftKind.OFF, twoOnTwoOff.kindAt(LocalDate(2026, 5, 21)))
    }

    @Test
    fun kindAt_manyCyclesLater_correctlyWraps() {
        // 100天后: (100) % 4 = 0
        assertEquals(ShiftKind.WORK, twoOnTwoOff.kindAt(LocalDate(2026, 8, 23)))
    }

    // ---- kindAt: 锚点之前的日期（负差值处理）----

    @Test
    fun kindAt_oneDayBeforeAnchor_returnsLastInCycle() {
        // -1 mod 4 = 3 -> OFF (cycle[3])
        assertEquals(ShiftKind.OFF, twoOnTwoOff.kindAt(LocalDate(2026, 5, 14)))
    }

    @Test
    fun kindAt_twoDaysBeforeAnchor_returnsThirdInCycle() {
        // -2 mod 4 = 2 -> OFF (cycle[2])
        assertEquals(ShiftKind.OFF, twoOnTwoOff.kindAt(LocalDate(2026, 5, 13)))
    }

    @Test
    fun kindAt_threeDaysBeforeAnchor_returnsSecondInCycle() {
        // -3 mod 4 = 1 -> WORK (cycle[1])
        assertEquals(ShiftKind.WORK, twoOnTwoOff.kindAt(LocalDate(2026, 5, 12)))
    }

    @Test
    fun kindAt_fourDaysBeforeAnchor_returnsFirstInCycle() {
        // -4 mod 4 = 0 -> WORK (cycle[0])
        assertEquals(ShiftKind.WORK, twoOnTwoOff.kindAt(LocalDate(2026, 5, 11)))
    }

    @Test
    fun kindAt_manyDaysBeforeAnchor_correctlyWraps() {
        // -100 mod 4 = 0 -> WORK
        assertEquals(ShiftKind.WORK, twoOnTwoOff.kindAt(LocalDate(2026, 2, 4)))
    }

    // ---- kindAt: 边界情况 ----

    @Test
    fun kindAt_emptyCycle_returnsNull() {
        val pattern = ShiftPattern(anchorDate = anchor, cycle = emptyList())
        assertNull(pattern.kindAt(anchor))
        assertNull(pattern.kindAt(LocalDate(2026, 5, 16)))
        assertNull(pattern.kindAt(LocalDate(2026, 5, 14)))
    }

    @Test
    fun kindAt_singleElementCycle_alwaysReturnsThatElement() {
        val pattern = ShiftPattern(anchorDate = anchor, cycle = listOf(ShiftKind.WORK))
        assertEquals(ShiftKind.WORK, pattern.kindAt(anchor))
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 20)))
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 1, 1)))
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2027, 12, 31)))
    }

    @Test
    fun kindAt_singleOffCycle_alwaysReturnsOff() {
        val pattern = ShiftPattern(anchorDate = anchor, cycle = listOf(ShiftKind.OFF))
        assertEquals(ShiftKind.OFF, pattern.kindAt(anchor))
        assertEquals(ShiftKind.OFF, pattern.kindAt(LocalDate(2030, 6, 15)))
    }

    // ---- kindAt: 多样化周期 ----

    @Test
    fun kindAt_threeOnOneOffCycle() {
        // 4 day cycle: WORK WORK WORK OFF
        val pattern = ShiftPattern(
            anchorDate = anchor,
            cycle = listOf(ShiftKind.WORK, ShiftKind.WORK, ShiftKind.WORK, ShiftKind.OFF)
        )
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 15))) // 0
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 16))) // 1
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 17))) // 2
        assertEquals(ShiftKind.OFF, pattern.kindAt(LocalDate(2026, 5, 18)))  // 3
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 19))) // 0
    }

    @Test
    fun kindAt_weekCycle_returnsCorrectDay() {
        // 7天周期：4天上班3天休息
        val pattern = ShiftPattern(
            anchorDate = anchor,
            cycle = listOf(
                ShiftKind.WORK, ShiftKind.WORK, ShiftKind.WORK, ShiftKind.WORK,
                ShiftKind.OFF, ShiftKind.OFF, ShiftKind.OFF
            )
        )
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 18))) // idx 3
        assertEquals(ShiftKind.OFF, pattern.kindAt(LocalDate(2026, 5, 19)))  // idx 4
        assertEquals(ShiftKind.OFF, pattern.kindAt(LocalDate(2026, 5, 21)))  // idx 6
        assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 22))) // idx 0 (next cycle)
    }

    // ---- ShiftPattern: 元数据 ----

    @Test
    fun shiftPattern_defaultNameIsChinese() {
        val pattern = ShiftPattern(anchorDate = anchor, cycle = listOf(ShiftKind.WORK))
        assertEquals("默认", pattern.name)
    }

    @Test
    fun shiftPattern_customNameIsPreserved() {
        val pattern = ShiftPattern(
            anchorDate = anchor,
            cycle = listOf(ShiftKind.WORK),
            name = "夜班"
        )
        assertEquals("夜班", pattern.name)
    }

    @Test
    fun shiftPattern_dataClassEquality() {
        val a = ShiftPattern(anchor, listOf(ShiftKind.WORK, ShiftKind.OFF))
        val b = ShiftPattern(anchor, listOf(ShiftKind.WORK, ShiftKind.OFF))
        assertEquals(a, b)
        assertEquals(a.hashCode(), b.hashCode())
    }
}
