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

import java.util.List;
import java.util.Objects;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

import com.yandex.direct.api.v5.campaignsext.AddRequest;
import com.yandex.direct.api.v5.campaignsext.AddResponse;
import one.util.streamex.EntryStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import ru.yandex.direct.api.v5.common.ApiPathConverter;
import ru.yandex.direct.api.v5.converter.ResultConverter;
import ru.yandex.direct.api.v5.entity.OperationOnListDelegateWithContainer;
import ru.yandex.direct.api.v5.entity.campaigns.container.AddCampaignsDelegateContainer;
import ru.yandex.direct.api.v5.entity.campaigns.validation.CampaignsApiValidationSignals;
import ru.yandex.direct.api.v5.entity.campaignsext.converter.CampaignsExtAddRequestConverter;
import ru.yandex.direct.api.v5.entity.campaignsext.validation.CampaignsExtRequestValidator;
import ru.yandex.direct.api.v5.result.ApiMassResult;
import ru.yandex.direct.api.v5.result.ApiResult;
import ru.yandex.direct.api.v5.security.ApiAuthenticationSource;
import ru.yandex.direct.api.v5.validation.DefectType;
import ru.yandex.direct.common.db.PpcPropertiesSupport;
import ru.yandex.direct.core.entity.campaign.model.BaseCampaign;
import ru.yandex.direct.core.entity.campaign.service.CampaignOperationService;
import ru.yandex.direct.core.entity.campaign.service.CampaignOptions;
import ru.yandex.direct.core.entity.feature.service.FeatureService;
import ru.yandex.direct.dbutil.model.UidAndClientId;
import ru.yandex.direct.result.MassResult;
import ru.yandex.direct.validation.result.ValidationResult;

import static ru.yandex.direct.api.v5.entity.campaigns.CampaignsEndpoint.DEFECT_PRESENTATIONS_RESPECTING_PATH;
import static ru.yandex.direct.utils.FunctionalUtils.mapList;

@Service
@ParametersAreNonnullByDefault
public class AddCampaignsExtDelegate extends OperationOnListDelegateWithContainer
        <AddRequest, AddResponse, BaseCampaign, Long, AddCampaignsDelegateContainer> {

    private static final Logger logger =
            LoggerFactory.getLogger(AddCampaignsExtDelegate.class);

    private final CampaignOperationService campaignOperationService;
    private final CampaignsExtAddRequestConverter requestConverter;
    private final ResultConverter resultConverter;

    @Autowired
    public AddCampaignsExtDelegate(
            ApiAuthenticationSource auth,
            CampaignOperationService campaignOperationService,
            CampaignsExtAddRequestConverter requestConverter,
            ResultConverter resultConverter,
            PpcPropertiesSupport ppcPropertiesSupport,
            FeatureService featureService) {
        super(ApiPathConverter.forCampaigns(), auth, ppcPropertiesSupport, featureService);
        this.campaignOperationService = campaignOperationService;
        this.requestConverter = requestConverter;
        this.resultConverter = resultConverter;
    }

    @Nullable
    @Override
    public ValidationResult<AddRequest, DefectType> validateRequest(AddRequest externalRequest) {
        return CampaignsExtRequestValidator.validateExternalRequest(externalRequest);
    }

    @Override
    public List<BaseCampaign> convertRequest(AddRequest externalRequest) {
        Long chiefUid = auth.getChiefSubclient().getUid();
        return requestConverter.convert(chiefUid, externalRequest);
    }

    @Override
    protected AddCampaignsDelegateContainer createContainer() {
        return new AddCampaignsDelegateContainer();
    }

    @Nonnull
    @Override
    public ValidationResult<List<BaseCampaign>, DefectType> validateInternalRequest(
            List<BaseCampaign> internalRequest,
            AddCampaignsDelegateContainer container) {

        var vr = CampaignsExtRequestValidator.validateInternalRequest(internalRequest);

        EntryStream.zip(vr.getValue(), internalRequest)
                // если null, значит мы не конвертировали кампанию в ядровую, потому что в ней есть ошибки уровня api
                .filterKeys(Objects::nonNull)
                .mapValues(CampaignsApiValidationSignals.CampaignWrapper.class::cast)
                .forKeyValue((vrCampaign, campaignWrapper) -> {
                    var requireServicing = campaignWrapper.getRequireServicing();
                    if (requireServicing != null) {
                        container.addRequireServicing(vrCampaign, requireServicing);
                    }
                });

        return vr;
    }

    @Override
    public ApiMassResult<Long> processList(
            List<BaseCampaign> validItems,
            AddCampaignsDelegateContainer container) {
        logger.debug("process request: {}", validItems);

        Long operatorUid = auth.getOperator().getUid();
        UidAndClientId uidAndClientId = auth.getChiefSubclient().toUidAndClientId();

        List<Boolean> requireServicing = mapList(validItems, container::getRequireServicing);

        CampaignOptions options = new CampaignOptions.Builder()
                .withForceCheckPositionIntervalEvent(true)
                .withForceEnableCheckPositionEvent(true)
                .withAllowNullDeviceTypeTargeting(true)
                .withAllowNullNetworkTargeting(true)
                .withAllowZeroMobileAppId(true)
                .withRequireServicing(requireServicing)
                .withLimitFioTo255Chars(true)
                .withValidateFioForForbiddenChars(true)
                .withValidateCampaignNameForForbiddenChars(true)
                .withForceValidateAvgCpmMin(true)
                .withSendAutoServicingMailNotification(true)
                .withValidatePayForConversionExtendedModeEnabled(true)
                .withValidatePayForConversionInAutobudgetAvgCpaPerFilter(true)
                .withCheckDisableAllGoalsOptimizationForDnaFeature(true)
                .withFetchMetrikaCountersForGoals(true)
                .build();
        MassResult<Long> result = campaignOperationService
                .createRestrictedCampaignAddOperation(validItems, operatorUid, uidAndClientId, options)
                .prepareAndApply();

        return resultConverter.toApiMassResultRespectingPath(result, DEFECT_PRESENTATIONS_RESPECTING_PATH);
    }

    @Override
    public AddResponse convertResponse(ApiResult<List<ApiResult<Long>>> result) {
        return new AddResponse().withAddResults(resultConverter.toActionResults(result, apiPathConverter));
    }
}
