package de.votian.services.service;

import de.votian.services.config.ParameterKeys;
import jakarta.persistence.EntityManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@Transactional
public class JobTourServiceSyncService {

    private static final String SERVICE_PRICE_NAME = "Servicepreis";
    private static final String BASIC_PRICE_NAME = "Grundpreis";
    private static final String SELECT_SQL =
            "SELECT tr_sort, trs_srv_name, trs_price FROM tourservice WHERE jb_id = :jobId";
    private static final String UPDATE_SQL =
            "UPDATE tourservice SET trs_price = :newPrice " +
                    "WHERE jb_id = :jobId AND tr_sort = :trSort AND trs_srv_name = :serviceName AND trs_price = :oldPrice";

    private final ParameterService parameterService;
    private final EntityManager entityManager;

    public JobTourServiceSyncService(ParameterService parameterService,
                                     EntityManager entityManager) {
        this.parameterService = parameterService;
        this.entityManager = entityManager;
    }

    public void syncMarkupAdjustedPrices(Long jobId,
                                         Long headquartersId,
                                         BigDecimal previousMarkup,
                                         BigDecimal nextMarkup) {
        if (jobId == null || !hasMarkupChange(previousMarkup, nextMarkup)) {
            return;
        }

        BigDecimal previousFactor = markupFactor(previousMarkup);
        if (previousFactor.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        BigDecimal nextFactor = markupFactor(nextMarkup);
        boolean suppressBasicPriceMarkup = headquartersId != null
                && "1".equals(parameterService.getParameterValueWithFallback(
                        ParameterKeys.MASK_NO_MARKUP_ON_VEHICLE_PRICE,
                        headquartersId
                ));

        @SuppressWarnings("unchecked")
        List<Object[]> rows = entityManager.createNativeQuery(SELECT_SQL)
                .setParameter("jobId", jobId)
                .getResultList();

        for (Object[] row : rows) {
            Integer sort = toInteger(value(row, 0));
            String serviceName = asString(value(row, 1));
            BigDecimal price = toBigDecimal(value(row, 2));
            if (sort == null || serviceName == null || price == null) {
                continue;
            }
            if (shouldKeepPriceUnchanged(serviceName, suppressBasicPriceMarkup)) {
                continue;
            }

            BigDecimal recalculatedPrice = price
                    .divide(previousFactor, 6, RoundingMode.HALF_UP)
                    .multiply(nextFactor)
                    .setScale(2, RoundingMode.HALF_UP);
            if (recalculatedPrice.compareTo(price) == 0) {
                continue;
            }

            entityManager.createNativeQuery(UPDATE_SQL)
                    .setParameter("newPrice", recalculatedPrice)
                    .setParameter("jobId", jobId)
                    .setParameter("trSort", sort)
                    .setParameter("serviceName", serviceName)
                    .setParameter("oldPrice", price)
                    .executeUpdate();
        }
    }

    private boolean hasMarkupChange(BigDecimal previousMarkup, BigDecimal nextMarkup) {
        return money(previousMarkup).compareTo(money(nextMarkup)) != 0;
    }

    private boolean shouldKeepPriceUnchanged(String serviceName, boolean suppressBasicPriceMarkup) {
        String normalized = serviceName.trim();
        return SERVICE_PRICE_NAME.equals(normalized)
                || (suppressBasicPriceMarkup && BASIC_PRICE_NAME.equals(normalized));
    }

    private BigDecimal markupFactor(BigDecimal markup) {
        return BigDecimal.ONE.add(money(markup).divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP));
    }

    private BigDecimal money(BigDecimal value) {
        return value != null ? value : BigDecimal.ZERO;
    }

    private Object value(Object[] row, int index) {
        return row != null && index >= 0 && index < row.length ? row[index] : null;
    }

    private Integer toInteger(Object value) {
        if (value instanceof Number number) {
            return number.intValue();
        }
        if (value instanceof String string && !string.isBlank()) {
            try {
                return Integer.parseInt(string.trim());
            } catch (NumberFormatException ignored) {
                return null;
            }
        }
        return null;
    }

    private BigDecimal toBigDecimal(Object value) {
        if (value instanceof BigDecimal decimal) {
            return decimal;
        }
        if (value instanceof Number number) {
            return BigDecimal.valueOf(number.doubleValue()).setScale(2, RoundingMode.HALF_UP);
        }
        if (value instanceof String string && !string.isBlank()) {
            try {
                return new BigDecimal(string.trim().replace(",", ".")).setScale(2, RoundingMode.HALF_UP);
            } catch (NumberFormatException ignored) {
                return null;
            }
        }
        return null;
    }

    private String asString(Object value) {
        if (value == null) {
            return null;
        }
        String text = value.toString();
        return text.isBlank() ? null : text;
    }
}
