package ru.yandex.direct.api.v5.entity.campaigns

import ru.yandex.direct.core.entity.timetarget.model.GeoTimezone
import ru.yandex.direct.core.entity.timetarget.model.GroupType
import ru.yandex.direct.i18n.I18NBundle
import ru.yandex.direct.i18n.Translatable
import ru.yandex.direct.i18n.bundle.MessageFormatStub
import ru.yandex.direct.i18n.bundle.TranslationBundle
import ru.yandex.direct.i18n.bundle.TranslationStub
import ru.yandex.direct.i18n.types.ConcatTranslatable
import ru.yandex.direct.i18n.types.DummyTranslatable
import ru.yandex.direct.utils.DateTimeUtils
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.time.temporal.TemporalAccessor
import kotlin.math.abs

interface StatusClarificationTranslations : TranslationBundle {
    @TranslationStub("Сконвертирована")
    fun converted(): Translatable

    @TranslationStub("Кампания перенесена в архив")
    fun archived(): Translatable

    @TranslationStub("Ожидает архивирования")
    fun archiving(): Translatable

    @TranslationStub("Ожидает разархивирования")
    fun unarchiving(): Translatable

    @TranslationStub("Нет объявлений")
    fun noAds(): Translatable

    @TranslationStub("Нет активных объявлений")
    fun noActiveAds(): Translatable

    @MessageFormatStub("Ожидает старта стратегии {0}")
    fun awaitingStrategyLaunch(startDate: String): Translatable

    @MessageFormatStub("Начало {0}")
    fun startDate(startDate: String): Translatable

    @MessageFormatStub("Кампания закончилась {0}")
    fun ended(endDate: String): Translatable

    @TranslationStub("Закончился период действия стратегии")
    fun strategyPeriodExpired(): Translatable

    @TranslationStub("Режим автопродления")
    fun strategyAutoProlongation(): Translatable

    @TranslationStub("Средства на счете закончились")
    fun fundsRunOut(): Translatable

    @TranslationStub("Кампания остановлена")
    fun stopped(): Translatable

    @MessageFormatStub("Показы приостановлены по дневному ограничению в {0} (MSK)")
    fun pausedByDayBudget(stopTime: String): Translatable

    @MessageFormatStub("Показы приостановлены по дневному ограничению общего счёта в {0} (MSK)")
    fun pausedByWalletDayBudget(stopTime: String): Translatable

    @TranslationStub("Идут показы")
    fun campaignIsInProgress(): Translatable

    @MessageFormatStub("Показы начнутся в {0}")
    fun impressionsWillBeginToday(time: String): Translatable

    @MessageFormatStub("Показы начнутся завтра в {0}")
    fun impressionsWillBeginTomorrow(time: String): Translatable

    @MessageFormatStub("Показы начнутся {0} в {1}")
    fun impressionsWillBegin(onDay: Translatable, time: String): Translatable

    @TranslationStub("в понедельник")
    fun onMonday(): Translatable

    @TranslationStub("во вторник")
    fun onTuesday(): Translatable

    @TranslationStub("в среду")
    fun onWednesday(): Translatable

    @TranslationStub("в четверг")
    fun onThursday(): Translatable

    @TranslationStub("в пятницу")
    fun onFriday(): Translatable

    @TranslationStub("в субботу")
    fun onSaturday(): Translatable

    @TranslationStub("в воскресенье")
    fun onSunday(): Translatable

    @TranslationStub("Ждёт оплаты")
    fun awaitingPayment(): Translatable

    @TranslationStub("Черновик")
    fun draft(): Translatable

    @TranslationStub("Допущено модератором")
    fun acceptedOnModeration(): Translatable

    @TranslationStub("Отклонено модератором")
    fun rejectedOnModeration(): Translatable

    @TranslationStub("Ожидает модерации")
    fun awaitingModeration(): Translatable

    @MessageFormatStub("Дата окончания кампании {0}")
    fun endDate(endDate: String): Translatable

    @TranslationStub("Идет активизация")
    fun activating(): Translatable

    companion object {
        @JvmField
        val INSTANCE: StatusClarificationTranslations =
            I18NBundle.implement(StatusClarificationTranslations::class.java)

        fun awaitingStrategyLaunch(startDate: LocalDate) =
            INSTANCE.awaitingStrategyLaunch(startDate.toDateString())

        fun startDate(startDate: LocalDate) =
            INSTANCE.startDate(startDate.toDateString())

        fun pausedByDayBudget(stopTime: LocalDateTime) =
            INSTANCE.pausedByDayBudget(stopTime.toTimeString(twoDigitsHour = true))

        fun pausedByWalletDayBudget(stopTime: LocalDateTime) =
            INSTANCE.pausedByWalletDayBudget(stopTime.toTimeString(twoDigitsHour = true))

        fun impressionsWillBeginToday(
            startDateTime: OffsetDateTime,
            geoTimezone: GeoTimezone,
        ): Translatable =
            INSTANCE.impressionsWillBeginToday(startDateTime.toTimeString(twoDigitsHour = false))
                .appendTimezoneOffset(startDateTime, geoTimezone)

        fun impressionsWillBeginTomorrow(
            startDateTime: OffsetDateTime,
            geoTimezone: GeoTimezone,
        ): Translatable =
            INSTANCE.impressionsWillBeginTomorrow(startDateTime.toTimeString(twoDigitsHour = false))
                .appendTimezoneOffset(startDateTime, geoTimezone)

        fun impressionsWillBeginThisWeek(
            startDateTime: OffsetDateTime,
            geoTimezone: GeoTimezone,
        ): Translatable {
            @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA")
            val weekday = with(INSTANCE) {
                when (startDateTime.dayOfWeek) {
                    DayOfWeek.MONDAY -> onMonday()
                    DayOfWeek.TUESDAY -> onTuesday()
                    DayOfWeek.WEDNESDAY -> onWednesday()
                    DayOfWeek.THURSDAY -> onThursday()
                    DayOfWeek.FRIDAY -> onFriday()
                    DayOfWeek.SATURDAY -> onSaturday()
                    DayOfWeek.SUNDAY -> onSunday()
                }
            }

            return INSTANCE.impressionsWillBegin(weekday, time = startDateTime.toTimeString(twoDigitsHour = false))
                .appendTimezoneOffset(startDateTime, geoTimezone)
        }

        fun impressionsWillBeginLaterThanThisWeek(
            startDateTime: OffsetDateTime,
            geoTimezone: GeoTimezone,
        ): Translatable {
            val date = startDateTime.format(DateTimeFormatter.ofPattern("dd.MM"))
            return INSTANCE.impressionsWillBegin(
                onDay = DummyTranslatable(date),
                time = startDateTime.toTimeString(twoDigitsHour = false),
            ).appendTimezoneOffset(startDateTime, geoTimezone)
        }

        fun endDate(endDate: LocalDate) =
            INSTANCE.endDate(endDate.toDateString())

        fun ended(endDate: LocalDate) =
            INSTANCE.ended(endDate.toDateString())
    }
}

private val DATE_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.uuuu")
private fun TemporalAccessor.toDateString(): String =
    DATE_FORMATTER.format(this)

private val TWO_DIGITS_HOUR_TIME_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
private val ONE_DIGIT_HOUR_TIME_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("H:mm")
private fun TemporalAccessor.toTimeString(twoDigitsHour: Boolean): String {
    val formatter = if (twoDigitsHour) {
        TWO_DIGITS_HOUR_TIME_FORMATTER
    } else {
        ONE_DIGIT_HOUR_TIME_FORMATTER
    }
    return formatter.format(this)
}

/**
 * Дублирует функционал [ru.yandex.direct.api.v5.entity.dictionaries.DictionariesService.getTimezoneOffset]
 */
private fun Translatable.appendTimezoneOffset(
    offsetDateTime: OffsetDateTime,
    geoTimezone: GeoTimezone,
): Translatable {
    val offset = offsetDateTime.offset
    val mskOffset = DateTimeUtils.MSK.rules.getOffset(offsetDateTime.toLocalDateTime())

    val offsetSuffix: String = when {
        geoTimezone.timezone == DateTimeUtils.MSK -> return this
        geoTimezone.groupType == GroupType.RUSSIA -> " (MSK ${formatOffset(offset, base = mskOffset)})"
        geoTimezone.groupType == GroupType.WORLD -> " (GMT ${formatOffset(offset, base = ZoneOffset.UTC)})"
        geoTimezone.groupType == GroupType.CIS -> {
            " (MSK ${formatOffset(offset, base = mskOffset)}, GMT ${formatOffset(offset, base = ZoneOffset.UTC)})"
        }
        else -> return this
    }

    return ConcatTranslatable(this, DummyTranslatable(offsetSuffix))
}

/**
 * Копия [ru.yandex.direct.grid.processing.service.constant.ConstantsConverter.formatOffset]
 */
private fun formatOffset(offset: ZoneOffset, base: ZoneOffset): String {
    val diff: Int = offset.totalSeconds - base.totalSeconds
    val hours = diff / (60 * 60)
    val minutes = abs(diff - hours * 60 * 60) / 60
    return "%s%02d:%02d".format(if (diff >= 0) "+" else "-", abs(hours), minutes)
}
