package ru.yandex.direct.api.v5.common;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Optional;

import javax.annotation.Nullable;

import com.yandex.direct.api.v5.general.PriorityEnum;
import com.yandex.direct.api.v5.general.StateEnum;
import com.yandex.direct.api.v5.general.YesNoEnum;

import ru.yandex.direct.api.v5.common.constants.GetRequestCommonConstants;
import ru.yandex.direct.multitype.entity.LimitOffset;
import ru.yandex.direct.validation.result.Path;
import ru.yandex.direct.validation.result.PathNode;

import static java.math.BigDecimal.ROUND_HALF_UP;
import static ru.yandex.direct.api.v5.ApiConstants.MONEY_MULTIPLIER;
import static ru.yandex.direct.api.v5.ApiConstants.MONEY_SCALE;
import static ru.yandex.direct.dbschema.ppc.Tables.BIDS;
import static ru.yandex.direct.utils.CommonUtils.ifNotNull;
import static ru.yandex.direct.validation.result.PathHelper.index;
import static ru.yandex.direct.validation.result.PathHelper.toPath;

/**
 * Конвертация основных общих единиц
 */
public class ConverterUtils {

    private static final int DB_PRICE_SCALE = BIDS.PRICE_CONTEXT.getDataType().scale();

    public static PriorityEnum convertStrategyPriority(@Nullable Integer autobudgetPriority) {
        if (autobudgetPriority == null) {
            return null;
        }
        switch (autobudgetPriority) {
            case 1:
                return PriorityEnum.LOW;
            case 3:
                return PriorityEnum.NORMAL;
            case 5:
                return PriorityEnum.HIGH;
            default:
                return PriorityEnum.NORMAL;
        }
    }

    public static Integer convertStrategyPriority(@Nullable PriorityEnum priority) {
        if (priority == null) {
            return null;
        }
        switch (priority) {
            case LOW:
                return 1;
            case NORMAL:
                return 3;
            case HIGH:
                return 5;
            default:
                throw new IllegalArgumentException("Unsupported enum value for PriorityEnum: " + priority);
        }
    }

    /**
     * Конвертирует цену из представления api в представление БД
     */
    public static BigDecimal convertToDbPrice(@Nullable Long priceInMicros) {
        return Optional.ofNullable(priceInMicros)
                .map(v -> BigDecimal.valueOf(v, MONEY_SCALE))
                .map(v -> v.setScale(DB_PRICE_SCALE, ROUND_HALF_UP))
                .orElse(null);
    }

    @Nullable
    public static Long convertToMicros(BigDecimal value) {
        return ifNotNull(value,
                v -> v.multiply(BigDecimal.valueOf(MONEY_MULTIPLIER)).setScale(0, ROUND_HALF_UP).longValue());
    }

    /**
     * Преобразует целочисленное число n * 1000_000 в n c двумя знаками после запятой
     * Если значение не кратно 10_000, последние 4 цифры обнуляются
     */
    @Nullable
    public static BigDecimal convertFromMicrosToDecimal(@Nullable Long valueInMicros) {
        return Optional.ofNullable(valueInMicros)
                .map(v -> BigDecimal.valueOf(v, 6))
                .map(v -> v.setScale(2, RoundingMode.DOWN))
                .orElse(null);
    }

    public static StateEnum convertState(Boolean isSuspended) {
        if (isSuspended == null) {
            return StateEnum.UNKNOWN;
        }
        if (isSuspended) {
            return StateEnum.SUSPENDED;
        } else {
            return StateEnum.ON;
        }
    }

    public static LimitOffset convertLimitOffset(@Nullable com.yandex.direct.api.v5.general.LimitOffset limitOffset) {
        return new LimitOffset(getLimit(limitOffset), getOffset(limitOffset));
    }

    public static int getLimit(@Nullable com.yandex.direct.api.v5.general.LimitOffset limitOffset) {
        Long limit = limitOffset != null ? limitOffset.getLimit() : null;
        return Optional.ofNullable(limit)
                .orElse(GetRequestCommonConstants.DEFAULT_LIMIT)
                .intValue();
    }

    public static int getOffset(@Nullable com.yandex.direct.api.v5.general.LimitOffset limitOffset) {
        Long offset = limitOffset != null ? limitOffset.getOffset() : null;
        return Optional.ofNullable(offset)
                .orElse(GetRequestCommonConstants.DEFAULT_OFFSET)
                .intValue();
    }

    public static Path convertArrayIndices(Path original) {
        return original.getNodes().stream().map(ConverterUtils::convertIndex).collect(toPath());
    }

    private static PathNode convertIndex(PathNode sourcePathNode) {
        return sourcePathNode instanceof PathNode.Index ?
                index(((PathNode.Index) sourcePathNode).getIndex() + 1) : sourcePathNode;
    }

    public static Long convertYesNoEnumToLong(YesNoEnum yesNoEnum) {
        return yesNoEnum == YesNoEnum.YES ? 1L : 0L;
    }

}
