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

import java.math.BigDecimal;

import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.xml.bind.JAXBElement;

import com.yandex.direct.api.v5.campaigns.StrategyAverageCpa;
import com.yandex.direct.api.v5.campaigns.StrategyAverageCpaPerCampaign;
import com.yandex.direct.api.v5.campaigns.StrategyAverageCpaPerFilter;
import com.yandex.direct.api.v5.campaigns.StrategyAverageCpc;
import com.yandex.direct.api.v5.campaigns.StrategyAverageCpcPerCampaign;
import com.yandex.direct.api.v5.campaigns.StrategyAverageCpcPerFilter;
import com.yandex.direct.api.v5.campaigns.StrategyAverageCpi;
import com.yandex.direct.api.v5.campaigns.StrategyAverageCrr;
import com.yandex.direct.api.v5.campaigns.StrategyAverageRoi;
import com.yandex.direct.api.v5.campaigns.StrategyCpAverageCpv;
import com.yandex.direct.api.v5.campaigns.StrategyCpDecreasedPriceForRepeatedImpressions;
import com.yandex.direct.api.v5.campaigns.StrategyCpMaximumImpressions;
import com.yandex.direct.api.v5.campaigns.StrategyMaximumAppInstalls;
import com.yandex.direct.api.v5.campaigns.StrategyMaximumClicks;
import com.yandex.direct.api.v5.campaigns.StrategyMaximumConversionRate;
import com.yandex.direct.api.v5.campaigns.StrategyPayForConversion;
import com.yandex.direct.api.v5.campaigns.StrategyPayForConversionCrr;
import com.yandex.direct.api.v5.campaigns.StrategyPayForConversionPerCampaign;
import com.yandex.direct.api.v5.campaigns.StrategyPayForConversionPerFilter;
import com.yandex.direct.api.v5.campaigns.StrategyPayForInstall;
import com.yandex.direct.api.v5.campaigns.StrategyWbAverageCpv;
import com.yandex.direct.api.v5.campaigns.StrategyWbDecreasedPriceForRepeatedImpressions;
import com.yandex.direct.api.v5.campaigns.StrategyWbMaximumImpressions;
import com.yandex.direct.api.v5.campaigns.StrategyWeeklyClickPackage;

import ru.yandex.direct.core.entity.campaign.model.StrategyData;
import ru.yandex.direct.core.entity.campaign.model.StrategyName;

import static ru.yandex.direct.api.v5.common.ConverterUtils.convertFromMicrosToDecimal;
import static ru.yandex.direct.api.v5.common.ConverterUtils.convertToDbPrice;
import static ru.yandex.direct.api.v5.common.ConverterUtils.convertYesNoEnumToLong;
import static ru.yandex.direct.api.v5.entity.campaigns.converter.CampaignDataConverterKt.toDate;
import static ru.yandex.direct.core.entity.campaign.service.validation.CampaignConstants.DEFAULT_CPI_GOAL_ID;
import static ru.yandex.direct.utils.CommonUtils.ifNotNull;

@ParametersAreNonnullByDefault
public class StrategyUpdateConverter {

    private static final Long FORMAT_VERSION = 1L;

    private StrategyUpdateConverter() {
        // no instantiation
    }

    public static StrategyData fromAverageCpa(StrategyAverageCpa settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPA.name().toLowerCase())
                .withPayForConversion(false)
                .withAvgCpa(convertToDbPrice(settingsStructure.getAverageCpa()))
                .withGoalId(settingsStructure.getGoalId())
                .withSum(convertJaxbElementToDbPrice(settingsStructure.getWeeklySpendLimit()))
                .withBid(convertJaxbElementToDbPrice(settingsStructure.getBidCeiling()));
    }

    public static StrategyData fromAverageCpc(StrategyAverageCpc settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CLICK.name().toLowerCase())
                .withAvgBid(convertToDbPrice(settingsStructure.getAverageCpc()))
                .withSum(convertJaxbElementToDbPrice(settingsStructure.getWeeklySpendLimit()));
    }

    public static StrategyData fromAverageCrr(StrategyAverageCrr settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_CRR.name().toLowerCase())
                .withCrr(Long.valueOf(settingsStructure.getCrr()))
                .withSum(convertJaxbElementToDbPrice(settingsStructure.getWeeklySpendLimit()))
                .withGoalId(settingsStructure.getGoalId())
                .withPayForConversion(false);
    }

    public static StrategyData fromAverageCpi(StrategyAverageCpi settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPI.name().toLowerCase())
                .withAvgCpi(convertToDbPrice(settingsStructure.getAverageCpi()))
                .withSum(convertJaxbElementToDbPrice(settingsStructure.getWeeklySpendLimit()))
                .withBid(convertJaxbElementToDbPrice(settingsStructure.getBidCeiling()))
                .withPayForConversion(false);
    }

    public static StrategyData fromAverageRoi(StrategyAverageRoi settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_ROI.name().toLowerCase())
                .withReserveReturn(ifNotNull(settingsStructure.getReserveReturn(), Integer::longValue))
                .withRoiCoef(convertFromMicrosToDecimal(settingsStructure.getRoiCoef()))
                .withGoalId(settingsStructure.getGoalId())
                .withSum(convertJaxbElementToDbPrice(settingsStructure.getWeeklySpendLimit()))
                .withBid(convertJaxbElementToDbPrice(settingsStructure.getBidCeiling()))
                .withProfitability(convertJaxbElementFromMicrosToDecimal(settingsStructure.getProfitability()));
    }

    public static StrategyData fromPayForConversion(StrategyPayForConversion settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPA.name().toLowerCase())
                .withPayForConversion(true)
                .withAvgCpa(convertToDbPrice(settingsStructure.getCpa()))
                .withGoalId(settingsStructure.getGoalId())
                .withSum(convertJaxbElementToDbPrice(settingsStructure.getWeeklySpendLimit()));
    }

    public static StrategyData fromPayForConversionCrr(StrategyPayForConversionCrr settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_CRR.name().toLowerCase())
                .withPayForConversion(true)
                .withCrr(Long.valueOf(settingsStructure.getCrr()))
                .withGoalId(settingsStructure.getGoalId())
                .withSum(convertJaxbElementToDbPrice(settingsStructure.getWeeklySpendLimit()));
    }

    public static StrategyData fromWbMaximumClicks(StrategyMaximumClicks settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET.name().toLowerCase())
                .withSum(convertToDbPrice(settingsStructure.getWeeklySpendLimit()))
                .withBid(convertJaxbElementToDbPrice(settingsStructure.getBidCeiling()));
    }

    public static StrategyData fromWbMaximumConversionRate(StrategyMaximumConversionRate settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET.name().toLowerCase())
                .withSum(convertToDbPrice(settingsStructure.getWeeklySpendLimit()))
                .withBid(convertJaxbElementToDbPrice(settingsStructure.getBidCeiling()))
                .withGoalId(settingsStructure.getGoalId());
    }

    public static StrategyData fromWbMaximumAppInstalls(StrategyMaximumAppInstalls settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET.name().toLowerCase())
                .withSum(convertToDbPrice(settingsStructure.getWeeklySpendLimit()))
                .withBid(convertJaxbElementToDbPrice(settingsStructure.getBidCeiling()))
                .withGoalId(DEFAULT_CPI_GOAL_ID);
    }

    public static StrategyData fromWeeklyClickPackage(StrategyWeeklyClickPackage settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_WEEK_BUNDLE.name().toLowerCase())
                .withLimitClicks(settingsStructure.getClicksPerWeek())
                .withAvgBid(convertJaxbElementToDbPrice(settingsStructure.getAverageCpc()))
                .withBid(convertJaxbElementToDbPrice(settingsStructure.getBidCeiling()));
    }

    // https://a.yandex-team.ru/arc_vcs/direct/perl/protected/Direct/Strategy/AutobudgetMaxReach.pm
    public static StrategyData fromWbDecreasedPriceForRepeatedImpressions(
            StrategyWbDecreasedPriceForRepeatedImpressions wbDecreasedPriceForRepeatedImpressions) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_MAX_REACH.name().toLowerCase())
                .withAvgCpm(convertToDbPrice(wbDecreasedPriceForRepeatedImpressions.getAverageCpm()))
                .withSum(convertToDbPrice(wbDecreasedPriceForRepeatedImpressions.getSpendLimit()));
    }

    public static StrategyData fromCpDecreasedPriceForRepeatedImpressions(
            StrategyCpDecreasedPriceForRepeatedImpressions cpDecreasedPriceForRepeatedImpressions) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_MAX_REACH_CUSTOM_PERIOD.name().toLowerCase())
                .withStart(toDate(cpDecreasedPriceForRepeatedImpressions.getStartDate()))
                .withFinish(toDate(cpDecreasedPriceForRepeatedImpressions.getEndDate()))
                .withAutoProlongation(convertYesNoEnumToLong(
                        cpDecreasedPriceForRepeatedImpressions.getAutoContinue()))
                .withBudget(convertToDbPrice(cpDecreasedPriceForRepeatedImpressions.getSpendLimit()))
                .withAvgCpm(convertToDbPrice(cpDecreasedPriceForRepeatedImpressions.getAverageCpm()));
        // dailyChangeCount - проставлять не нужно, т.к. системное поле
    }

    public static StrategyData fromWbMaximumImpressions(StrategyWbMaximumImpressions wbMaximumImpressions) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_MAX_IMPRESSIONS.name().toLowerCase())
                .withAvgCpm(convertToDbPrice(wbMaximumImpressions.getAverageCpm()))
                .withSum(convertToDbPrice(wbMaximumImpressions.getSpendLimit()));
    }

    public static StrategyData fromCpMaximumImpressions(StrategyCpMaximumImpressions cpMaximumImpressions) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_MAX_IMPRESSIONS_CUSTOM_PERIOD.name().toLowerCase())
                .withStart(toDate(cpMaximumImpressions.getStartDate()))
                .withFinish(toDate(cpMaximumImpressions.getEndDate()))
                .withBudget(convertToDbPrice(cpMaximumImpressions.getSpendLimit()))
                .withAvgCpm(convertToDbPrice(cpMaximumImpressions.getAverageCpm()))
                .withAutoProlongation(convertYesNoEnumToLong(cpMaximumImpressions.getAutoContinue()));
    }

    public static StrategyData fromWbAverageCpv(StrategyWbAverageCpv wbAverageCpv) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPV.name().toLowerCase())
                .withSum(convertToDbPrice(wbAverageCpv.getSpendLimit()))
                .withAvgCpv(convertToDbPrice(wbAverageCpv.getAverageCpv()))
                .withPayForConversion(true);
    }

    public static StrategyData fromCpAverageCpv(StrategyCpAverageCpv cpAverageCpv) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPV_CUSTOM_PERIOD.name().toLowerCase())
                .withStart(toDate(cpAverageCpv.getStartDate()))
                .withFinish(toDate(cpAverageCpv.getEndDate()))
                .withBudget(convertToDbPrice(cpAverageCpv.getSpendLimit()))
                .withAvgCpv(convertToDbPrice(cpAverageCpv.getAverageCpv()))
                .withAutoProlongation(convertYesNoEnumToLong(cpAverageCpv.getAutoContinue()))
                .withPayForConversion(true);
    }

    public static StrategyData fromAverageCpcPerCampaign(StrategyAverageCpcPerCampaign averageCpcPerCampaign) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPC_PER_CAMP.name().toLowerCase())
                .withAvgBid(convertToDbPrice(averageCpcPerCampaign.getAverageCpc()))
                .withBid(convertJaxbElementToDbPrice(averageCpcPerCampaign.getBidCeiling()))
                .withSum(convertJaxbElementToDbPrice(averageCpcPerCampaign.getWeeklySpendLimit()));
    }

    public static StrategyData fromAverageCpcPerFilter(StrategyAverageCpcPerFilter averageCpcPerFilter) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPC_PER_FILTER.name().toLowerCase())
                .withFilterAvgBid(convertJaxbElementToDbPrice(averageCpcPerFilter.getFilterAverageCpc()))
                .withBid(convertJaxbElementToDbPrice(averageCpcPerFilter.getBidCeiling()))
                .withSum(convertJaxbElementToDbPrice(averageCpcPerFilter.getWeeklySpendLimit()));
    }

    public static StrategyData fromAverageCpaPerCampaign(StrategyAverageCpaPerCampaign averageCpaPerCampaign) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPA_PER_CAMP.name().toLowerCase())
                .withAvgCpa(convertToDbPrice(averageCpaPerCampaign.getAverageCpa()))
                .withGoalId(averageCpaPerCampaign.getGoalId())
                .withPayForConversion(false)
                .withBid(convertJaxbElementToDbPrice(averageCpaPerCampaign.getBidCeiling()))
                .withSum(convertJaxbElementToDbPrice(averageCpaPerCampaign.getWeeklySpendLimit()));
    }

    public static StrategyData fromAverageCpaPerFilter(StrategyAverageCpaPerFilter averageCpaPerFilter) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPA_PER_FILTER.name().toLowerCase())
                .withFilterAvgCpa(convertToDbPrice(averageCpaPerFilter.getFilterAverageCpa()))
                .withGoalId(averageCpaPerFilter.getGoalId())
                .withPayForConversion(false)
                .withBid(convertJaxbElementToDbPrice(averageCpaPerFilter.getBidCeiling()))
                .withSum(convertJaxbElementToDbPrice(averageCpaPerFilter.getWeeklySpendLimit()));
    }

    public static StrategyData fromPayForConversionPerCampaign(
            StrategyPayForConversionPerCampaign payForConversionPerCampaign) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPA_PER_CAMP.name().toLowerCase())
                .withAvgCpa(convertToDbPrice(payForConversionPerCampaign.getCpa()))
                .withGoalId(payForConversionPerCampaign.getGoalId())
                .withPayForConversion(true)
                .withSum(convertJaxbElementToDbPrice(payForConversionPerCampaign.getWeeklySpendLimit()));
    }

    public static StrategyData fromPayForConversionPerFilter(
            StrategyPayForConversionPerFilter payForConversionPerFilter) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPA_PER_FILTER.name().toLowerCase())
                .withFilterAvgCpa(convertToDbPrice(payForConversionPerFilter.getCpa()))
                .withGoalId(payForConversionPerFilter.getGoalId())
                .withPayForConversion(true)
                .withSum(convertJaxbElementToDbPrice(payForConversionPerFilter.getWeeklySpendLimit()));
    }

    public static StrategyData fromPayForInstall(StrategyPayForInstall settingsStructure) {
        return new StrategyData()
                .withVersion(FORMAT_VERSION)
                .withName(StrategyName.AUTOBUDGET_AVG_CPI.name().toLowerCase())
                .withPayForConversion(true)
                .withAvgCpi(convertToDbPrice(settingsStructure.getAverageCpi()))
                .withSum(convertJaxbElementToDbPrice(settingsStructure.getWeeklySpendLimit()));
    }

    public static BigDecimal convertJaxbElementToDbPrice(@Nullable JAXBElement<Long> apiPrice) {
        if (apiPrice == null || apiPrice.isNil()) {
            return null;
        } else {
            return convertToDbPrice(apiPrice.getValue());
        }
    }

    public static BigDecimal convertJaxbElementFromMicrosToDecimal(@Nullable JAXBElement<Long> apiMicros) {
        if (apiMicros == null || apiMicros.isNil()) {
            return null;
        } else {
            return convertFromMicrosToDecimal(apiMicros.getValue());
        }
    }
}
