package com.adealink.weparty.commonui.widget.wheel

import android.os.Bundle
import com.adealink.frame.aab.util.getCompatString
import com.adealink.frame.mvvm.view.viewBinding
import com.adealink.frame.util.getBirthStr
import com.adealink.frame.util.timeToYMDHM
import com.adealink.weparty.R
import com.adealink.weparty.commonui.BaseFragment
import com.adealink.weparty.commonui.MIN_YEAR
import com.adealink.weparty.commonui.dialogfragment.data.DatePickerType
import com.adealink.weparty.commonui.dialogfragment.data.Month
import com.adealink.weparty.commonui.ext.show
import com.adealink.weparty.databinding.FragmentWheelDatePickerBinding
import java.util.Calendar
import java.util.Date

/**
 * 日期选择控件；
 */
class WheelDatePickerFragment : BaseFragment(R.layout.fragment_wheel_date_picker) {

    companion object {
        const val EXTRA_INIT_DATE = "extra_init_date"
        const val EXTRA_TARGET_MONTH_SCOPE = "extra_target_month_scope"
        const val EXTRA_TARGET_DAY_SCOPE = "extra_target_day_scope"
        const val EXTRA_CURR_MINUTE_SCOPE = "extra_curr_minute_scope"
        const val EXTRA_TYPE = "extra_type"

        fun newInstance(
            timestamp: Long = System.currentTimeMillis(),
            tips: String? = null,
            targetMonthScope: Int? = -2,
            targetDayScope: Int? = 0,
            currMinuteScope: Int? = 0,
            type: Int? = DatePickerType.YMD.ordinal,
        ): WheelDatePickerFragment {
            val fragment = WheelDatePickerFragment()
            val bundle = Bundle()
            if (targetMonthScope != null) {
                bundle.putInt(EXTRA_TARGET_MONTH_SCOPE, targetMonthScope)
            }
            if (targetDayScope != null) {
                bundle.putInt(EXTRA_TARGET_DAY_SCOPE, targetDayScope)
            }
            if (currMinuteScope != null) {
                bundle.putInt(EXTRA_CURR_MINUTE_SCOPE, currMinuteScope)
            }
            bundle.putLong(EXTRA_INIT_DATE, timestamp)
            if (type != null) {
                bundle.putInt(EXTRA_TYPE, type)
            }

            fragment.arguments = bundle
            return fragment
        }
    }

    private val binding by viewBinding(FragmentWheelDatePickerBinding::bind)

    private val yearList = mutableListOf<String>()
    private val monthList =
        mutableListOf<Pair<Int, String>>()  //first用于索引及获取相应时间戳，second用于日期控件滚轮文案显示
    private val dayList = mutableListOf<String>()
    private var hourList = arrayListOf<String>()
    private var minuteList = arrayListOf<String>()

    private var initTimestamp = System.currentTimeMillis()
    private var targetMonthScope: Int = -2   //-N选取前N个月  N选取后N个月
    private var targetDayScope: Int = 0   //-N选取前N天  N选取后N天
    private var currMinuteScope: Int = 0   //选取开始的小时数，1表示选取当前时间一小时

    private var selectYear = 0
    private var selectMonth = 0
    private var selectDay = 0
    private var selectHour = 0
    private var selectMinute = 0

    private var currYear = 0
    private var targetYear = MIN_YEAR
    private var currMonth = 1
    private var targetMonth = 1
    private var currDay = 1
    private var targetDay = 1
    private var targetHour = 1
    private var targetMinute = 1
    private var currHour = 1
    private var currMinute = 1
    private var type: Int = DatePickerType.YMD.ordinal

    private fun parseIntent() {
        arguments?.getLong(EXTRA_INIT_DATE)?.let {
            initTimestamp = it
        }
        arguments?.getInt(EXTRA_TARGET_MONTH_SCOPE)?.let {
            targetMonthScope = it
        }
        arguments?.getInt(EXTRA_TARGET_DAY_SCOPE)?.let {
            targetDayScope = it
        }
        arguments?.getInt(EXTRA_CURR_MINUTE_SCOPE)?.let {
            currMinuteScope = it
        }
        arguments?.getInt(EXTRA_TYPE)?.let {
            type = it
        }
        if (initTimestamp <= 0) {
            initTimestamp = System.currentTimeMillis()
        }
    }

    /**
     * - get(Calendar.MONTH) 比真实的月份少1；
     */
    private fun initDataPicker() {
        val cal = Calendar.getInstance()
        // 当前的时间
        if (currMinuteScope != 0) {
            cal.add(Calendar.MINUTE, currMinuteScope)
        }
        currYear = cal.get(Calendar.YEAR)
        currMonth = cal.get(Calendar.MONTH) + 1
        currDay = cal.get(Calendar.DAY_OF_MONTH)
        currHour = cal.get(Calendar.HOUR_OF_DAY)
        currMinute = cal.get(Calendar.MINUTE)
        val currTimestamp = cal.timeInMillis
        // 选择月份或日
        if (targetDayScope != 0) {
            cal.add(Calendar.DATE, targetDayScope)
        } else {
            cal.add(Calendar.MONTH, targetMonthScope)
        }
//        targetYear = cal.get(Calendar.YEAR)
        targetMonth = cal.get(Calendar.MONTH) + 1
        targetDay = cal.get(Calendar.DAY_OF_MONTH)
        targetHour = cal.get(Calendar.HOUR_OF_DAY)
        targetMinute = cal.get(Calendar.MINUTE)
        val targetTimestamp = cal.timeInMillis
        //防越界
        if (targetTimestamp > currTimestamp) {
            if (currTimestamp > initTimestamp || initTimestamp > targetTimestamp) {
                initTimestamp = currTimestamp
            }
        } else {
            if (currTimestamp < initTimestamp || initTimestamp < targetTimestamp) {
                initTimestamp = currTimestamp
            }
        }
        // 初始化选中的时间
        val date = Date(initTimestamp)
        cal.time = date
        selectYear = cal.get(Calendar.YEAR)
        selectMonth = cal.get(Calendar.MONTH) + 1
        selectDay = cal.get(Calendar.DAY_OF_MONTH)
        selectHour = cal.get(Calendar.HOUR_OF_DAY)
        selectMinute = cal.get(Calendar.MINUTE)
        // 填充列表
        if (targetYear < currYear) {
            for (year in targetYear..currYear) {
                yearList.add("$year")
            }
        } else {
            for (year in currYear..targetYear) {
                yearList.add("$year")
            }
        }
    }

    private fun inflateMonthList(month: Int) {
        when (month) {
            Month.JANUARY.value -> monthList.add(
                Pair(
                    1,
                    getCompatString(R.string.commonui_january)
                )
            )

            Month.FEBRUARY.value -> monthList.add(
                Pair(
                    2,
                    getCompatString(R.string.commonui_february)
                )
            )

            Month.MARCH.value -> monthList.add(Pair(3, getCompatString(R.string.commonui_march)))
            Month.APRIL.value -> monthList.add(Pair(4, getCompatString(R.string.commonui_april)))
            Month.MAY.value -> monthList.add(Pair(5, getCompatString(R.string.commonui_may)))
            Month.JUNE.value -> monthList.add(Pair(6, getCompatString(R.string.commonui_june)))
            Month.JULY.value -> monthList.add(Pair(7, getCompatString(R.string.commonui_july)))
            Month.AUGUST.value -> monthList.add(Pair(8, getCompatString(R.string.commonui_august)))
            Month.SEPTEMBER.value -> monthList.add(
                Pair(
                    9,
                    getCompatString(R.string.commonui_september)
                )
            )

            Month.OCTOBER.value -> monthList.add(
                Pair(
                    10,
                    getCompatString(R.string.commonui_october)
                )
            )

            Month.NOVEMBER.value -> monthList.add(
                Pair(
                    11,
                    getCompatString(R.string.commonui_november)
                )
            )

            else -> monthList.add(Pair(12, getCompatString(R.string.commonui_december)))
        }
    }

    override fun initViews() {
        parseIntent()
        initDataPicker()
        binding.wheelYear.setEntries(yearList)
        updateMonthList()

        // 设置选中x
        binding.wheelYear.currentIndex = selectYear - targetYear
        binding.wheelMonth.currentIndex = monthList.indexOfFirst { it.first == selectMonth }
        binding.wheelDay.currentIndex = dayList.indexOf(selectDay.toString())
        binding.wheelHour.currentIndex = hourList.indexOf(selectHour.toString())
        binding.wheelMinute.currentIndex = minuteList.indexOf(
            if (selectMinute < 10) {
                "0$selectMinute"
            } else {
                selectMinute.toString()
            }
        )
        when (type) {
            DatePickerType.YMDHM.ordinal -> {  //精确到分钟
                binding.wheelDay.setItemWidth(100)
                binding.wheelMonth.setItemWidth(200)
                binding.wheelYear.setItemWidth(200)
                binding.wheelHour.show()
                binding.wheelMinute.show()
                binding.wheelHour.setOnWheelChangedListener { _, _, newIndex ->
                    selectHour = hourList.getOrNull(newIndex)?.toInt() ?: 0
                    updateMinuteList()
                    selectMinute = minuteList.getOrNull(0)?.toInt() ?: 0
                }
                binding.wheelMinute.setOnWheelChangedListener { _, _, newIndex ->
                    selectMinute = minuteList.getOrNull(newIndex)?.toInt() ?: 0
                }
            }

            else -> {}
        }
        binding.wheelYear.setOnWheelChangedListener { _, _, newIndex ->
            selectYear = yearList.getOrNull(newIndex)?.toInt() ?: 0
            updateMonthList()
            selectMonth = monthList.getOrNull(0)?.first ?: 0
            updateDayList()
            selectDay = dayList.getOrNull(0)?.toInt() ?: 0
            selectHour = hourList.getOrNull(0)?.toInt() ?: 0
            selectMinute = minuteList.getOrNull(0)?.toInt() ?: 0
        }
        binding.wheelMonth.setOnWheelChangedListener { _, _, newIndex ->
            selectMonth = monthList.getOrNull(newIndex)?.first ?: 0
            updateDayList()
            selectDay = dayList.getOrNull(0)?.toInt() ?: 0
            selectHour = hourList.getOrNull(0)?.toInt() ?: 0
            selectMinute = minuteList.getOrNull(0)?.toInt() ?: 0
        }
        binding.wheelDay.setOnWheelChangedListener { _, _, newIndex ->
            selectDay = dayList.getOrNull(newIndex)?.toInt() ?: 0
            updateHourList()
            selectHour = hourList.getOrNull(0)?.toInt() ?: 0
            selectMinute = minuteList.getOrNull(0)?.toInt() ?: 0
        }
    }

    fun getSelectedDate(callback: (timestamp: Long, timeStr: String) -> Unit) {
        val calendar = Calendar.getInstance()
        calendar.clear()
        if (type == DatePickerType.YMDHM.ordinal) {
            calendar.set(selectYear, selectMonth - 1, selectDay, selectHour, selectMinute)
        } else {
            calendar.set(selectYear, selectMonth - 1, selectDay)
        }
        val timestamp = calendar.timeInMillis
        callback.invoke(
            timestamp,
            if (type == DatePickerType.YMDHM.ordinal) {
                timeToYMDHM(timestamp)
            } else {
                getBirthStr(timestamp)
            }
        )
    }


    /**
     * 更新可选择的月份；
     * 1. 只有1年：直接从小到大填充月份；注意目标月份是否大于当前月份
     * 2. 跨年：[targetMonth, 12] 、[1, currMonth]
     */
    private fun updateMonthList() {
        monthList.clear()
        val startMonth = if (yearList.size < 2) {
            if (targetMonth > currMonth) currMonth else targetMonth
        } else {
            if (currYear < targetYear) {
                if (selectYear.toString() == yearList.getOrNull(0)) currMonth else 1
            } else {
                if (selectYear.toString() == yearList.getOrNull(0)) targetMonth else 1
            }

        }

        val endMonth = if (yearList.size < 2) {
            if (targetMonth > currMonth) targetMonth else currMonth
        } else {
            if (currYear < targetYear) {
                if (selectYear.toString() == yearList.getOrNull(0)) 12 else targetMonth
            } else {
                if (selectYear.toString() == yearList.getOrNull(0)) 12 else currMonth
            }

        }

        for (month in startMonth until endMonth + 1) {
            inflateMonthList(month)
        }
        binding.wheelMonth.setEntries(monthList.map { it.second })
        updateDayList()
    }

    /**
     * 更新可选择的日子；
     */
    private fun updateDayList() {
        val calendar = Calendar.getInstance()
        calendar[Calendar.YEAR] = selectYear
        calendar[Calendar.MONTH] = selectMonth - 1
        val days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH)
        dayList.clear()
        try {
            when (selectMonth) {
                targetMonth -> {
                    if (targetMonth <= currMonth) {
                        if (yearList.size < 2) {
                            if (targetDayScope != 0) {
                                for (i in currDay..targetDay) {
                                    dayList.add("$i")
                                }
                            } else {
                                for (i in targetDay..days) {
                                    dayList.add("$i")
                                }
                            }
                        } else {
                            if (targetDayScope != 0) {
                                for (i in 1..targetDay) {
                                    dayList.add("$i")
                                }
                            } else {
                                for (i in currDay..days) {
                                    dayList.add("$i")
                                }
                            }
                        }
                    } else {
                        if (targetYear > currYear) {
                            for (i in 1..targetDay) {
                                dayList.add("$i")
                            }
                        } else {
                            for (i in currDay..days) {
                                dayList.add("$i")
                            }
                        }

                    }
                }

                currMonth -> {
                    if (targetMonth <= currMonth) {
                        if (yearList.size < 2) {
                            for (i in 1..currDay) {
                                dayList.add("$i")
                            }
                        } else {
                            for (i in currDay..days) {
                                dayList.add("$i")
                            }
                        }
                    } else {
                        if (targetYear > currYear) {
                            for (i in currDay..days) {
                                dayList.add("$i")
                            }
                        } else {
                            for (i in 1..currDay) {
                                dayList.add("$i")
                            }
                        }
                    }

                }

                else -> {
                    for (i in 1..days) {
                        dayList.add("$i")
                    }
                }
            }
        } catch (e: Exception) {
            dayList.clear()
            for (i in 1..days) {
                dayList.add("$i")
            }
        }

        binding.wheelDay.setEntries(dayList)
        updateHourList()
    }

    private fun updateHourList() {
        hourList.clear()

        val startHour =
            if (selectDay == currDay && selectMonth == currMonth) currHour
            else if (selectDay == targetDay && selectMonth == targetMonth) targetHour
            else 0
        if (selectDay == targetDay && selectMonth == targetMonth) {
            for (i in 0..startHour) {
                hourList.add("$i")
            }
        } else {
            for (i in startHour..23) {
                hourList.add("$i")
            }
        }

        binding.wheelHour.setEntries(hourList)
        updateMinuteList()
    }

    private fun updateMinuteList() {
        minuteList.clear()
        val startMinute =
            if (selectHour == currHour && selectDay == currDay && selectMonth == currMonth) currMinute
            else if (selectHour == targetHour && selectDay == targetDay && selectMonth == targetMonth) targetMinute
            else 0
        if (selectHour == targetHour && selectDay == targetDay && selectMonth == targetMonth) {
            for (i in 0..startMinute) {
                minuteList.add(if (i < 10) "0$i" else "$i")
            }
        } else {
            for (i in startMinute..59) {
                minuteList.add(if (i < 10) "0$i" else "$i")
            }
        }

        binding.wheelMinute.setEntries(minuteList)
    }


    interface ISelectDateCallback {
        /**
         * @param timeStr "yyyy-MM-dd" 格式 或者“yyyy-MM-DD-HH-MM-SS”
         */
        fun onSelectComplete(timestamp: Long, timeStr: String)
    }
}