package de.tsschulz.tt_tagebuch.app.util

import de.tsschulz.tt_tagebuch.shared.api.models.Member
import de.tsschulz.tt_tagebuch.shared.api.models.OfficialParsedCompetitionDto
import de.tsschulz.tt_tagebuch.shared.api.models.OfficialParsedTournamentEnvelopeDto
import java.text.Collator
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.GregorianCalendar
import java.util.Locale

/**
 * Entspricht der Logik in [frontend/src/views/OfficialTournaments.vue] (isEligibleForCompetition).
 */
object OfficialTournamentEligibility {

    private val collator: Collator = Collator.getInstance(Locale.GERMAN).apply { strength = Collator.PRIMARY }

    fun compareMembers(a: Member, b: Member): Int {
        val c = collator.compare(a.firstName, b.firstName)
        if (c != 0) return c
        return collator.compare(a.lastName, b.lastName)
    }

    fun eligibleActiveMembers(
        parsed: OfficialParsedTournamentEnvelopeDto?,
        competition: OfficialParsedCompetitionDto,
        members: List<Member>,
    ): List<Member> {
        return members
            .filter { it.active }
            .filter { isEligibleForCompetition(it, competition, parsed) }
            .sortedWith(::compareMembers)
    }

    fun isEligibleForCompetition(
        member: Member,
        c: OfficialParsedCompetitionDto,
        parsed: OfficialParsedTournamentEnvelopeDto?,
    ): Boolean {
        val rule = getGenderRule(c)
        val g = (member.gender ?: "unknown").lowercase(Locale.ROOT)
        if (rule == "female" && g != "female") return false
        if (rule == "male" && g != "male") return false
        if (!isEligibleByAge(member, c, parsed)) return false
        return true
    }

    private fun getGenderRule(c: OfficialParsedCompetitionDto): String {
        val txt = "${c.ageClassCompetition ?: c.altersklasseWettbewerb ?: ""} ${c.openTo ?: c.offenFuer ?: ""}"
            .lowercase(Locale.ROOT)
        if (Regex("(mädchen|weiblich|\\bw\\b)").containsMatchIn(txt)) return "female"
        if (Regex("(jungen|männlich|\\bm\\b)").containsMatchIn(txt)) return "male"
        if ("jugend" in txt) return "both"
        return "both"
    }

    /** Entspricht [OfficialTournaments.vue] `isEligibleByAge` (Reihenfolge: Jahrgang → Stichtag → U/Jugend/AK). */
    private fun isEligibleByAge(member: Member, c: OfficialParsedCompetitionDto, parsed: OfficialParsedTournamentEnvelopeDto?): Boolean {
        val bd = parseDateFlexible(member.birthDate ?: "") ?: return false

        val birthYearRule = getBirthYearFromPerformanceClass(c)
        if (birthYearRule != null) {
            val memberYear = bd.get(Calendar.YEAR)
            if (birthYearRule.exact != null) return memberYear == birthYearRule.exact
            if (birthYearRule.minYear != null) return memberYear >= birthYearRule.minYear
        }

        val cutoff = getCutoffDate(c)
        if (cutoff != null) {
            if (bd.timeInMillis <= cutoff.timeInMillis) return false
        }

        val ageLimit = getAgeLimitFromText("${c.ageClassCompetition ?: c.altersklasseWettbewerb ?: ""}")
        if (ageLimit != null) {
            val ref = getReferenceDate(c, parsed) ?: return false
            val age = calculateAgeOnDate(bd, ref) ?: return false
            return if (ageLimit.exclusive) age < ageLimit.limit else age <= ageLimit.limit
        }

        return true
    }

    data class AgeLimit(val limit: Int, val exclusive: Boolean)
    data class BirthYearRule(val exact: Int? = null, val minYear: Int? = null)

    private fun getAgeLimitFromText(text: String?): AgeLimit? {
        if (text.isNullOrBlank()) return null
        val t = text
        Regex("(?:jugend|mädchen|maedchen)\\s+(\\d{1,2})\\b", RegexOption.IGNORE_CASE).find(t)?.let {
            return AgeLimit(it.groupValues[1].toInt(), exclusive = false)
        }
        Regex("\\bU\\s*(\\d{1,2})\\b", RegexOption.IGNORE_CASE).find(t)?.let {
            return AgeLimit(it.groupValues[1].toInt(), exclusive = true)
        }
        Regex("\\bAK\\s*(\\d{1,2})\\b", RegexOption.IGNORE_CASE).find(t)?.let { m ->
            if (!Regex("\\(\\s*AK\\s*\\d+\\s*\\)", RegexOption.IGNORE_CASE).containsMatchIn(t)) {
                return AgeLimit(m.groupValues[1].toInt(), exclusive = true)
            }
        }
        return null
    }

    private fun getBirthYearFromPerformanceClass(c: OfficialParsedCompetitionDto): BirthYearRule? {
        val pc = "${c.performanceClass ?: c.leistungsklasse ?: ""}"
        Regex("jahrgang\\s+(\\d{4})\\s+und\\s+jünger", RegexOption.IGNORE_CASE).find(pc)?.let {
            return BirthYearRule(minYear = it.groupValues[1].toInt())
        }
        Regex("jahrgang\\s+(\\d{4})\\b", RegexOption.IGNORE_CASE).find(pc)?.let {
            return BirthYearRule(exact = it.groupValues[1].toInt())
        }
        return null
    }

    private fun getCutoffDate(c: OfficialParsedCompetitionDto): Calendar? {
        var valStr = (c.cutoffDate ?: c.stichtag ?: "").trim()
        if (valStr.isEmpty()) {
            val pc = "${c.performanceClass ?: c.leistungsklasse ?: ""}"
            Regex("stichtag\\s*:?\\s*([0-3]?\\d\\.[01]?\\d\\.\\d{4})", RegexOption.IGNORE_CASE).find(pc)?.let {
                valStr = it.groupValues[1].trim()
            }
        }
        return parseDateFlexible(valStr)
    }

    fun getReferenceDate(c: OfficialParsedCompetitionDto, parsed: OfficialParsedTournamentEnvelopeDto?): Calendar? {
        val fromStart = Regex("(\\d{1,2}\\.\\d{1,2}\\.\\d{4})").find("${c.startTime ?: c.startzeit ?: ""}")
        if (fromStart != null) return parseDateFlexible(fromStart.value)
        return parseDateFlexible(parsed?.parsedData?.termin ?: "")
    }

    private fun calculateAgeOnDate(birth: Calendar, ref: Calendar): Int? {
        var age = ref.get(Calendar.YEAR) - birth.get(Calendar.YEAR)
        val m = ref.get(Calendar.MONTH) - birth.get(Calendar.MONTH)
        if (m < 0 || (m == 0 && ref.get(Calendar.DAY_OF_MONTH) < birth.get(Calendar.DAY_OF_MONTH))) age--
        return age
    }

    fun parseDateFlexible(s: String?): Calendar? {
        if (s.isNullOrBlank()) return null
        val t = s.trim()
        Regex("^(\\d{1,2})\\.(\\d{1,2})\\.(\\d{4})$").find(t)?.let {
            val d = it.groupValues[1].toInt()
            val m = it.groupValues[2].toInt()
            val y = it.groupValues[3].toInt()
            return GregorianCalendar(y, m - 1, d)
        }
        Regex("^(\\d{4})-(\\d{2})-(\\d{2})$").find(t)?.let {
            val y = it.groupValues[1].toInt()
            val m = it.groupValues[2].toInt()
            val d = it.groupValues[3].toInt()
            return GregorianCalendar(y, m - 1, d)
        }
        return runCatching {
            val ms = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.GERMAN).parse(t)?.time
                ?: SimpleDateFormat("yyyy-MM-dd", Locale.GERMAN).parse(t)?.time
            if (ms == null) null else GregorianCalendar.getInstance().apply { timeInMillis = ms }
        }.getOrNull()
    }
}
