package ru.yandex.direct.api.v5.entity.campaignsext.delegate

import com.yandex.direct.api.v5.campaignsext.CampaignStateEnum
import com.yandex.direct.api.v5.campaignsext.CampaignTypeEnum
import com.yandex.direct.api.v5.campaignsext.CampaignsSelectionCriteria
import com.yandex.direct.api.v5.campaignsext.GetRequest
import com.yandex.direct.api.v5.campaignsext.GetResponse
import org.springframework.stereotype.Service
import ru.yandex.direct.api.v5.common.ApiPathConverter
import ru.yandex.direct.api.v5.entity.GenericGetRequest
import ru.yandex.direct.api.v5.entity.GetApiServiceDelegate
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.container.toAnyFieldEnum
import ru.yandex.direct.api.v5.entity.campaigns.service.CampaignDataFetcher
import ru.yandex.direct.api.v5.entity.campaignsext.converter.CampaignsExtGetResponseConverter
import ru.yandex.direct.api.v5.entity.campaignsext.validation.CampaignsExtGetRequestValidator
import ru.yandex.direct.api.v5.security.ApiAuthenticationSource
import ru.yandex.direct.api.v5.validation.DefectType
import ru.yandex.direct.core.entity.campaign.container.ApiCampaignsSelectionCriteria
import ru.yandex.direct.core.entity.campaign.model.CampaignType
import ru.yandex.direct.core.entity.campaign.model.CampaignTypeKinds
import ru.yandex.direct.utils.mapToSet
import ru.yandex.direct.validation.result.ValidationResult

@Service
class GetCampaignsExtDelegate(
    apiAuthenticationSource: ApiAuthenticationSource,
    private val getResponseConverter: CampaignsExtGetResponseConverter,
    private val campaignDataFetcher: CampaignDataFetcher,
) : GetApiServiceDelegate<GetRequest, GetResponse, CampaignAnyFieldEnum, ApiCampaignsSelectionCriteria, GetCampaignsContainer>(
    ApiPathConverter.forCampaigns(),
    apiAuthenticationSource,
) {

    override fun validateRequest(
        request: GetRequest,
    ): ValidationResult<GetRequest, DefectType> {
        return CampaignsExtGetRequestValidator.validateRequest(request)
    }

    override fun extractFieldNames(externalRequest: GetRequest): Set<CampaignAnyFieldEnum> {
        return with(externalRequest) {
            buildSet {
                fieldNames.mapTo(this) { it.toAnyFieldEnum() }
                contentPromotionCampaignFieldNames.mapTo(this) { it.toAnyFieldEnum() }
                cpmBannerCampaignFieldNames.mapTo(this) { it.toAnyFieldEnum() }
                dynamicTextCampaignFieldNames.mapTo(this) { it.toAnyFieldEnum() }
                mobileAppCampaignFieldNames.mapTo(this) { it.toAnyFieldEnum() }
                smartCampaignFieldNames.mapTo(this) { it.toAnyFieldEnum() }
                textCampaignFieldNames.mapTo(this) { it.toAnyFieldEnum() }
            }
        }
    }

    override fun extractSelectionCriteria(externalRequest: GetRequest): ApiCampaignsSelectionCriteria {
        val selectionCriteria = externalRequest.selectionCriteria
            ?: CampaignsSelectionCriteria()

        return ApiCampaignsSelectionCriteria(
            ids = selectionCriteria.ids,
            types = selectionCriteria.types.mapToSet { it.toCoreType() }
                .ifEmpty { CampaignTypeKinds.API5_EDIT_CAMPAIGNS_EXT },
            states = selectionCriteria.states.mapToSet { enumValueOf(it.name) },
            statuses = selectionCriteria.statuses.mapToSet { enumValueOf(it.name) },
            paymentStatuses = selectionCriteria.statusesPayment.mapToSet { enumValueOf(it.name) },
            withoutConvertedCampaigns = selectionCriteria.ids.isEmpty()
                && CampaignStateEnum.CONVERTED !in selectionCriteria.states,
        )
    }

    private fun CampaignTypeEnum.toCoreType(): CampaignType =
        when (this) {
            CampaignTypeEnum.CONTENT_PROMOTION_CAMPAIGN -> CampaignType.CONTENT_PROMOTION
            CampaignTypeEnum.CPM_BANNER_CAMPAIGN -> CampaignType.CPM_BANNER
            CampaignTypeEnum.DYNAMIC_TEXT_CAMPAIGN -> CampaignType.DYNAMIC
            CampaignTypeEnum.MOBILE_APP_CAMPAIGN -> CampaignType.MOBILE_CONTENT
            CampaignTypeEnum.SMART_CAMPAIGN -> CampaignType.PERFORMANCE
            CampaignTypeEnum.TEXT_CAMPAIGN -> CampaignType.TEXT
        }

    override fun get(
        getRequest: GenericGetRequest<CampaignAnyFieldEnum, ApiCampaignsSelectionCriteria>,
    ): List<GetCampaignsContainer> {
        return campaignDataFetcher.getData(
            selectionCriteria = getRequest.selectionCriteria,
            requestedFields = getRequest.requestedFields,
            limitOffset = getRequest.limitOffset,
        )
    }

    override fun convertGetResponse(
        result: List<GetCampaignsContainer>,
        requestedFields: Set<CampaignAnyFieldEnum>,
        limitedBy: Long?,
    ): GetResponse {
        return GetResponse().apply {
            this.limitedBy = limitedBy
            campaigns = getResponseConverter
                .massConvertResponseItems(result)
                .values
                .toList()
        }
    }
}
