package de.tsschulz.tt_tagebuch.app.ui

import de.tsschulz.tt_tagebuch.shared.api.models.Member
import java.util.Calendar
import java.util.GregorianCalendar

/**
 * Entspricht der Aufstellungs-/Meldungslogik in `TeamManagementView.vue`.
 */
internal object TeamEditorLineupLogic {

    fun seasonReferenceYearFromLabel(seasonLabel: String): Int {
        val s = seasonLabel.trim()
        Regex("(\\d{4})\\s*/\\s*(\\d{4})").find(s)?.let {
            return it.groupValues[2].toInt()
        }
        Regex("(\\d{4})\\s*/\\s*(\\d{2})").find(s)?.let {
            val startYear = it.groupValues[1].toInt()
            val endTwo = it.groupValues[2].toInt()
            val century = (startYear / 100) * 100
            return century + endTwo
        }
        Regex("(\\d{4})").find(s)?.let { return it.groupValues[1].toInt() }
        return GregorianCalendar().get(Calendar.YEAR)
    }

    fun parseBirthCalendar(value: String?): Calendar? {
        if (value.isNullOrBlank()) return null
        val trimmed = value.trim()
        Regex("^(\\d{1,2})\\.(\\d{1,2})\\.(\\d{4})$").find(trimmed)?.let {
            return GregorianCalendar(
                it.groupValues[3].toInt(),
                it.groupValues[2].toInt() - 1,
                it.groupValues[1].toInt(),
            )
        }
        Regex("^(\\d{4})-(\\d{2})-(\\d{2})$").find(trimmed)?.let {
            return GregorianCalendar(
                it.groupValues[1].toInt(),
                it.groupValues[2].toInt() - 1,
                it.groupValues[3].toInt(),
            )
        }
        return null
    }

    fun memberActualAge(member: Member, today: Calendar = GregorianCalendar()): Int? {
        val birth = parseBirthCalendar(member.birthDate) ?: return null
        var age = today.get(Calendar.YEAR) - birth.get(Calendar.YEAR)
        val hadBirthday =
            today.get(Calendar.MONTH) > birth.get(Calendar.MONTH) ||
                (today.get(Calendar.MONTH) == birth.get(Calendar.MONTH) &&
                    today.get(Calendar.DAY_OF_MONTH) >= birth.get(Calendar.DAY_OF_MONTH))
        if (!hadBirthday) age -= 1
        return age
    }

    fun memberSeasonAge(member: Member, seasonLabel: String): Int? {
        val birth = parseBirthCalendar(member.birthDate) ?: return null
        val refYear = seasonReferenceYearFromLabel(seasonLabel)
        return refYear - birth.get(Calendar.YEAR)
    }

    fun memberAgeGroupCode(member: Member, seasonLabel: String): String {
        val seasonAge = memberSeasonAge(member, seasonLabel) ?: return "unknown"
        return when {
            seasonAge <= 11 -> "J11"
            seasonAge <= 13 -> "J13"
            seasonAge <= 15 -> "J15"
            seasonAge <= 17 -> "J17"
            seasonAge <= 19 -> "J19"
            else -> "adult"
        }
    }

    fun parseLeagueAgeGroupCode(leagueName: String?): String {
        val source = leagueName.orEmpty()
        Regex("([JM])(\\d{1,2})", RegexOption.IGNORE_CASE).find(source)?.let {
            return "J${it.groupValues[2]}"
        }
        Regex("(?:jugend|mädchen)\\s*(\\d{1,2})", RegexOption.IGNORE_CASE).find(source)?.let {
            return "J${it.groupValues[1]}"
        }
        return "adult"
    }

    fun parseLeagueGenderCode(leagueName: String?): String {
        val s = leagueName.orEmpty().lowercase()
        return if (Regex("(frauen|damen|mädchen|girls|women)").containsMatchIn(s)) "female" else "open"
    }

    fun configuredTeamAgeGroup(teamAgeGroup: String?, leagueName: String?): String =
        teamAgeGroup?.takeIf { it.isNotBlank() } ?: parseLeagueAgeGroupCode(leagueName)

    fun configuredTeamGender(teamGender: String?, leagueName: String?): String =
        teamGender?.takeIf { it.isNotBlank() } ?: parseLeagueGenderCode(leagueName)

    fun isFemaleEligible(member: Member, teamGender: String): Boolean {
        if (teamGender != "female") return true
        return member.gender == "female"
    }

    fun isEligibleForTeam(member: Member, teamAgeGroup: String, teamGender: String, seasonLabel: String): Boolean {
        if (!member.active || !isFemaleEligible(member, teamGender)) return false
        val memberAgeGroup = memberAgeGroupCode(member, seasonLabel)
        if (memberAgeGroup == "unknown") return false
        if (teamAgeGroup == "adult") {
            val actual = memberActualAge(member)
            if (actual != null && actual >= 18) return true
            return member.adultReleaseApproved == true || member.adultReserveApproved == true
        }
        val order = listOf("J11", "J13", "J15", "J17", "J19")
        val mi = order.indexOf(memberAgeGroup)
        val ti = order.indexOf(teamAgeGroup)
        if (mi < 0 || ti < 0) return false
        return mi <= ti
    }

    fun lineupRatingValue(member: Member): Double {
        val q = member.qttr ?: return Double.NEGATIVE_INFINITY
        if (q <= 0) return Double.NEGATIVE_INFINITY
        return q.toDouble()
    }

    fun lineupRatingLabel(member: Member): String {
        val q = member.qttr ?: return "–"
        if (q <= 0) return "–"
        return q.toString()
    }

    fun eligibilityLabel(member: Member, teamAgeGroup: String, seasonLabel: String): String {
        val mag = memberAgeGroupCode(member, seasonLabel)
        if (teamAgeGroup != "adult" || mag == "adult") return "OK"
        if (member.adultReleaseApproved == true && member.adultReserveApproved == true) {
            return "Erw.+Reserve"
        }
        if (member.adultReleaseApproved == true) return "Erw.Freigabe"
        if (member.adultReserveApproved == true) return "Reserve"
        return "OK"
    }

    data class LineupGroup(
        val code: String,
        val label: String,
        val members: List<MemberDisplay>,
    )

    data class MemberDisplay(
        val member: Member,
        val lineupRatingLabel: String,
        val memberAgeGroupLabel: String,
        val eligibilityLabel: String,
    )

    fun buildLineupGroups(
        members: List<Member>,
        teamAgeGroup: String,
        teamGender: String,
        seasonLabel: String,
        labelAdults: String,
        labelUnknown: String,
    ): List<LineupGroup> {
        val eligible = members.filter { isEligibleForTeam(it, teamAgeGroup, teamGender, seasonLabel) }
        if (eligible.isEmpty()) return emptyList()
        val preferred = teamAgeGroup
        val defaultOrder =
            if (teamAgeGroup == "adult") {
                listOf("adult", "J19", "J17", "J15", "J13", "J11", "unknown")
            } else {
                listOf(teamAgeGroup) + listOf("J19", "J17", "J15", "J13", "J11").filter { it != teamAgeGroup } + "unknown"
            }
        val groupOrder =
            if (defaultOrder.contains(preferred)) {
                listOf(preferred) + defaultOrder.filter { it != preferred }
            } else {
                defaultOrder
            }

        fun ageLabel(code: String) = when (code) {
            "adult" -> labelAdults
            "unknown" -> labelUnknown
            else -> code
        }

        val byCode = linkedMapOf<String, MutableList<MemberDisplay>>()
        for (m in eligible) {
            val code = memberAgeGroupCode(m, seasonLabel)
            val row = MemberDisplay(
                member = m,
                lineupRatingLabel = lineupRatingLabel(m),
                memberAgeGroupLabel = ageLabel(code),
                eligibilityLabel = eligibilityLabel(m, teamAgeGroup, seasonLabel),
            )
            byCode.getOrPut(code) { mutableListOf() }.add(row)
        }
        for (list in byCode.values) {
            list.sortWith(
                compareByDescending<MemberDisplay> { lineupRatingValue(it.member) }
                    .thenBy { it.member.lastName.lowercase() }
                    .thenBy { it.member.firstName.lowercase() },
            )
        }
        return groupOrder.mapNotNull { code ->
            val rows = byCode[code] ?: return@mapNotNull null
            if (rows.isEmpty()) return@mapNotNull null
            LineupGroup(code = code, label = ageLabel(code), members = rows)
        }
    }

    fun lineupGapViolationMessage(selectedOrdered: List<Member>, template: (String, String) -> String): String? {
        for (i in 0 until selectedOrdered.size - 1) {
            val a = selectedOrdered[i]
            val b = selectedOrdered[i + 1]
            val ra = lineupRatingValue(a)
            val rb = lineupRatingValue(b)
            if (!ra.isFinite() || !rb.isFinite()) continue
            if (rb > ra + 30) {
                return template("${b.firstName} ${b.lastName}".trim(), "${a.firstName} ${a.lastName}".trim())
            }
        }
        return null
    }
}
