package de.votian.services.service;

import de.votian.services.dto.JobTourServiceRowDto;
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.ArrayList;
import java.util.List;

@Service
@Transactional(readOnly = true)
public class JobTourServiceReadService {

    private static final String PLACEHOLDER_FILTER_SQL =
            "AND NOT (trs.trs_srvt_name = 'z' AND trs.trs_price = 0) ";
    private static final String SELECT_SQL =
            "SELECT trs.csc_id, csc.csc_name, trs.tr_sort, trs.trs_srv_name, trs.trs_srvt_name, trs.trs_price, trs.trs_discount " +
                    "FROM tourservice trs " +
                    "LEFT JOIN costcenter csc ON trs.csc_id = csc.csc_id " +
                    "WHERE trs.jb_id = :jobId " +
                    PLACEHOLDER_FILTER_SQL +
                    "ORDER BY trs.csc_id, trs.tr_sort, trs.trs_srv_name, trs.trs_srvt_name";
    private static final String SELECT_BY_JOB_IDS_SQL =
            "SELECT trs.jb_id, trs.csc_id, csc.csc_name, trs.tr_sort, trs.trs_srv_name, trs.trs_srvt_name, trs.trs_price, trs.trs_discount " +
                    "FROM tourservice trs " +
                    "LEFT JOIN costcenter csc ON trs.csc_id = csc.csc_id " +
                    "WHERE trs.jb_id IN (:jobIds) " +
                    PLACEHOLDER_FILTER_SQL +
                    "ORDER BY trs.jb_id, trs.csc_id, trs.tr_sort, trs.trs_srv_name, trs.trs_srvt_name";
    private static final String SELECT_BUSINESS_VOLUME_SQL =
            "SELECT trs.jb_id, trs.csc_id, SUM(trs.trs_price * ((100 - trs.trs_discount) / 100)) " +
                    "FROM tourservice trs " +
                    "WHERE trs.jb_id IN (:jobIds) " +
                    PLACEHOLDER_FILTER_SQL +
                    "GROUP BY trs.jb_id, trs.csc_id " +
                    "ORDER BY trs.jb_id, trs.csc_id";

    private final EntityManager entityManager;

    public JobTourServiceReadService(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public List<JobTourServiceRowDto> findByJobId(Long jobId) {
        if (jobId == null) {
            return List.of();
        }

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

        List<JobTourServiceRowDto> result = new ArrayList<>();
        for (Object[] row : rows) {
            result.add(mapRow(row, 0));
        }
        return result;
    }

    public java.util.Map<Long, List<JobTourServiceRowDto>> findRowsByJobIds(List<Long> jobIds) {
        if (jobIds == null || jobIds.isEmpty()) {
            return java.util.Map.of();
        }

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

        java.util.Map<Long, List<JobTourServiceRowDto>> result = new java.util.LinkedHashMap<>();
        for (Object[] row : rows) {
            Long jobId = toLong(value(row, 0));
            if (jobId == null) {
                continue;
            }
            result.computeIfAbsent(jobId, ignored -> new ArrayList<>())
                    .add(mapRow(row, 1));
        }
        return result;
    }

    public java.util.Map<Long, java.util.Map<Long, BigDecimal>> findBusinessVolumeByJobIds(List<Long> jobIds) {
        if (jobIds == null || jobIds.isEmpty()) {
            return java.util.Map.of();
        }

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

        java.util.Map<Long, java.util.Map<Long, BigDecimal>> result = new java.util.LinkedHashMap<>();
        for (Object[] row : rows) {
            Long jobId = toLong(value(row, 0));
            Long costCenterId = toLong(value(row, 1));
            BigDecimal businessVolume = toBigDecimal(value(row, 2));
            if (jobId == null || businessVolume == null) {
                continue;
            }
            result.computeIfAbsent(jobId, ignored -> new java.util.LinkedHashMap<>())
                    .put(costCenterId, businessVolume);
        }
        return result;
    }

    private BigDecimal calculateBusinessVolume(BigDecimal price, BigDecimal discount) {
        if (price == null) {
            return null;
        }
        BigDecimal discountFactor = BigDecimal.ONE.subtract(
                money(discount).divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP)
        );
        return price.multiply(discountFactor).setScale(2, RoundingMode.HALF_UP);
    }

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

    private JobTourServiceRowDto mapRow(Object[] row, int offset) {
        BigDecimal price = toBigDecimal(value(row, offset + 5));
        BigDecimal discount = toBigDecimal(value(row, offset + 6));
        JobTourServiceRowDto dto = new JobTourServiceRowDto();
        dto.setCostCenterId(toLong(value(row, offset)));
        dto.setCostCenterName(asString(value(row, offset + 1)));
        dto.setTourSort(toInteger(value(row, offset + 2)));
        dto.setServiceName(normalizeLegacyServiceText(asString(value(row, offset + 3))));
        dto.setServiceTypeName(normalizeLegacyServiceText(asString(value(row, offset + 4))));
        dto.setPrice(price);
        dto.setDiscount(discount);
        dto.setBusinessVolume(calculateBusinessVolume(price, discount));
        return dto;
    }

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

    private Long toLong(Object value) {
        if (value instanceof Number number) {
            return number.longValue();
        }
        if (value instanceof String string && !string.isBlank()) {
            try {
                return Long.parseLong(string.trim());
            } catch (NumberFormatException ignored) {
                return null;
            }
        }
        return 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.setScale(2, RoundingMode.HALF_UP);
        }
        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 "";
        }
        String text = value.toString();
        return text.isBlank() ? "" : text;
    }

    private String normalizeLegacyServiceText(String value) {
        if (value == null || value.isBlank()) {
            return "";
        }
        return value
                .replace("<br>", " ")
                .replace("<br/>", " ")
                .replace("<br />", " ")
                .trim();
    }
}
