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

import com.yandex.direct.api.v5.campaigns.CampaignFieldEnum
import com.yandex.direct.api.v5.campaigns.CampaignGetItem
import com.yandex.direct.api.v5.campaigns.CampaignTypeGetEnum
import com.yandex.direct.api.v5.campaigns.CpmBannerCampaignFieldEnum
import com.yandex.direct.api.v5.campaigns.CpmBannerCampaignGetItem
import com.yandex.direct.api.v5.campaigns.DynamicTextCampaignFieldEnum
import com.yandex.direct.api.v5.campaigns.DynamicTextCampaignGetItem
import com.yandex.direct.api.v5.campaigns.MobileAppCampaignFieldEnum
import com.yandex.direct.api.v5.campaigns.MobileAppCampaignGetItem
import com.yandex.direct.api.v5.campaigns.SmartCampaignFieldEnum
import com.yandex.direct.api.v5.campaigns.SmartCampaignGetItem
import com.yandex.direct.api.v5.campaigns.TextCampaignFieldEnum
import com.yandex.direct.api.v5.campaigns.TextCampaignGetItem
import com.yandex.direct.api.v5.general.Statistics
import org.springframework.stereotype.Component
import ru.yandex.direct.api.v5.common.EnumPropertyFilter
import ru.yandex.direct.api.v5.common.toArrayOfInteger
import ru.yandex.direct.api.v5.common.toArrayOfString
import ru.yandex.direct.api.v5.entity.campaigns.container.CampaignAnyFieldEnum
import ru.yandex.direct.api.v5.entity.campaigns.container.GetCampaignsContainer
import ru.yandex.direct.api.v5.entity.campaigns.service.CampaignStatusCalculator
import ru.yandex.direct.common.TranslationService
import ru.yandex.direct.common.util.PropertyFilter
import ru.yandex.direct.core.entity.campaign.model.CampaignWithCheckPositionEvent
import ru.yandex.direct.core.entity.campaign.model.CampaignWithClicks
import ru.yandex.direct.core.entity.campaign.model.CampaignWithDayBudget
import ru.yandex.direct.core.entity.campaign.model.CampaignWithDisabledDomainsAndSsp
import ru.yandex.direct.core.entity.campaign.model.CampaignWithMinusKeywords
import ru.yandex.direct.core.entity.campaign.model.CampaignWithShows
import ru.yandex.direct.core.entity.campaign.model.CampaignWithSourceId
import ru.yandex.direct.core.entity.campaign.model.CampaignWithStrategy
import ru.yandex.direct.core.entity.campaign.model.CpmBannerCampaign
import ru.yandex.direct.core.entity.campaign.model.DynamicCampaign
import ru.yandex.direct.core.entity.campaign.model.MobileContentCampaign
import ru.yandex.direct.core.entity.campaign.model.SmartCampaign
import ru.yandex.direct.core.entity.campaign.model.TextCampaign
import ru.yandex.direct.core.entity.feature.service.FeatureService
import ru.yandex.direct.feature.FeatureName
import ru.yandex.direct.libs.timetarget.TimeTargetUtils

@Component
class CampaignsGetResponseConverter(
    private val propertyFilter: PropertyFilter,
    private val translationService: TranslationService,
    private val campaignStatusCalculator: CampaignStatusCalculator,
) {
    private val baseCampaignFilter = createEnumPropertyFilter<CampaignFieldEnum>()
    private val cpmBannerCampaignFilter = createEnumPropertyFilter<CpmBannerCampaignFieldEnum>()
    private val dynamicTextCampaignFilter = createEnumPropertyFilter<DynamicTextCampaignFieldEnum>()
    private val mobileAppCampaignFilter = createEnumPropertyFilter<MobileAppCampaignFieldEnum>()
    private val smartCampaignFilter = createEnumPropertyFilter<SmartCampaignFieldEnum>()
    private val textCampaignFilter = createEnumPropertyFilter<TextCampaignFieldEnum>()

    private inline fun <reified T : Enum<T>> createEnumPropertyFilter(): EnumPropertyFilter<T> =
        EnumPropertyFilter.from(T::class.java, propertyFilter)

    fun massConvertResponseItems(
        containers: Collection<GetCampaignsContainer>,
    ): Map<Long, CampaignGetItem> {
        return containers
            .associateBy(
                keySelector = { it.campaign.id },
                valueTransform = { container -> convertToResponseItem(container) },
            )
            .also { result ->
                if (result.isNotEmpty()) {
                    // для всех кампаний запрашиваются одинаковые поля,
                    // поэтому без разницы, из какого контейнера их брать
                    val requestedFields = containers.first().requestedFields
                    filterProperties(result.values, requestedFields)
                }
            }
    }

    private fun convertToResponseItem(
        container: GetCampaignsContainer,
    ): CampaignGetItem {
        return CampaignGetItem().apply {
            convertCommonFields(container)

            when (container.campaign) {
                is CpmBannerCampaign -> {
                    type = CampaignTypeGetEnum.CPM_BANNER_CAMPAIGN
                    cpmBannerCampaign = createCpmBannerCampaignGetItem(container)
                }
                is DynamicCampaign -> {
                    type = CampaignTypeGetEnum.DYNAMIC_TEXT_CAMPAIGN
                    dynamicTextCampaign = createDynamicTextCampaignGetItem(container)
                }
                is MobileContentCampaign -> {
                    type = CampaignTypeGetEnum.MOBILE_APP_CAMPAIGN
                    mobileAppCampaign = createMobileAppCampaignGetItem(container)
                }
                is SmartCampaign -> {
                    type = CampaignTypeGetEnum.SMART_CAMPAIGN
                    smartCampaign = createSmartCampaignGetItem(container)
                }
                is TextCampaign -> {
                    type = CampaignTypeGetEnum.TEXT_CAMPAIGN
                    textCampaign = createTextCampaignGetItem(container)
                }
            }
        }
    }

    private fun CampaignGetItem.convertCommonFields(
        container: GetCampaignsContainer,
    ) {
        val campaign = container.campaign
        val requestedFields = container.requestedFields

        id = campaign.id
        endDate = FACTORY.createCampaignGetItemEndDate(campaign.endDate?.toApiDate())
        name = campaign.name
        startDate = campaign.startDate?.toApiDate() ?: "0000-00-00"
        status = campaignStatusCalculator.calculateStatus(campaign)
        statusPayment = campaignStatusCalculator.calculateStatusPayment(campaign)
        currency = enumValueOf(campaign.currency.name)
        clientInfo = campaign.fio
        blockedIps = FACTORY.createCampaignGetItemBlockedIps(
            campaign.disabledIps
                ?.ifEmpty { null }
                ?.toArrayOfString()
        )

        if (CampaignAnyFieldEnum.FUNDS in requestedFields) {
            funds = createApiFundsParam(
                campaign = campaign as CampaignWithStrategy,
                ndsRatio = container.ndsRatio,
                sumForTransfer = container.sumForTransfer,
            )
        }

        if (CampaignAnyFieldEnum.SOURCE_ID in requestedFields) {
            sourceId = FACTORY.createCampaignGetItemSourceId(
                (campaign as CampaignWithSourceId).sourceId
            )
        }

        if (CampaignAnyFieldEnum.STATISTICS in requestedFields) {
            statistics = Statistics().apply {
                clicks = (campaign as CampaignWithClicks).clicks?.toLong() ?: 0L
                impressions = (campaign as CampaignWithShows).shows ?: 0
            }
        }

        if (CampaignAnyFieldEnum.DAILY_BUDGET in requestedFields) {
            dailyBudget = FACTORY.createCampaignGetItemDailyBudget(
                (campaign as CampaignWithDayBudget).extractApiDailyBudget()
            )
        }

        if (CampaignAnyFieldEnum.NOTIFICATION in requestedFields) {
            campaign as CampaignWithCheckPositionEvent
            notification = createApiNotificationsSettings(campaign)
        }

        if (CampaignAnyFieldEnum.REPRESENTED_BY in requestedFields) {
            representedBy = createApiRepresentedBySettings(
                managerFio = container.managerFio,
                agencyName = container.agencyName,
            )
        }

        if (CampaignAnyFieldEnum.TIME_TARGETING in requestedFields) {
            val timeTarget = campaign
                .timeTarget
                .takeIf { it.weekdayCoefs.isNotEmpty() }
                ?: TimeTargetUtils.timeTarget24x7()

            timeTargeting = timeTarget.toApiTimeTargeting()
        }

        if (CampaignAnyFieldEnum.TIME_ZONE in requestedFields) {
            timeZone = container.geoTimezone.timezone.id
        }

        if (CampaignAnyFieldEnum.EXCLUDED_SITES in requestedFields) {
            excludedSites = FACTORY.createCampaignGetItemExcludedSites(
                (campaign as CampaignWithDisabledDomainsAndSsp).extractApiExcludedSites()
            )
        }

        if (CampaignAnyFieldEnum.NEGATIVE_KEYWORDS in requestedFields) {
            negativeKeywords = FACTORY.createCampaignGetItemNegativeKeywords(
                (campaign as? CampaignWithMinusKeywords)
                    ?.minusKeywords
                    ?.ifEmpty { null }
                    ?.toArrayOfString()
            )
        }

        if (CampaignAnyFieldEnum.STATE in requestedFields) {
            state = campaignStatusCalculator.calculateState(campaign)
        }

        if (CampaignAnyFieldEnum.STATUS_CLARIFICATION in requestedFields) {
            val text = campaignStatusCalculator.calculateStatusClarification(container)
            statusClarification = translationService.translate(text)
        }
    }

    private fun createCpmBannerCampaignGetItem(
        container: GetCampaignsContainer,
    ): CpmBannerCampaignGetItem =
        CpmBannerCampaignGetItem().apply {
            val campaign = container.campaign as CpmBannerCampaign
            val requestedFields = container.requestedFields

            if (CampaignAnyFieldEnum.CPM_BANNER_CAMPAIGN_BIDDING_STRATEGY in requestedFields) {
                biddingStrategy = createCpmBannerCampaignGetStrategy(campaign)
            }

            if (CampaignAnyFieldEnum.CPM_BANNER_CAMPAIGN_VIDEO_TARGET in requestedFields) {
                videoTarget = FACTORY.createCpmBannerCampaignGetItemVideoTarget(
                    campaign.extractVideoTarget()
                )
            }
            if (CampaignAnyFieldEnum.CPM_BANNER_CAMPAIGN_FREQUENCY_CAP in requestedFields) {
                frequencyCap = FACTORY.createCpmBannerCampaignBaseFrequencyCap(
                    campaign.extractFrequencyCap()
                )
            }

            if (CampaignAnyFieldEnum.CPM_BANNER_CAMPAIGN_COUNTER_IDS in requestedFields) {
                counterIds = FACTORY.createCpmBannerCampaignBaseCounterIds(
                    campaign.metrikaCounters
                        ?.ifEmpty { null }
                        ?.toArrayOfInteger()
                )
            }

            if (CampaignAnyFieldEnum.CPM_BANNER_CAMPAIGN_SETTINGS in requestedFields) {
                settings = createCpmBannerCampaignSettings(
                    campaign, container.clientUid,
                    container.advancedGeoTargeting,
                )
            }
        }

    private fun createDynamicTextCampaignGetItem(
        container: GetCampaignsContainer,
    ): DynamicTextCampaignGetItem =
        DynamicTextCampaignGetItem().apply {
            val campaign = container.campaign as DynamicCampaign
            val requestedFields = container.requestedFields

            if (CampaignAnyFieldEnum.DYNAMIC_TEXT_CAMPAIGN_BIDDING_STRATEGY in requestedFields) {
                biddingStrategy = createDynamicTextCampaignGetStrategy(campaign)
            }

            if (CampaignAnyFieldEnum.DYNAMIC_TEXT_CAMPAIGN_ATTRIBUTION_MODEL in requestedFields) {
                attributionModel = campaign.attributionModel.toApiAttributionModel()
            }

            if (CampaignAnyFieldEnum.DYNAMIC_TEXT_CAMPAIGN_COUNTER_IDS in requestedFields) {
                counterIds = FACTORY.createDynamicTextCampaignBaseCounterIds(
                    campaign.metrikaCounters
                        ?.ifEmpty { null }
                        ?.toArrayOfInteger()
                )
            }

            if (CampaignAnyFieldEnum.DYNAMIC_TEXT_CAMPAIGN_SETTINGS in requestedFields) {
                settings = createDynamicTextCampaignSettings(
                    campaign,
                    container.clientUid,
                    container.advancedGeoTargeting,
                )
            }

            if (CampaignAnyFieldEnum.DYNAMIC_TEXT_CAMPAIGN_PLACEMENT_TYPES in requestedFields) {
                placementTypes = campaign.placementTypes.toApiPlacementTypesArray()
            }

            if (CampaignAnyFieldEnum.DYNAMIC_TEXT_CAMPAIGN_PRIORITY_GOALS in requestedFields) {
                priorityGoals = FACTORY.createDynamicTextCampaignGetItemPriorityGoals(
                    campaign.meaningfulGoals.toApiPriorityGoalsArray()
                )
            }

            if (CampaignAnyFieldEnum.DYNAMIC_TEXT_CAMPAIGN_STRATEGY_ID in requestedFields) {
                strategyId = campaign.strategyId
            }

            if (CampaignAnyFieldEnum.DYNAMIC_TEXT_CAMPAIGN_TRACKING_PARAMS in requestedFields) {
                trackingParams = FACTORY.createDynamicTextCampaignGetItemTrackingParams(
                    campaign.bannerHrefParams
                )
            }
        }

    private fun createMobileAppCampaignGetItem(
        container: GetCampaignsContainer,
    ): MobileAppCampaignGetItem =
        MobileAppCampaignGetItem().apply {
            val campaign = container.campaign as MobileContentCampaign
            val requestedFields = container.requestedFields

            if (CampaignAnyFieldEnum.MOBILE_APP_CAMPAIGN_BIDDING_STRATEGY in requestedFields) {
                biddingStrategy = createMobileAppCampaignGetStrategy(campaign)
            }

            if (CampaignAnyFieldEnum.MOBILE_APP_CAMPAIGN_SETTINGS in requestedFields) {
                settings = createMobileAppCampaignSettings(
                    campaign,
                    container.clientUid,
                    container.advancedGeoTargeting,
                )
            }

            if (CampaignAnyFieldEnum.MOBILE_APP_CAMPAIGN_STRATEGY_ID in requestedFields) {
                strategyId = campaign.strategyId
            }
        }

    private fun createSmartCampaignGetItem(
        container: GetCampaignsContainer,
    ): SmartCampaignGetItem =
        SmartCampaignGetItem().apply {
            val campaign = container.campaign as SmartCampaign
            val requestedFields = container.requestedFields

            if (CampaignAnyFieldEnum.SMART_CAMPAIGN_BIDDING_STRATEGY in requestedFields) {
                biddingStrategy = createSmartCampaignGetStrategy(campaign)
            }

            if (CampaignAnyFieldEnum.SMART_CAMPAIGN_ATTRIBUTION_MODEL in requestedFields) {
                attributionModel = campaign.attributionModel.toApiAttributionModel()
            }

            if (CampaignAnyFieldEnum.SMART_CAMPAIGN_COUNTER_ID in requestedFields) {
                counterId = campaign.metrikaCounters?.firstOrNull()
            }

            if (CampaignAnyFieldEnum.SMART_CAMPAIGN_SETTINGS in requestedFields) {
                settings = createSmartCampaignSettings(
                    campaign,
                    container.clientUid,
                    container.advancedGeoTargeting,
                )
            }

            if (CampaignAnyFieldEnum.SMART_CAMPAIGN_PRIORITY_GOALS in requestedFields) {
                priorityGoals = FACTORY.createSmartCampaignGetItemPriorityGoals(
                    campaign.meaningfulGoals.toApiPriorityGoalsArray()
                )
            }

            if (CampaignAnyFieldEnum.SMART_CAMPAIGN_STRATEGY_ID in requestedFields) {
                strategyId = campaign.strategyId
            }

            if (CampaignAnyFieldEnum.SMART_CAMPAIGN_TRACKING_PARAMS in requestedFields) {
                trackingParams = FACTORY.createSmartCampaignGetItemTrackingParams(
                    campaign.bannerHrefParams
                )
            }
        }

    private fun createTextCampaignGetItem(
        container: GetCampaignsContainer,
    ): TextCampaignGetItem =
        TextCampaignGetItem().apply {
            val campaign = container.campaign as TextCampaign
            val requestedFields = container.requestedFields

            if (CampaignAnyFieldEnum.TEXT_CAMPAIGN_BIDDING_STRATEGY in requestedFields) {
                biddingStrategy = createTextCampaignGetStrategy(campaign)
            }

            if (CampaignAnyFieldEnum.TEXT_CAMPAIGN_ATTRIBUTION_MODEL in requestedFields) {
                attributionModel = campaign.attributionModel.toApiAttributionModel()
            }

            if (CampaignAnyFieldEnum.TEXT_CAMPAIGN_COUNTER_IDS in requestedFields) {
                counterIds = FACTORY.createTextCampaignBaseCounterIds(
                    campaign.metrikaCounters
                        ?.ifEmpty { null }
                        ?.toArrayOfInteger()
                )
            }

            if (CampaignAnyFieldEnum.TEXT_CAMPAIGN_SETTINGS in requestedFields) {
                settings = createTextCampaignSettings(
                    campaign,
                    container.clientUid,
                    container.advancedGeoTargeting,
                )
            }

            if (CampaignAnyFieldEnum.TEXT_CAMPAIGN_PRIORITY_GOALS in requestedFields) {
                priorityGoals = FACTORY.createTextCampaignGetItemPriorityGoals(
                    campaign.meaningfulGoals?.toApiPriorityGoalsArray()
                )
            }

            if (CampaignAnyFieldEnum.TEXT_CAMPAIGN_RELEVANT_KEYWORDS in requestedFields) {
                relevantKeywords = FACTORY.createTextCampaignBaseRelevantKeywords(
                    campaign.broadMatch?.toApiRelevantKeywordsSetting()
                )
            }

            if (CampaignAnyFieldEnum.TEXT_CAMPAIGN_STRATEGY_ID in requestedFields) {
                strategyId = campaign.strategyId
            }

            if (CampaignAnyFieldEnum.TEXT_CAMPAIGN_TRACKING_PARAMS in requestedFields) {
                trackingParams = FACTORY.createTextCampaignGetItemTrackingParams(
                    campaign.bannerHrefParams
                )
            }
        }

    fun filterProperties(items: Collection<CampaignGetItem>, requestedFields: Set<CampaignAnyFieldEnum>) {
        val requestedFieldsByType = requestedFields.groupBy { it.apiValue?.let { x -> x::class } }

        val propertyNames = mutableListOf<String>()

        val cpmBannerCampaigns = items.mapNotNull { it.cpmBannerCampaign }
        if (cpmBannerCampaigns.isNotEmpty()) {
            val cpmBannerCampaignFields = requestedFieldsByType[CpmBannerCampaignFieldEnum::class]
                ?.map { it.apiValue as CpmBannerCampaignFieldEnum }
                ?.also { propertyNames += "cpmBannerCampaign" }
                ?: emptyList()

            cpmBannerCampaignFilter.filterProperties(cpmBannerCampaigns, cpmBannerCampaignFields)
        }

        val dynamicTextCampaigns = items.mapNotNull { it.dynamicTextCampaign }
        if (dynamicTextCampaigns.isNotEmpty()) {
            val dynamicTextCampaignFields = requestedFieldsByType[DynamicTextCampaignFieldEnum::class]
                ?.map { it.apiValue as DynamicTextCampaignFieldEnum }
                ?.also { propertyNames += "dynamicTextCampaign" }
                ?: emptyList()

            dynamicTextCampaignFilter.filterProperties(dynamicTextCampaigns, dynamicTextCampaignFields)
        }

        val mobileAppCampaigns = items.mapNotNull { it.mobileAppCampaign }
        if (mobileAppCampaigns.isNotEmpty()) {
            val mobileAppCampaignFields = requestedFieldsByType[MobileAppCampaignFieldEnum::class]
                ?.map { it.apiValue as MobileAppCampaignFieldEnum }
                ?.also { propertyNames += "mobileAppCampaign" }
                ?: emptyList()

            mobileAppCampaignFilter.filterProperties(mobileAppCampaigns, mobileAppCampaignFields)
        }

        val smartCampaigns = items.mapNotNull { it.smartCampaign }
        if (smartCampaigns.isNotEmpty()) {
            val smartCampaignFields = requestedFieldsByType[SmartCampaignFieldEnum::class]
                ?.map { it.apiValue as SmartCampaignFieldEnum }
                ?.also { propertyNames += "smartCampaign" }
                ?: emptyList()

            smartCampaignFilter.filterProperties(smartCampaigns, smartCampaignFields)
        }

        val textCampaigns = items.mapNotNull { it.textCampaign }
        if (textCampaigns.isNotEmpty()) {
            val textCampaignFields = requestedFieldsByType[TextCampaignFieldEnum::class]
                ?.map { it.apiValue as TextCampaignFieldEnum }
                ?.also { propertyNames += "textCampaign" }
                ?: emptyList()

            textCampaignFilter.filterProperties(textCampaigns, textCampaignFields)
        }

        val baseCampaignPropertyNames = requestedFieldsByType[CampaignFieldEnum::class]
            ?.map { baseCampaignFilter.enumToFieldMap.getValue(it.apiValue as CampaignFieldEnum) }
            .orEmpty()
        propertyNames += baseCampaignPropertyNames
        baseCampaignFilter.filterPropertiesByNames(items, propertyNames)
    }
}
