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

import java.util.List;
import java.util.Set;

import javax.annotation.ParametersAreNonnullByDefault;

import com.yandex.direct.api.v5.keywordbids.KeywordBidSetAutoItem;
import com.yandex.direct.api.v5.keywordbids.SetAutoRequest;
import com.yandex.direct.api.v5.keywordbids.SetAutoResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.direct.api.v5.common.ApiPathConverter;
import ru.yandex.direct.api.v5.converter.ResultConverter;
import ru.yandex.direct.api.v5.entity.OperationOnListDelegate;
import ru.yandex.direct.api.v5.entity.bids.service.validation.SetAutoBidsDefectPresentations;
import ru.yandex.direct.api.v5.entity.keywordbids.converter.SetAutoKeywordBidsConverter;
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.core.entity.bids.container.SetAutoBidItem;
import ru.yandex.direct.core.entity.bids.service.BidService;
import ru.yandex.direct.core.units.OperationSummary;
import ru.yandex.direct.dbutil.model.ClientId;
import ru.yandex.direct.result.MassResult;
import ru.yandex.direct.validation.builder.ItemValidationBuilder;
import ru.yandex.direct.validation.builder.When;
import ru.yandex.direct.validation.result.ValidationResult;

import static ru.yandex.direct.api.v5.entity.OperationOnListUtils.notCountErrorsWithCodes;
import static ru.yandex.direct.api.v5.entity.bids.Constants.MAX_BID_ADGROUPIDS_PER_REQUEST;
import static ru.yandex.direct.api.v5.entity.bids.Constants.MAX_BID_CAMPAIGNIDS_PER_REQUEST;
import static ru.yandex.direct.api.v5.entity.bids.Constants.MAX_BID_IDS_PER_REQUEST;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidChangeNotAllowed;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.fieldDoesNotMatchStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.maxAdGroupsBidsPerRequest;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.maxCampBidsPerRequest;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.maxKeywordBidsPerRequest;
import static ru.yandex.direct.api.v5.validation.SetBidsConstraints.bidsListSizeMaxLimit;
import static ru.yandex.direct.api.v5.validation.constraints.Constraints.eachNotNull;

@ParametersAreNonnullByDefault
@Component
public class SetAutoKeywordBidsDelegate
        extends OperationOnListDelegate<SetAutoRequest, SetAutoResponse, SetAutoBidItem, SetAutoBidItem> {
    private static final Set<Integer> NO_WITHDRAWAL_UNITS_ERROR_CODES = Set.of(fieldDoesNotMatchStrategy().getCode(),
            bidChangeNotAllowed().getCode());

    private final BidService bidService;
    private final SetAutoKeywordBidsConverter bidsConverter;
    private final ResultConverter resultConverter;

    @Autowired
    public SetAutoKeywordBidsDelegate(
            ApiAuthenticationSource auth,
            BidService bidService,
            SetAutoKeywordBidsConverter bidsConverter,
            ResultConverter resultConverter) {
        super(ApiPathConverter.forKeywordBidsSetAuto(), auth);
        this.bidService = bidService;
        this.bidsConverter = bidsConverter;
        this.resultConverter = resultConverter;
    }

    @Override
    public ValidationResult<SetAutoRequest, DefectType> validateRequest(SetAutoRequest externalRequest) {
        ItemValidationBuilder<SetAutoRequest, DefectType> vb = ItemValidationBuilder.of(externalRequest);
        vb.item(externalRequest.getKeywordBids(), "KeywordBids")
                .check(eachNotNull())
                .check(bidsListSizeMaxLimit(
                        KeywordBidSetAutoItem::getKeywordId, MAX_BID_IDS_PER_REQUEST, t -> maxKeywordBidsPerRequest(),
                        KeywordBidSetAutoItem::getAdGroupId, MAX_BID_ADGROUPIDS_PER_REQUEST, t -> maxAdGroupsBidsPerRequest(),
                        KeywordBidSetAutoItem::getCampaignId, MAX_BID_CAMPAIGNIDS_PER_REQUEST, t -> maxCampBidsPerRequest()), When.isValid());

        return vb.getResult();
    }

    @Override
    public List<SetAutoBidItem> convertRequest(SetAutoRequest externalRequest) {
        return bidsConverter.convertFromSetAutoRequest(externalRequest);
    }

    @Override
    public ApiMassResult<SetAutoBidItem> processList(List<SetAutoBidItem> internalRequest) {
        ClientId clientId = auth.getChiefSubclient().getClientId();
        long operatorUid = auth.getOperator().getUid();
        MassResult<SetAutoBidItem> setAutoResult = bidService.setAutoBids(clientId, operatorUid, internalRequest, true);
        return resultConverter.toApiMassResult(setAutoResult, SetAutoBidsDefectPresentations.HOLDER);
    }

    @Override
    public SetAutoResponse convertResponse(ApiResult<List<ApiResult<SetAutoBidItem>>> result) {
        return bidsConverter.convertToSetAutoResponse(result, apiPathConverter);
    }

    /**
     * Не вычитаем баллы в случае, когда невозможно указать ставку из-за несоответствия стратегии.
     */
    @Override
    public OperationSummary correctOperationSummary(List<SetAutoBidItem> internalRequest,
                                                    ApiResult<List<ApiResult<SetAutoBidItem>>> apiResult) {
        return notCountErrorsWithCodes(apiResult, NO_WITHDRAWAL_UNITS_ERROR_CODES);
    }
}
