package ru.forbion.f7cloud.feature.calendar

import java.time.LocalDate
import java.time.format.DateTimeFormatter

/** Разобранное правило повторения (подмножество RFC 5545 RRULE, как в веб-версии). */
data class RecurrenceRule(
    val freq: String = "WEEKLY", // DAILY | WEEKLY | MONTHLY | YEARLY
    val interval: Int = 1,
    /** Дни недели для WEEKLY: MO..SU (порядок как в ICS). */
    val byDays: Set<String> = emptySet(),
    /** Для MONTHLY с BYDAY=<n><день>: ординал (1..5 = n-й, -1 = последний). null — по числу месяца. */
    val monthlyOrdinal: Int? = null,
    val endType: EndType = EndType.NEVER,
    val untilDate: LocalDate? = null,
    val count: Int = 10,
) {
    enum class EndType { NEVER, UNTIL, COUNT }

    fun toRrule(): String = buildString {
        append("FREQ=$freq")
        if (interval > 1) append(";INTERVAL=$interval")
        if (freq == "MONTHLY" && monthlyOrdinal != null && byDays.isNotEmpty()) {
            // Месячный n-й день недели, напр. BYDAY=1MO (первый понедельник) — раньше терялся.
            append(";BYDAY=$monthlyOrdinal${ICS_DAYS.first { it in byDays }}")
        } else if (freq == "WEEKLY" && byDays.isNotEmpty()) {
            append(";BYDAY=${ICS_DAYS.filter { it in byDays }.joinToString(",")}")
        }
        when (endType) {
            EndType.UNTIL -> untilDate?.let { append(";UNTIL=${it.format(UNTIL_FMT)}T235959Z") }
            EndType.COUNT -> append(";COUNT=${count.coerceAtLeast(1)}")
            EndType.NEVER -> Unit
        }
    }

    companion object {
        val ICS_DAYS = listOf("MO", "TU", "WE", "TH", "FR", "SA", "SU")
        val DAY_LABELS = mapOf(
            "MO" to "Пн", "TU" to "Вт", "WE" to "Ср", "TH" to "Чт",
            "FR" to "Пт", "SA" to "Сб", "SU" to "Вс",
        )
        private val UNTIL_FMT = DateTimeFormatter.BASIC_ISO_DATE
        private val BYDAY_ORDINAL = Regex("^(-?\\d+)(MO|TU|WE|TH|FR|SA|SU)$")

        /** Парсинг RRULE-строки; null для пустой/неразбираемой. */
        fun parse(rrule: String): RecurrenceRule? {
            if (rrule.isBlank()) return null
            val parts = rrule.trim().removePrefix("RRULE:").split(';')
                .mapNotNull { p ->
                    val idx = p.indexOf('=')
                    if (idx <= 0) null else p.substring(0, idx).uppercase() to p.substring(idx + 1)
                }.toMap()
            val freq = parts["FREQ"]?.uppercase() ?: return null
            if (freq !in setOf("DAILY", "WEEKLY", "MONTHLY", "YEARLY")) return null
            val until = parts["UNTIL"]?.let { raw ->
                runCatching { LocalDate.parse(raw.take(8), UNTIL_FMT) }.getOrNull()
            }
            val count = parts["COUNT"]?.toIntOrNull()
            val bydayTokens = parts["BYDAY"]?.split(',')?.map { it.trim().uppercase() }.orEmpty()
            // Ординал для MONTHLY (BYDAY=1MO/-1FR): берём из первого токена с числовым префиксом.
            val monthlyOrdinal = bydayTokens
                .takeIf { freq == "MONTHLY" }
                ?.firstNotNullOfOrNull { BYDAY_ORDINAL.find(it)?.groupValues?.get(1)?.toIntOrNull() }
            return RecurrenceRule(
                freq = freq,
                interval = parts["INTERVAL"]?.toIntOrNull()?.coerceAtLeast(1) ?: 1,
                byDays = bydayTokens.map { it.takeLast(2) }.filter { it in ICS_DAYS }.toSet(),
                monthlyOrdinal = monthlyOrdinal,
                endType = when {
                    until != null -> EndType.UNTIL
                    count != null -> EndType.COUNT
                    else -> EndType.NEVER
                },
                untilDate = until,
                count = count ?: 10,
            )
        }

        /** Человекочитаемое описание правила по-русски (для пилюли «Повторение»). */
        fun describe(rrule: String): String {
            val rule = parse(rrule) ?: return "Не повторять"
            val freqLabel = when (rule.freq) {
                "DAILY" -> if (rule.interval == 1) "Каждый день" else "Каждые ${rule.interval} дн."
                "WEEKLY" -> if (rule.interval == 1) "Каждую неделю" else "Каждые ${rule.interval} нед."
                "MONTHLY" -> if (rule.interval == 1) "Каждый месяц" else "Каждые ${rule.interval} мес."
                else -> if (rule.interval == 1) "Каждый год" else "Каждые ${rule.interval} г."
            }
            val days = if (rule.freq == "WEEKLY" && rule.byDays.isNotEmpty()) {
                " по " + ICS_DAYS.filter { it in rule.byDays }.joinToString(", ") { DAY_LABELS[it] ?: it }
            } else ""
            val end = when (rule.endType) {
                EndType.UNTIL -> rule.untilDate?.let { " до ${it.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))}" }.orEmpty()
                EndType.COUNT -> ", ${rule.count} раз"
                EndType.NEVER -> ""
            }
            return freqLabel + days + end
        }
    }
}
