package com.example.shiftalarm

import java.time.DayOfWeek
import java.time.LocalDate
import java.time.ZoneId
import android.icu.util.ChineseCalendar
import android.os.Build

/**
 * 대한민국 공휴일 관리자 + 물때(Tide) 계산기.
 * - 양력 고정 공휴일
 * - 음력 공휴일 (ICU ChineseCalendar)
 * - 대체공휴일
 * - 물때 (7물때식: 서해안/남해서부 기준)
 */
object HolidayManager {

    private val cache = mutableMapOf<Int, Map<LocalDate, String>>()

    fun getHolidayName(date: LocalDate): String? {
        return getHolidaysForYear(date.year)[date]
    }

    fun isHoliday(date: LocalDate): Boolean {
        return getHolidaysForYear(date.year).containsKey(date)
    }

    fun getHolidaysForYear(year: Int): Map<LocalDate, String> {
        return cache.getOrPut(year) { generateHolidays(year) }
    }

    private fun generateHolidays(year: Int): Map<LocalDate, String> {
        val holidays = mutableMapOf<LocalDate, String>()
        addFixedHolidays(year, holidays)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            addLunarHolidays(year, holidays)
        }
        addSubstituteHolidays(holidays)
        return holidays
    }

    // ── 양력 고정 공휴일 ──
    private fun addFixedHolidays(year: Int, h: MutableMap<LocalDate, String>) {
        h[LocalDate.of(year, 1, 1)] = "신정"
        h[LocalDate.of(year, 3, 1)] = "삼일절"
        h[LocalDate.of(year, 5, 5)] = "어린이날"
        h[LocalDate.of(year, 6, 6)] = "현충일"
        h[LocalDate.of(year, 8, 15)] = "광복절"
        h[LocalDate.of(year, 10, 3)] = "개천절"
        h[LocalDate.of(year, 10, 9)] = "한글날"
        h[LocalDate.of(year, 12, 25)] = "성탄절"
    }

    // ── 음력 공휴일 ──
    private fun addLunarHolidays(year: Int, h: MutableMap<LocalDate, String>) {
        lunarToSolar(year, 1, 1)?.let { seolnal ->
            h[seolnal.minusDays(1)] = "설날 연휴"
            h[seolnal] = "설날"
            h[seolnal.plusDays(1)] = "설날 연휴"
        }
        lunarToSolar(year, 4, 8)?.let { buddha ->
            h[buddha] = "부처님오신날"
        }
        lunarToSolar(year, 8, 15)?.let { chuseok ->
            h[chuseok.minusDays(1)] = "추석 연휴"
            h[chuseok] = "추석"
            h[chuseok.plusDays(1)] = "추석 연휴"
        }
    }

    // ── 대체공휴일 (대한민국 관공서의 공휴일에 관한 규정 개정안) ──
    // 1. 단일 공휴일 (토/일 겹침 시 대체): 삼일절, 어린이날, 부처님오신날, 광복절, 개천절, 한글날, 성탄절
    // 2. 연휴 공휴일 (일요일 또는 다른 공휴일 겹침 시 대체): 설날 연휴, 추석 연휴
    // * 현충일(6/6), 신정(1/1)은 법정 대체공휴일 제외
    private val SINGLE_SUBSTITUTE_ELIGIBLE = setOf(
        "삼일절", "어린이날", "부처님오신날", "광복절", "개천절", "한글날", "성탄절"
    )
    private val LUNAR_SERIES_ELIGIBLE = setOf(
        "설날", "설날 연휴", "추석", "추석 연휴"
    )

    private fun addSubstituteHolidays(holidays: MutableMap<LocalDate, String>) {
        val occupied = holidays.keys.toMutableSet()
        val substitutes = mutableListOf<Pair<LocalDate, String>>()

        for ((date, name) in holidays.entries.sortedBy { it.key }) {
            val dow = date.dayOfWeek
            val isSingleEligible = name in SINGLE_SUBSTITUTE_ELIGIBLE && (dow == DayOfWeek.SATURDAY || dow == DayOfWeek.SUNDAY)
            val isLunarSeriesEligible = name in LUNAR_SERIES_ELIGIBLE && (dow == DayOfWeek.SUNDAY)

            if (isSingleEligible || isLunarSeriesEligible) {
                var sub = date.plusDays(1)
                while (sub.dayOfWeek == DayOfWeek.SATURDAY ||
                    sub.dayOfWeek == DayOfWeek.SUNDAY ||
                    sub in occupied
                ) {
                    sub = sub.plusDays(1)
                }
                substitutes.add(sub to "대체공휴일($name)")
                occupied.add(sub)
            }
        }
        for ((d, n) in substitutes) holidays[d] = n
    }

    // ── 음력 → 양력 변환 (ICU ChineseCalendar) ──
    private fun lunarToSolar(gregorianYear: Int, lunarMonth: Int, lunarDay: Int): LocalDate? {
        try {
            val cc = ChineseCalendar()
            val cal = java.util.GregorianCalendar(gregorianYear, 6, 1)
            cc.timeInMillis = cal.timeInMillis
            val chineseYear = cc.get(ChineseCalendar.EXTENDED_YEAR)

            cc.set(ChineseCalendar.EXTENDED_YEAR, chineseYear)
            cc.set(ChineseCalendar.MONTH, lunarMonth - 1)
            cc.set(ChineseCalendar.DAY_OF_MONTH, lunarDay)
            cc.set(ChineseCalendar.IS_LEAP_MONTH, 0)

            val result = java.time.Instant.ofEpochMilli(cc.timeInMillis)
                .atZone(ZoneId.of("Asia/Seoul")).toLocalDate()

            if (result.year == gregorianYear) return result

            cc.set(ChineseCalendar.EXTENDED_YEAR, chineseYear + 1)
            cc.set(ChineseCalendar.MONTH, lunarMonth - 1)
            cc.set(ChineseCalendar.DAY_OF_MONTH, lunarDay)
            cc.set(ChineseCalendar.IS_LEAP_MONTH, 0)

            val result2 = java.time.Instant.ofEpochMilli(cc.timeInMillis)
                .atZone(ZoneId.of("Asia/Seoul")).toLocalDate()

            return if (result2.year == gregorianYear) result2 else result
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        }
    }

    // ── 음력 날짜 문자열 (달력 표시용) ──
    fun getLunarDateString(date: LocalDate): String {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            try {
                val cc = ChineseCalendar()
                cc.timeInMillis = date.atStartOfDay(ZoneId.of("Asia/Seoul"))
                    .toInstant().toEpochMilli()
                val m = cc.get(ChineseCalendar.MONTH) + 1
                val d = cc.get(ChineseCalendar.DAY_OF_MONTH)
                return "$m.$d"
            } catch (_: Exception) {}
        }
        return ""
    }
    
    // ── 물때 계산 (7물때 및 8물때 고도화) ──
    fun getTide(date: LocalDate, location: String = "군산"): String {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            try {
                val cc = ChineseCalendar()
                cc.timeInMillis = date.atStartOfDay(ZoneId.of("Asia/Seoul"))
                    .toInstant().toEpochMilli()
                val d = cc.get(ChineseCalendar.DAY_OF_MONTH)
                
                val is8Tide = location == "여수" // 여수 등 남해 일부는 8물때식 선호 경향

                return if (is8Tide) {
                    // 8물때식 (남해/동해 기준)
                    when(d) {
                        in 1..7 -> "${d + 7}물"
                        8 -> "조금"
                        9 -> "무시"
                        in 10..22 -> "${d - 9}물"
                        23 -> "사리"
                        24 -> "조금"
                        25 -> "무시"
                        in 26..30 -> "${d - 25}물"
                        else -> ""
                    }
                } else {
                    // 7물때식 (서해/남해서부 기준: 군산, 변산, 태안 등)
                    when(d) {
                        in 1..6 -> "${d + 6}물"
                        7 -> "13물"
                        8 -> "사리"
                        9 -> "조금"
                        10 -> "무시"
                        in 11..21 -> "${d - 10}물"
                        22 -> "12물"
                        23 -> "13물"
                        24 -> "사리"
                        25 -> "조금"
                        26 -> "무시"
                        in 27..30 -> "${d - 26}물"
                        else -> ""
                    }
                }
            } catch (_: Exception) {}
        }
        return ""
    }
}
