
package com.example.shiftalarm

import org.junit.Test
import java.io.File
import java.io.FileOutputStream
import java.nio.charset.StandardCharsets
import java.time.LocalDate
import java.time.DayOfWeek
import java.time.temporal.ChronoUnit

class SimulationTest {

    // ==========================================
    // 1. Simulation Configuration
    // ==========================================
    val START_DATE = LocalDate.of(2026, 2, 1)
    val END_DATE = LocalDate.of(2026, 3, 31)

    // Holidays (Mock)
    val HOLIDAYS = setOf(
        LocalDate.of(2026, 2, 11), // Mock Holiday
        LocalDate.of(2026, 3, 1)   // Samiljeol
    )

    // ==========================================
    // 2. Logic (Duplicated for Safety in Unit Test)
    // ==========================================
    
    // Internal Logic Simulating ShiftCalculator
    object SimShiftCalculator {
        val JEONJU_CYCLE = listOf(
            "석간", "석간", "석간", "휴무", "휴무",
            "주간", "주간", "주간", "주간", "주간", "휴무", "휴무",
            "야간", "야간", "야간", "야간", "야간", "휴무",
            "석간", "석간"
        )
        val JEONJU_BASE_DATE = LocalDate.of(2026, 2, 1) // Verify base date usage in actual code
        
        val TEAM_OFFSETS = mapOf("A" to 0, "B" to 15, "C" to 10, "D" to 5)

        fun getJeonjuShift(date: LocalDate, team: String): String {
            val offset = TEAM_OFFSETS[team] ?: 0
            val days = ChronoUnit.DAYS.between(JEONJU_BASE_DATE, date).toInt()
            val index = Math.floorMod(days + offset, JEONJU_CYCLE.size)
            return JEONJU_CYCLE[index]
        }

        fun getNonsanShift(date: LocalDate, team: String): String {
            val dayOfWeek = date.dayOfWeek
            if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) return "휴무"

            val baseDate = LocalDate.of(2026, 2, 9)
            val alignedDate = date.minusDays((dayOfWeek.value - 1).toLong())
            val weeksPassed = ChronoUnit.WEEKS.between(baseDate, alignedDate).toInt()

            val rotation = listOf("주간", "야간", "석간")
            val startOffset = when (team) {
                "A" -> 0; "B" -> 1; "C" -> 2; else -> 0
            }
            val index = Math.floorMod(startOffset + weeksPassed, 3)
            return rotation[index]
        }
    }

    // Internal Logic Simulating ShiftAlarmDefaults values
    fun getDefaultAlarmTime(shift: String, factory: String): String? {
        if (!listOf("주간", "석간", "야간", "야간 맞교대", "주간 맞교대").contains(shift)) return null
        
        return if (factory == "Nonsan") {
            when (shift) {
                "주간" -> "07:00"
                "석간" -> "15:00"
                "야간" -> "23:00"
                "야간 맞교대" -> "19:00"
                else -> null
            }
        } else {
             when (shift) {
                "주간" -> "06:00"
                "석간" -> "14:00"
                "야간" -> "22:00"
                "야간 맞교대" -> "18:00"
                "주간 맞교대" -> "06:00"
                else -> null
            }
        }
    }

    // ==========================================
    // 3. User Data Structures
    // ==========================================

    data class UserSettings(
        val factory: String,
        val team: String,
        val customShifts: Map<LocalDate, String> = emptyMap(),
        val customAlarmTimes: Map<LocalDate, String> = emptyMap(),
        val customShiftAlarmTimes: Map<String, String> = emptyMap(),
        val name: String
    )

    data class SimResult(
        val date: LocalDate,
        val shift: String,
        val alarmTime: String?,
        val isHoliday: Boolean,
        val notes: String
    )

    // ==========================================
    // 4. Engine
    // ==========================================

    fun runSimulation(user: UserSettings): List<SimResult> {
        val results = mutableListOf<SimResult>()
        var currentDate = START_DATE
        while (!currentDate.isAfter(END_DATE)) {
            var shift = if (user.factory == "Nonsan") {
                SimShiftCalculator.getNonsanShift(currentDate, user.team)
            } else {
                SimShiftCalculator.getJeonjuShift(currentDate, user.team)
            }
            
            var notes = ""
            if (user.customShifts.containsKey(currentDate)) {
                shift = user.customShifts[currentDate]!!
                notes += "[Manual Override] "
            }

            var alarmTime: String? = null
            if (shift == "휴무" || shift == "비번") {
                alarmTime = null 
            } else {
                alarmTime = getDefaultAlarmTime(shift, user.factory)
                
                if (user.customShiftAlarmTimes.containsKey(shift)) {
                    alarmTime = user.customShiftAlarmTimes[shift]
                    notes += "[Shift Rule] "
                }
                
                if (user.customAlarmTimes.containsKey(currentDate)) {
                    alarmTime = user.customAlarmTimes[currentDate]
                    notes += "[Date Rule] "
                }
            }
            
            val isHoliday = HOLIDAYS.contains(currentDate)
            if (isHoliday) notes += "HOLIDAY "

            results.add(SimResult(currentDate, shift, alarmTime, isHoliday, notes))
            currentDate = currentDate.plusDays(1)
        }
        return results
    }

    // ==========================================
    // 5. Reporting
    // ==========================================

    fun generateReport(user: UserSettings, results: List<SimResult>): String {
        val sb = StringBuilder()
        sb.append("\n### Simulation Report: ${user.name} (${user.factory} - Team ${user.team})\n")
        sb.append("| Date | Day | Shift | Alarm Time | Status | Logic Check |\n")
        sb.append("|---|---|---|---|---|---|\n")
        
        val dayNames = listOf("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") 
        
        for (r in results) {
            val dayOfW = r.date.dayOfWeek.value 
            // 1=Mon...7=Sun.
            // list index 0=Sun, 6=Sat.
            // 1 % 7 = 1 (Mon). 7 % 7 = 0 (Sun). Correct.
            val dayStr = dayNames[dayOfW % 7]
            
            val alarmStr = r.alarmTime ?: "-"
            val status = if (r.alarmTime != null) "ON" else "OFF"
            
            var check = "OK"
            if (r.shift == "주간" && user.factory == "Jeonju" && r.alarmTime == "06:00") check = "Valid (Default)"
            if (r.shift == "주간" && user.factory == "Nonsan" && r.alarmTime == "07:00") check = "Valid (Nonsan)"
            
            sb.append("| ${r.date} | $dayStr | ${r.shift} | $alarmStr | $status | $check ${r.notes} |\n")
        }
        return sb.toString()
    }

    @Test
    fun runFullSimulation() {
        val outputFile = File("simulation_result.md")
        // Use UTF-8 explicitly
        val writer = outputFile.bufferedWriter(StandardCharsets.UTF_8)
        
        writer.write("# ShiftRing Alarm Logic Simulation V2 (Robust)\n")
        writer.write("Period: $START_DATE ~ $END_DATE\n")
        
        // Scenario A: Jeonju C
        val userJeonju = UserSettings("Jeonju", "C", name = "User 1 (Jeonju-C Standard)")
        val resA = runSimulation(userJeonju)
        val reportA = generateReport(userJeonju, resA)
        writer.write(reportA)
        println(reportA)

        // Scenario B: Nonsan A
        val userNonsan = UserSettings("Nonsan", "A", name = "User 2 (Nonsan-A Standard)")
        val resB = runSimulation(userNonsan)
        val reportB = generateReport(userNonsan, resB)
        writer.write(reportB)
        println(reportB)
        
        // Scenario C: Custom
        val userCustom = UserSettings(
            factory = "Jeonju",
            team = "A",
            customShifts = mapOf(
                LocalDate.of(2026, 2, 14) to "휴무", 
                LocalDate.of(2026, 2, 15) to "주간" 
            ),
            customAlarmTimes = mapOf(
                 LocalDate.of(2026, 2, 20) to "05:00"
            ),
            name = "User 3 (Jeonju-A Customizer)"
        )
        val resC = runSimulation(userCustom)
        val reportC = generateReport(userCustom, resC)
        writer.write(reportC)
        println(reportC)
        
        writer.close()
    }
}
