package ru.yandex.direct.web.entity.uac.converter

import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.EnumSet
import ru.yandex.direct.core.entity.bidmodifier.BidModifier
import ru.yandex.direct.core.entity.campaign.CampaignUtils.getDisabledDomains
import ru.yandex.direct.core.entity.campaign.model.CampaignAttributionModel
import ru.yandex.direct.core.entity.campaign.model.CampaignMeasurer
import ru.yandex.direct.core.entity.campaign.model.CampaignMeasurerSystem
import ru.yandex.direct.core.entity.campaign.model.CampaignType
import ru.yandex.direct.core.entity.campaign.model.CampaignsAutobudget
import ru.yandex.direct.core.entity.campaign.model.CampaignsPlatform
import ru.yandex.direct.core.entity.campaign.model.CpmBannerCampaign
import ru.yandex.direct.core.entity.campaign.model.DayBudgetShowMode
import ru.yandex.direct.core.entity.campaign.model.DbStrategy
import ru.yandex.direct.core.entity.campaign.model.SmsFlag
import ru.yandex.direct.core.entity.campaign.model.StrategyData
import ru.yandex.direct.core.entity.campaign.model.StrategyName
import ru.yandex.direct.core.entity.campaign.service.validation.CampaignConstants
import ru.yandex.direct.core.entity.crypta.utils.CryptaSegmentBrandSafetyUtils.makeBrandSafetyCategories
import ru.yandex.direct.core.entity.sspplatform.repository.SspPlatformsRepository
import ru.yandex.direct.core.entity.uac.UacCommonUtils.getInventoryTypesBidModifier
import ru.yandex.direct.core.entity.uac.converter.UacBidModifiersConverter.toUacAdjustments
import ru.yandex.direct.core.entity.uac.converter.UacGrutCampaignConverter.toCampaignSpec
import ru.yandex.direct.core.entity.uac.model.AdvType
import ru.yandex.direct.core.entity.uac.model.DirectCampaignStatus
import ru.yandex.direct.core.entity.uac.model.UacCampaignOptions
import ru.yandex.direct.core.entity.uac.model.UacStrategy
import ru.yandex.direct.core.entity.uac.model.UacStrategyData
import ru.yandex.direct.core.entity.uac.model.UacStrategyName
import ru.yandex.direct.core.entity.uac.repository.ydb.UacYdbUtils.toEpochSecond
import ru.yandex.direct.core.entity.uac.repository.ydb.UacYdbUtils.toIdLong
import ru.yandex.direct.core.entity.uac.repository.ydb.model.UacYdbCampaign
import ru.yandex.direct.core.entity.uac.repository.ydb.model.UacYdbDirectCampaign
import ru.yandex.direct.core.entity.user.model.User
import ru.yandex.direct.grid.processing.service.campaign.converter.CommonCampaignConverter
import ru.yandex.direct.grid.processing.service.campaign.converter.type.CpmBannerCampaignConverterSupport
import ru.yandex.direct.grid.processing.service.campaign.uc.GdUcCampaignService
import ru.yandex.direct.libs.mirrortools.utils.HostingsHandler
import ru.yandex.direct.libs.timetarget.TimeTargetUtils
import ru.yandex.direct.model.ModelChanges
import ru.yandex.direct.web.entity.uac.converter.proto.enummappers.UacEnumMappers.Companion.toProtoCampaignType
import ru.yandex.direct.web.entity.uac.model.UacModifyCampaignDataContainer
import ru.yandex.grut.objects.proto.AssetLink.ELinkType
import ru.yandex.grut.objects.proto.AssetLink.TAssetLink
import ru.yandex.grut.objects.proto.AssetLink.TAssetLinkStatus
import ru.yandex.grut.objects.proto.AssetLink.TAssetLinkStatus.EAssetLinkStatus
import ru.yandex.grut.objects.proto.AssetLink.TAssetLinks
import ru.yandex.grut.objects.proto.AssetLink.TAssetLinksStatuses
import ru.yandex.grut.objects.proto.Campaign.ECampaignTypeOld
import ru.yandex.grut.objects.proto.Campaign.TCampaignSpec

object UacCampaignConverter {

    fun makeFaviconLink(campAdvType: AdvType, href: String): String? {
        return makeFaviconLink(toProtoCampaignType(campAdvType), href)
    }

    fun makeFaviconLink(campAdvType: ECampaignTypeOld, href: String): String? {
        return if (campAdvType == ECampaignTypeOld.CTO_TEXT || campAdvType == ECampaignTypeOld.CTO_CPM_BANNER)
            GdUcCampaignService.makeFaviconLink(href)
        else null
    }

    fun toUacYdbDirectCampaign(campaignId: String, directCampaignId: Long) = UacYdbDirectCampaign(
        id = campaignId,
        directCampaignId = directCampaignId,
        status = DirectCampaignStatus.DRAFT,
        syncedAt = LocalDateTime.now(),
        rejectReasons = null,
    )

    fun toUacYdbCampaign(
        modifyCampaignDataContainer: UacModifyCampaignDataContainer,
        accountId: String,
    ) = UacYdbCampaign(
        id = modifyCampaignDataContainer.id,
        name = modifyCampaignDataContainer.displayName,
        storeUrl = modifyCampaignDataContainer.href,
        appId = modifyCampaignDataContainer.appInfo?.id,
        regions = modifyCampaignDataContainer.regions,
        minusRegions = modifyCampaignDataContainer.minusRegions,
        trackingUrl = modifyCampaignDataContainer.trackingUrl?.getUrl(),
        impressionUrl = modifyCampaignDataContainer.impressionUrl?.getUrl(),
        createdAt = modifyCampaignDataContainer.createdTime,
        updatedAt = modifyCampaignDataContainer.updatedTime,
        startedAt = modifyCampaignDataContainer.startedTime,
        targetId = modifyCampaignDataContainer.targetId,
        cpa = modifyCampaignDataContainer.cpa,
        weekLimit = modifyCampaignDataContainer.weekLimit,
        account = accountId,
        options = modifyCampaignDataContainer.limitPeriod?.let { UacCampaignOptions(it) } ?: UacCampaignOptions(),
        skadNetworkEnabled = modifyCampaignDataContainer.skadNetworkEnabled,
        adultContentEnabled = modifyCampaignDataContainer.adultContentEnabled,
        targetStatus = modifyCampaignDataContainer.targetStatus,
        contentFlags = modifyCampaignDataContainer.contentFlags,
        advType = modifyCampaignDataContainer.advType,
        hyperGeoId = modifyCampaignDataContainer.hyperGeoId,
        keywords = modifyCampaignDataContainer.keywords,
        minusKeywords = modifyCampaignDataContainer.minusKeywords,
        socdem = modifyCampaignDataContainer.socdem,
        deviceTypes = modifyCampaignDataContainer.deviceTypes,
        inventoryTypes = modifyCampaignDataContainer.inventoryTypes,
        goals = modifyCampaignDataContainer.goals,
        counters = modifyCampaignDataContainer.counters,
        permalinkId = modifyCampaignDataContainer.permalinkId,
        phoneId = modifyCampaignDataContainer.phoneId,
        calltrackingSettingsId = modifyCampaignDataContainer.calltrackingSettingsId,
        timeTarget = modifyCampaignDataContainer.timeTarget,
        strategy = modifyCampaignDataContainer.strategy,
        retargetingCondition = modifyCampaignDataContainer.retargetingCondition,
        videosAreNonSkippable = modifyCampaignDataContainer.videosAreNonSkippable,
        zenPublisherId = modifyCampaignDataContainer.zenPublisherId,
        brandSurveyId = modifyCampaignDataContainer.brandSurveyId,
        briefSynced = true,
        showsFrequencyLimit = modifyCampaignDataContainer.showsFrequencyLimit,
        strategyPlatform = modifyCampaignDataContainer.strategyPlatform,
        isEcom = modifyCampaignDataContainer.isEcom,
        crr = modifyCampaignDataContainer.crr,
        feedId = modifyCampaignDataContainer.feedId,
        feedFilters = modifyCampaignDataContainer.feedFilters,
        trackingParams = modifyCampaignDataContainer.trackingParams,
        cpmAssets = modifyCampaignDataContainer.cpmAssets,
        adjustments = toUacAdjustments(modifyCampaignDataContainer.adjustments),
        campaignMeasurers = modifyCampaignDataContainer.campaignMeasurers,
        uacBrandsafety = modifyCampaignDataContainer.uacBrandsafety,
        uacDisabledPlaces = modifyCampaignDataContainer.uacDisabledPlaces,
        recommendationsManagementEnabled = modifyCampaignDataContainer.isRecommendationsManagementEnabled,
        priceRecommendationsManagementEnabled = modifyCampaignDataContainer.isPriceRecommendationsManagementEnabled,
        relevanceMatch = modifyCampaignDataContainer.relevanceMatch,
        showTitleAndBody = modifyCampaignDataContainer.showTitleAndBody,
        bizLandingId = modifyCampaignDataContainer.bizLandingId,
        audienceSegmentsSynchronized = true,
        searchLift = modifyCampaignDataContainer.searchLift,
    )

    data class AssetLinkInfo(
        val id: String,
        val assetId: String,
        val order: Int,
    )

    fun buildCampaignSpec(
        uacYdbCampaign: UacYdbCampaign,
        assetLinkInfos: List<AssetLinkInfo>,
    ) = TCampaignSpec.newBuilder(toCampaignSpec(uacYdbCampaign))
        .apply {
            briefAssetLinks = buildAssetLinks(assetLinkInfos, uacYdbCampaign.createdAt)
            briefAssetLinksStatuses = buildAssetLinksStatuses(assetLinkInfos)
            status = TCampaignSpec.ECampaignStatus.CS_DRAFT
        }.build()

    private fun buildAssetLinks(
        assetLinkInfos: List<AssetLinkInfo>,
        now: LocalDateTime,
    ): TAssetLinks {
        return TAssetLinks.newBuilder().apply {
            addAllLinks(assetLinkInfos.map { assetLinkInfo ->
                TAssetLink.newBuilder().apply {
                    id = assetLinkInfo.id.toIdLong()
                    assetId = assetLinkInfo.assetId.toIdLong()
                    linkType = ELinkType.LT_NOT_SPECIFIED
                    createTime = toEpochSecond(now).toInt()
                    order = assetLinkInfo.order
                }.build()
            })
        }.build()
    }

    private fun buildAssetLinksStatuses(
        assetLinkInfos: List<AssetLinkInfo>,
    ): TAssetLinksStatuses {
        return TAssetLinksStatuses.newBuilder().apply {
            addAllLinkStatuses(assetLinkInfos.map { assetLinkInfo ->
                TAssetLinkStatus.newBuilder().apply {
                    assetLinkId = assetLinkInfo.id.toIdLong()
                    assetId = assetLinkInfo.assetId.toIdLong()
                    linkType = ELinkType.LT_NOT_SPECIFIED
                    status = EAssetLinkStatus.ALS_CREATED
                }.build()
            })
        }.build()
    }

    fun toCpmBannerCampaign(
        createDataContainer: UacModifyCampaignDataContainer,
        subjectUser: User,
        sspPlatformsRepository: SspPlatformsRepository,
        hostingsHandler: HostingsHandler,
    ): CpmBannerCampaign {

        val isSearchLiftEnabled = !createDataContainer.searchLift?.searchObjects.isNullOrEmpty() ||
            !createDataContainer.searchLift?.brands.isNullOrEmpty()

        val measurers = createDataContainer.campaignMeasurers?.map {
            CampaignMeasurer()
                .withParams(it.params)
                .withMeasurerSystem(CampaignMeasurerSystem.valueOf(it.measurerType.name))
        }

        //Проходим по всем переданным запрещенным местам, если есть в базе площадка(без проверки регистра), то
        //добавляем ее в запрещенные площадки в формате, в котором она записана в базе, иначе — в домены
        val knownSsp: List<String> = sspPlatformsRepository.allSspPlatforms

        val disabledSsp = CommonCampaignConverter.getDisabledSsp(
            hostingsHandler, knownSsp,
            createDataContainer.uacDisabledPlaces?.disabledPlaces.orEmpty()
        )

        val disabledDomains = getDisabledDomains(
            knownSsp,
            createDataContainer.uacDisabledPlaces?.disabledPlaces.orEmpty()
        )

        val campaign = CpmBannerCampaign()
            .withImpressionRateCount(createDataContainer.showsFrequencyLimit?.impressionRateCount)
            .withImpressionRateIntervalDays(createDataContainer.showsFrequencyLimit?.impressionRateIntervalDays)
            .withType(CampaignType.CPM_BANNER)
            .withName(createDataContainer.displayName)
            .withStartDate(createDataContainer.startedTime?.toLocalDate())
            .withMetrikaCounters(createDataContainer.counters?.toMutableList()?.map { it.toLong() } ?: emptyList())
            .withRequireFiltrationByDontShowDomains(false)
            .withHasExtendedGeoTargeting(false)
            .withHasSiteMonitoring(true)
            .withHasAddMetrikaTagToUrl(false)
            .withHasAddOpenstatTagToUrl(false)
            .withAllowedPageIds(null)
            .withAllowedDomains(null)
            .withSmsTime(CampaignConstants.DEFAULT_SMS_TIME_INTERVAL)
            .withSmsFlags(EnumSet.noneOf(SmsFlag::class.java))
            .withEmail(subjectUser.email)
            .withWarningBalance(null)
            .withEnableSendAccountNews(false)
            .withEnablePausedByDayBudgetEvent(false)
            .withEnableOfflineStatNotice(false)
            .withIsServiceRequested(null)
            .withAbSegmentGoalIds(null)
            .withBrandSurveyId(createDataContainer.brandSurveyId)
            .withBrandSurveyName(createDataContainer.brandSurveyName)
            .withAttributionModel(CampaignAttributionModel.LAST_SIGNIFICANT_CLICK)
            .withStrategy(createDataContainer.strategy?.let { toCampaignStrategy(it) })
            .withDayBudget(
                CommonCampaignConverter.setScaleForCore(createDataContainer.strategy?.uacStrategyData?.budget)
                    ?: BigDecimal.ZERO
            )
            .withDayBudgetShowMode(DayBudgetShowMode.DEFAULT_)
            .withStartDate(LocalDate.now())
            .withHref(createDataContainer.href)
            .withTimeZoneId(TimeTargetUtils.DEFAULT_TIMEZONE)
            .withSource(createDataContainer.source)
            .withMeasurers(measurers.orEmpty())
            .withBrandSafetyCategories(createDataContainer.uacBrandsafety?.let {
                makeBrandSafetyCategories(
                    createDataContainer.uacBrandsafety.isEnabled,
                    createDataContainer.uacBrandsafety.additionalCategories
                )
            })
            .withDisabledDomains(disabledDomains)
            .withDisabledVideoPlacements(createDataContainer.uacDisabledPlaces?.disabledVideoAdsPlaces)
            .withDisabledIps(createDataContainer.uacDisabledPlaces?.disabledIps)
            .withDisallowedPageIds(createDataContainer.uacDisabledPlaces?.disallowedPageIds)
            .withDisabledSsp(disabledSsp)
            .withWidgetPartnerId(createDataContainer.widgetPartnerId)
            .withBidModifiers(toCampaignBidModifiers(createDataContainer))
            .withUseCurrentRegion(createDataContainer.useCurrentRegion)
            .withUseRegularRegion(createDataContainer.useRegularRegion)
            .withIsSearchLiftEnabled(isSearchLiftEnabled)
        return campaign;
    }

    private fun toCampaignStrategy(strategy: UacStrategy): DbStrategy {
        if (strategy.uacStrategyName == UacStrategyName.CPM_DEFAULT) {
            return CpmBannerCampaignConverterSupport.createDefaultStrategyData().apply {
                withPlatform(CampaignsPlatform.CONTEXT)
                withAutobudget(CampaignsAutobudget.NO)
            }
        }

        return DbStrategy().apply {
            withAutobudget(CampaignsAutobudget.YES)
            withPlatform(CampaignsPlatform.CONTEXT)
            withStrategy(null)
            withStrategyName(toCampaignStrategyName(strategy.uacStrategyName))
            withStrategyData(toCampaignStrategyData(strategy.uacStrategyData, strategy.uacStrategyName))
        }
    }

    private fun toCampaignStrategyName(gdStrategyName: UacStrategyName): StrategyName {
        return StrategyName.valueOf(gdStrategyName.name)
    }

    private fun toCampaignStrategyData(
        uacStrategyData: UacStrategyData,
        uacStrategyName: UacStrategyName
    ): StrategyData? {
        val strategyData = StrategyData().withVersion(1L)
            .withAvgCpm(CommonCampaignConverter.setScaleForCore(uacStrategyData.avgCpm))
            .withStart(uacStrategyData.startDate)
            .withFinish(uacStrategyData.finishDate)
            .withAvgCpv(CommonCampaignConverter.setScaleForCore(uacStrategyData.avgCpv))
            .withName(uacStrategyName.name.lowercase())

        if (uacStrategyName == UacStrategyName.AUTOBUDGET_MAX_REACH_CUSTOM_PERIOD
            || uacStrategyName == UacStrategyName.AUTOBUDGET_MAX_IMPRESSIONS_CUSTOM_PERIOD
            || uacStrategyName == UacStrategyName.AUTOBUDGET_AVG_CPV_CUSTOM_PERIOD
        ) {
            strategyData.withBudget(CommonCampaignConverter.setScaleForCore(uacStrategyData.sum))
            strategyData.withAutoProlongation(if (uacStrategyData.autoProlongation == true) 1L else 0L)
        } else {
            strategyData.withSum(CommonCampaignConverter.setScaleForCore(uacStrategyData.sum))
        }
        return strategyData
    }

     private fun toCampaignBidModifiers(createDataContainer: UacModifyCampaignDataContainer): List<BidModifier>? {
         if (createDataContainer.advType != AdvType.CPM_BANNER) {
             return null
         }
         if (createDataContainer!!.inventoryTypes != null &&
             !createDataContainer!!.inventoryTypes!!.isEmpty()) {
             val inventoryBM = getInventoryTypesBidModifier(createDataContainer!!.inventoryTypes)
             if (inventoryBM != null) {
                 return listOf(inventoryBM)
             }
         }
         return emptyList()
    }

    fun toCoreCpmCampaignModelChanges(
        campaign: CpmBannerCampaign,
    ): ModelChanges<CpmBannerCampaign> {

        val ch = ModelChanges(campaign.id, CpmBannerCampaign::class.java);

        with(ch) {
            process(campaign.name, CpmBannerCampaign.NAME)
            process(campaign.dayBudget, CpmBannerCampaign.DAY_BUDGET)
            process(campaign.strategy, CpmBannerCampaign.STRATEGY)
            process(campaign.brandSurveyId, CpmBannerCampaign.BRAND_SURVEY_ID)
            process(campaign.brandSurveyName, CpmBannerCampaign.BRAND_SURVEY_NAME)
            process(campaign.impressionRateCount, CpmBannerCampaign.IMPRESSION_RATE_COUNT)
            process(campaign.impressionRateIntervalDays, CpmBannerCampaign.IMPRESSION_RATE_INTERVAL_DAYS)
            process(campaign.measurers, CpmBannerCampaign.MEASURERS)
            process(campaign.brandSafetyCategories ?: emptyList(), CpmBannerCampaign.BRAND_SAFETY_CATEGORIES)
            process(campaign.disabledDomains, CpmBannerCampaign.DISABLED_DOMAINS)
            process(campaign.disabledVideoPlacements, CpmBannerCampaign.DISABLED_VIDEO_PLACEMENTS)
            process(campaign.disabledIps, CpmBannerCampaign.DISABLED_IPS)
            process(campaign.disallowedPageIds, CpmBannerCampaign.DISALLOWED_PAGE_IDS)
            process(campaign.disabledSsp, CpmBannerCampaign.DISABLED_SSP)
            process(campaign.bidModifiers, CpmBannerCampaign.BID_MODIFIERS)
            process(campaign.metrikaCounters, CpmBannerCampaign.METRIKA_COUNTERS)
        }

        return ch;
    }
}
