/*
 * This file is part of Breezy Weather.
 *
 * Breezy Weather is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, version 3 of the License.
 *
 * Breezy Weather is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
 */

package org.breezyweather.common.options.appearance

import android.content.Context
import android.icu.util.ULocale
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.text.util.LocalePreferences
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import org.breezyweather.R
import org.breezyweather.common.extensions.capitalize
import org.breezyweather.common.extensions.currentLocale
import org.breezyweather.common.utils.helpers.LogHelper
import org.breezyweather.domain.settings.SettingsManager
import java.util.Locale

object CalendarHelper {

    const val CALENDAR_EXTENSION_TYPE = "ca"
    private const val NUMBERS_EXTENSION_TYPE = "nu"
    private const val DISPLAY_KEYWORD_OF_CALENDAR = "calendar"

    private val supportedCalendars = listOf(
        LocalePreferences.CalendarType.CHINESE,
        LocalePreferences.CalendarType.DANGI,
        LocalePreferences.CalendarType.HEBREW,
        LocalePreferences.CalendarType.INDIAN,
        LocalePreferences.CalendarType.ISLAMIC,
        LocalePreferences.CalendarType.ISLAMIC_CIVIL,
        LocalePreferences.CalendarType.ISLAMIC_RGSA,
        LocalePreferences.CalendarType.ISLAMIC_TBLA,
        LocalePreferences.CalendarType.ISLAMIC_UMALQURA,
        LocalePreferences.CalendarType.PERSIAN
    )

    @RequiresApi(Build.VERSION_CODES.N)
    private fun getDisplayName(
        calendar: String,
        locale: Locale = Locale.Builder().setLanguage("en").setRegion("001").build(),
    ): String {
        val localeWithCalendar = Locale.Builder()
            .setUnicodeLocaleKeyword(CALENDAR_EXTENSION_TYPE, calendar)
            .build()

        return ULocale.getDisplayKeywordValue(
            localeWithCalendar.toLanguageTag(),
            DISPLAY_KEYWORD_OF_CALENDAR,
            ULocale.forLocale(locale)
        )
    }

    @RequiresApi(Build.VERSION_CODES.N)
    fun getCalendars(context: Context): ImmutableList<AlternateCalendar> {
        return supportedCalendars.map {
            val displayName = try {
                getDisplayName(it, context.currentLocale).let { result ->
                    if (result.equals(it, ignoreCase = false)) {
                        // Fallback to English if there is no translation
                        getDisplayName(it).capitalize(context.currentLocale)
                    } else {
                        result.capitalize()
                    }
                }
            } catch (_: Exception) {
                try {
                    getDisplayName(it).capitalize()
                } catch (_: Exception) {
                    it.capitalize()
                }
            }
            AlternateCalendar(
                id = it,
                displayName = displayName,
                additionalParams = when (it) {
                    "chinese" -> mapOf(NUMBERS_EXTENSION_TYPE to "hanidays")
                    else -> null
                },
                specificPattern = when (it) {
                    "chinese" -> "MMMd"
                    else -> null
                }
            )
        }.sortedBy {
            it.displayName
        }.toMutableList().apply {
            add(0, AlternateCalendar("none", context.getString(R.string.settings_none)))
            add(
                1,
                AlternateCalendar(
                    "",
                    context.getString(
                        R.string.parenthesis,
                        context.getString(R.string.settings_regional_preference),
                        getCalendarPreferenceForLocale(context.currentLocale).let { calendarRegionalPref ->
                            firstOrNull { it.id == calendarRegionalPref }?.displayName
                        } ?: context.getString(R.string.settings_none)
                    )
                )
            )
        }.toImmutableList()
    }

    private fun getCalendarPreferenceForLocale(locale: Locale): String {
        LogHelper.log(msg = "${locale.language}")
        return with(locale) {
            when {
                arrayOf("CN", "HK", "MO", "TW").any { country.equals(it, ignoreCase = true) } ||
                    language.equals("zh", ignoreCase = true) -> {
                    LocalePreferences.CalendarType.CHINESE
                }
                arrayOf("KP", "KR").any { country.equals(it, ignoreCase = true) } ||
                    language.equals("ko", ignoreCase = true) -> {
                    LocalePreferences.CalendarType.DANGI
                }
                arrayOf("he", "iw").any { language.equals(it, ignoreCase = true) } -> {
                    LocalePreferences.CalendarType.HEBREW
                }
                country.equals("IN", ignoreCase = true) -> LocalePreferences.CalendarType.INDIAN
                country.equals("SA", ignoreCase = true) -> LocalePreferences.CalendarType.ISLAMIC_RGSA
                country.equals("IR", ignoreCase = true) || language.equals("fa", ignoreCase = true) -> {
                    LocalePreferences.CalendarType.PERSIAN
                }
                // Looks like all locales defaults to Gregorian calendar:
                // https://unicode-org.github.io/icu/userguide/datetime/calendar/#calendar-locale-and-keyword-handling
                else -> LocalePreferences.getCalendarType(locale)
            }
        }
    }

    fun getAlternateCalendarSetting(context: Context): String? {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            return null
        }

        val alternateCalendarSetting = SettingsManager.getInstance(context).alternateCalendar
        if (alternateCalendarSetting == "none") {
            return null
        }
        val alternateCalendar = alternateCalendarSetting.ifEmpty {
            getCalendarPreferenceForLocale(context.currentLocale)
        }
        return if (supportedCalendars.contains(alternateCalendar)) {
            alternateCalendar
        } else {
            null
        }
    }

    data class AlternateCalendar(
        val id: String,
        val displayName: String,
        val additionalParams: Map<String, String>? = null,
        val specificPattern: String? = null,
    )
}
