package com.imprimelibros.erp.presupuesto;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.Locale;
import java.text.NumberFormat;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.math.BigDecimal;
import java.math.RoundingMode;

import com.imprimelibros.erp.configurationERP.VariableService;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
import com.imprimelibros.erp.presupuesto.classes.PresupuestadorItems;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter;
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPrecios;
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPreciosRepository;
import com.imprimelibros.erp.presupuesto.marcapaginas.Marcapaginas;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas;
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatricesRepository;
import com.imprimelibros.erp.presupuesto.marcapaginas.MarcapaginasRepository;
import com.imprimelibros.erp.externalApi.skApiClient;

@Service
public class PresupuestoService {

    @Autowired
    protected VariableService variableService;

    @Autowired
    protected MessageSource messageSource;

    @Autowired
    protected MaquetacionPreciosRepository maquetacionPreciosRepository;

    @Autowired
    protected MaquetacionMatricesRepository maquetacionMatricesRepository;

    @Autowired
    protected MarcapaginasRepository marcapaginasRepository;

    private final PresupuestadorItems presupuestadorItems;
    private final PresupuestoFormatter presupuestoFormatter;
    private final skApiClient apiClient;

    public PresupuestoService(PresupuestadorItems presupuestadorItems, PresupuestoFormatter presupuestoFormatter, skApiClient apiClient) {
        this.presupuestadorItems = presupuestadorItems;
        this.presupuestoFormatter = presupuestoFormatter;
        this.apiClient = apiClient;
    }

    public boolean validateDatosGenerales(int[] tiradas) {

        for (int tirada : tiradas) {
            if (tirada <= 0) {
                return false; // Invalid tirada found
            }
        }
        return true;
    }

    public Boolean isPOD(Presupuesto presupuesto) {

        int pod_value = variableService.getValorEntero("POD");
        return (presupuesto.getTirada1() != null && presupuesto.getTirada1() <= pod_value) ||
                (presupuesto.getTirada2() != null && presupuesto.getTirada2() <= pod_value) ||
                (presupuesto.getTirada3() != null && presupuesto.getTirada3() <= pod_value) ||
                (presupuesto.getTirada4() != null && presupuesto.getTirada4() <= pod_value);
    }

    public Map<String, Object> obtenerOpcionesColor(Presupuesto presupuesto, Locale locale) {

        List<ImagenPresupuesto> opciones = new ArrayList<>();

        if (presupuesto.getPaginasColor() > 0) {
            if (!this.isPOD(presupuesto)) {
                // POD solo color foto
                ImagenPresupuesto opcionColor = this.presupuestadorItems.getImpresionColor(locale);
                opcionColor.setSelected(Presupuesto.TipoImpresion.color.equals(presupuesto.getTipoImpresion()));
                opciones.add(opcionColor);
            }
            ImagenPresupuesto opcionColorHq = this.presupuestadorItems.getImpresionColorPremium(locale);
            if (Presupuesto.TipoImpresion.colorhq.equals(presupuesto.getTipoImpresion()))
                opcionColorHq.setSelected(true);
            opciones.add(opcionColorHq);
        } else {
            ImagenPresupuesto opcionNegro = this.presupuestadorItems.getImpresionNegro(locale);
            if (Presupuesto.TipoImpresion.negro.equals(presupuesto.getTipoImpresion()))
                opcionNegro.setSelected(true);
            opciones.add(opcionNegro);
            ImagenPresupuesto opcionNegroHq = this.presupuestadorItems.getImpresionNegroPremium(locale);
            if (Presupuesto.TipoImpresion.negrohq.equals(presupuesto.getTipoImpresion()))
                opcionNegroHq.setSelected(true);
            opciones.add(opcionNegroHq);
        }

        boolean opcionSeleccionada = opciones.stream().anyMatch(ImagenPresupuesto::isSelected);
        if (!opcionSeleccionada) {
            opciones.get(0).setSelected(true);
            presupuesto.setTipoImpresion(Presupuesto.TipoImpresion.valueOf(opciones.get(0).getId()));
        }

        Map<String, Object> response = new HashMap<>();
        response.put("opciones_color", opciones);

        return response;
    }

    public Map<String, Object> obtenerOpcionesPapelInterior(Presupuesto presupuesto, Locale locale) {

        List<ImagenPresupuesto> opciones = new ArrayList<>();

        opciones.add(this.presupuestadorItems.getPapelOffsetBlanco(locale));
        if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
                presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
            opciones.add(this.presupuestadorItems.getPapelOffsetBlancoVolumen(locale));
        }
        opciones.add(this.presupuestadorItems.getPapelOffsetAhuesado(locale));
        if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
                presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
            opciones.add(this.presupuestadorItems.getPapelOffsetAhuesadoVolumen(locale));
        }
        opciones.add(this.presupuestadorItems.getPapelEstucadoMate(locale));

        for (ImagenPresupuesto imagenPresupuesto : opciones) {
            imagenPresupuesto.setSelected(
                    presupuesto.getPapelInteriorId() != null
                            && imagenPresupuesto.getExtra_data().get("sk-id").equals(
                                    String.valueOf(presupuesto.getPapelInteriorId())));
        }

        boolean yaSeleccionado = opciones.stream().anyMatch(ImagenPresupuesto::isSelected);

        if (!yaSeleccionado && !opciones.isEmpty()) {
            ImagenPresupuesto primeraOpcion = opciones.get(0);
            primeraOpcion.setSelected(true);
            presupuesto.setPapelInteriorId(Integer.parseInt(primeraOpcion.getExtra_data().get("sk-id")));
        }

        Map<String, Object> response = new HashMap<>();
        response.put("opciones_papel_interior", opciones);

        return response;
    }

    public Map<String, Object> obtenerOpcionesGramajeInterior(Presupuesto presupuesto) {

        List<String> gramajes = new ArrayList<>();

        final int BLANCO_OFFSET_ID = 3;
        final int BLANCO_OFFSET_VOLUMEN_ID = 7;
        final int AHUESADO_OFFSET_ID = 4;
        final int AHUESADO_OFFSET_VOLUMEN_ID = 6;
        final int ESTUCADO_MATE_ID = 2;

        if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == BLANCO_OFFSET_ID) {

            gramajes.add("80");
            gramajes.add("90");
            if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq ||
                    presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) {
                gramajes.add("100");
                gramajes.add("120");
                gramajes.add("150");
                gramajes.add("170");
            }
        } else if (presupuesto.getPapelInteriorId() != null
                && presupuesto.getPapelInteriorId() == BLANCO_OFFSET_VOLUMEN_ID) {

            if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
                    presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
                gramajes.add("80");
            }

        } else if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == AHUESADO_OFFSET_ID) {

            gramajes.add("80");
            gramajes.add("90");
            if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq ||
                    presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) {
                gramajes.add("100");
            }

        } else if (presupuesto.getPapelInteriorId() != null
                && presupuesto.getPapelInteriorId() == AHUESADO_OFFSET_VOLUMEN_ID) {

            if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
                    presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
                gramajes.add("70");
                gramajes.add("80");
            }

        } else if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == ESTUCADO_MATE_ID) {

            if (presupuesto.getTipoImpresion() != Presupuesto.TipoImpresion.color) {
                gramajes.add("90");
            }
            if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq ||
                    presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) {
                gramajes.add("100");
                gramajes.add("115");
            }
            if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
                    presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
                gramajes.add("120");
            }
            if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq ||
                    presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) {
                gramajes.add("125");
                gramajes.add("135");
                gramajes.add("150");
                gramajes.add("170");
                gramajes.add("200");
            }
        }

        Map<String, Object> response = new HashMap<>();
        response.put("opciones_gramaje_interior", gramajes);
        return response;
    }

    public Map<String, Object> obtenerOpcionesPapelCubierta(Presupuesto presupuesto, Locale locale) {

        List<ImagenPresupuesto> opciones = new ArrayList<>();

        if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaBlanda) {
            opciones.add(this.presupuestadorItems.getCartulinaGraficaCubierta(locale));
        }
        opciones.add(this.presupuestadorItems.getEstucadoMateCubierta(locale));

        for (ImagenPresupuesto imagenPresupuesto : opciones) {
            imagenPresupuesto.setSelected(
                    presupuesto.getPapelCubiertaId() != null
                            && imagenPresupuesto.getExtra_data().get("sk-id").equals(
                                    String.valueOf(presupuesto.getPapelCubiertaId())));
        }

        Map<String, Object> response = new HashMap<>();
        response.put("opciones_papel_cubierta", opciones);
        return response;
    }

    public Map<String, Object> obtenerOpcionesGramajeCubierta(Presupuesto presupuesto) {

        List<String> gramajes = new ArrayList<>();

        final int CARTULINA_GRAFICA_ID = 5;
        final int ESTUCADO_MATE_ID = 2;

        if (presupuesto.getPapelCubiertaId() != null && presupuesto.getPapelCubiertaId() == CARTULINA_GRAFICA_ID) {
            gramajes.add("240");
            gramajes.add("270");
            gramajes.add("300");
            gramajes.add("350");
        } else if (presupuesto.getPapelCubiertaId() != null && presupuesto.getPapelCubiertaId() == ESTUCADO_MATE_ID) {
            if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaBlanda) {

                gramajes.add("250");
                gramajes.add("300");
                gramajes.add("350");
            } else {
                gramajes.add("170");
            }
        }

        Map<String, Object> response = new HashMap<>();
        response.put("opciones_gramaje_cubierta", gramajes);
        return response;
    }

    public Map<String, Object> toSkApiRequest(Presupuesto presupuesto) {

        final int SK_CLIENTE_ID = 1284;
        final int SK_PAGINAS_CUADERNILLO = 32;

        Map<String, Object> tamanio = Map.of(
                "ancho", presupuesto.getAncho(),
                "alto", presupuesto.getAlto());
        Map<String, Object> interior = Map.of(
                "papelInterior", presupuesto.getPapelInteriorId(),
                "gramajeInterior", presupuesto.getGramajeInterior());
        Map<String, Object> cubierta = Map.of(
                "tipoCubierta", presupuesto.getTipoCubierta().name(),
                "papelCubierta", presupuesto.getPapelCubiertaId(),
                "gramajeCubierta", presupuesto.getGramajeCubierta(),
                "carasImpresion", presupuesto.getCubiertaCaras(),
                "solapas", presupuesto.getSolapasCubierta() ? presupuesto.getTamanioSolapasCubierta() : 0,
                "acabado", presupuesto.getAcabado(),
                "cabezada", presupuesto.getCabezada(),
                "lomoRedondo", presupuesto.getTipoCubierta() == TipoCubierta.tapaDuraLomoRedondo ? 1 : 0);

        Map<String, Object> body = new HashMap<>();
        body.put("tipo_impresion_id", this.getTipoImpresionId(presupuesto));
        body.put("tirada", Arrays.stream(presupuesto.getTiradas())
                .filter(Objects::nonNull)
                .collect(Collectors.toList()));
        body.put("tamanio", tamanio);
        body.put("tipo", presupuesto.getTipoEncuadernacion());
        body.put("clienteId", SK_CLIENTE_ID);
        body.put("isColor", presupuesto.getTipoImpresion().name().contains("color") ? 1 : 0);
        body.put("isHq", presupuesto.getTipoImpresion().name().contains("hq") ? 1 : 0);
        body.put("paginas", presupuesto.getPaginasNegro() + presupuesto.getPaginasColor());
        body.put("paginasColor", presupuesto.getPaginasColor());
        body.put("paginasCuadernillo", SK_PAGINAS_CUADERNILLO);
        body.put("interior", interior);
        body.put("cubierta", cubierta);
        body.put("guardas", null);
        if (presupuesto.getSobrecubierta()) {
            Map<String, Object> sobrecubierta = new HashMap<>();
            sobrecubierta.put("papel", presupuesto.getPapelSobrecubiertaId());
            sobrecubierta.put("gramaje", presupuesto.getGramajeSobrecubierta());
            sobrecubierta.put("solapas", presupuesto.getTamanioSolapasSobrecubierta());
            sobrecubierta.put("acabado", presupuesto.getAcabadoSobrecubierta());
            body.put("sobrecubierta", sobrecubierta);
        }

        if (presupuesto.getFaja()) {
            Map<String, Object> faja = new HashMap<>();
            faja.put("papel", presupuesto.getPapelFajaId());
            faja.put("gramaje", presupuesto.getGramajeFaja());
            faja.put("solapas", presupuesto.getTamanioSolapasFaja());
            faja.put("acabado", presupuesto.getAcabadoFaja());
            faja.put("alto", presupuesto.getAltoFaja());
            body.put("faja", faja);
        }
        // body.put("servicios", servicios);

        return body;
    }

    public Integer getTipoImpresionId(Presupuesto presupuesto) {

        if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.fresado) {
            if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura ||
                    presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
                return 1; // Fresado tapa dura
            } else {
                return 2; // Fresado tapa blanda
            }
        } else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.cosido) {
            if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura ||
                    presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
                return 3; // Cosido tapa dura
            } else {
                return 4; // Cosido tapa blanda
            }
        } else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.espiral) {
            if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura ||
                    presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
                return 5; // Espiral tapa dura
            } else {
                return 6; // Espiral tapa blanda
            }
        } else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.wireo) {
            if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura ||
                    presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
                return 7; // Wireo tapa dura
            } else {
                return 8; // Wireo tapa blanda
            }
        } else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.grapado) {
            return 21; // Grapado
        } else {
            return 0; // Default case, no valid type
        }
    }

    public Map<String, Object> obtenerOpcionesAcabadosCubierta(Presupuesto presupuesto, Locale locale) {
        Map<String, Object> resultado = new HashMap<>();
        List<Object> opciones = new ArrayList<>();

        opciones.add(new HashMap<>() {
            {
                put("name", messageSource.getMessage("presupuesto.acabado-ninguno", null, locale));
                put("sk-id", 0);
            }
        });
        opciones.add(new HashMap<>() {
            {
                put("name", messageSource.getMessage("presupuesto.acabado-plastificado-brillo-1c", null, locale));
                put("sk-id", 1);
            }
        });
        opciones.add(new HashMap<>() {
            {
                put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-1c", null, locale));
                put("sk-id", 5);
            }
        });
        opciones.add(new HashMap<>() {
            {
                put("name",
                        messageSource.getMessage("presupuesto.acabado-plastificado-mate-1c-antirrayado", null, locale));
                put("sk-id", 8);
            }
        });
        opciones.add(new HashMap<>() {
            {
                put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi", null, locale));
                put("sk-id", 2);
            }
        });
        opciones.add(new HashMap<>() {
            {
                put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi3d", null, locale));
                put("sk-id", 3);
            }
        });
        opciones.add(new HashMap<>() {
            {
                put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi-braile", null, locale));
                put("sk-id", 4);
            }
        });
        opciones.add(new HashMap<>() {
            {
                put("name", messageSource.getMessage("presupuesto.acabado-plastificado-sandy-1c", null, locale));
                put("sk-id", 9);
            }
        });

        resultado.put("opciones_acabados_cubierta", opciones);
        return resultado;
    }

    public Map<String, Object> aplicarMargenTiradas(Map<String, Object> data) {

        // implementar margenes
        return (Map<String, Object>) data;
    }

    public String obtenerPrecioRetractilado(Presupuesto presupuesto, Locale locale) {
        Integer[] tiradas = presupuesto.getTiradas();
        Integer tirada_min = Arrays.stream(tiradas)
                .filter(Objects::nonNull)
                .min(Integer::compareTo)
                .orElse(0);
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("tirada",
                presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : tirada_min);
        Double precio_retractilado = apiClient.getRetractilado(requestBody);
        return precio_retractilado != null
                ? NumberFormat.getNumberInstance(locale)
                        .format(Math.round(precio_retractilado * 100.0) / 100.0)
                : "0,00";
    }

    public Map<String, Object> obtenerServiciosExtras(Presupuesto presupuesto, Locale locale) {

        List<Object> opciones = new ArrayList<>();

        Double price_prototipo = this.obtenerPrototipo(presupuesto);

        opciones.add(new HashMap<String, String>() {
            {
                put("id", "retractilado");
                put("title", messageSource.getMessage("presupuesto.extras-retractilado", null, locale));
                put("description", "");
                put("price", obtenerPrecioRetractilado(presupuesto, locale));
                put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
            }
        });
        opciones.add(new HashMap<String, String>() {
            {
                put("id", "service-isbn");
                put("title", messageSource.getMessage("presupuesto.extras-isbn", null, locale));
                put("description", "");
                put("price", "30");
                put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
            }
        });
        opciones.add(new HashMap<String, String>() {
            {
                put("id", "deposito-legal");
                put("title", messageSource.getMessage("presupuesto.extras-deposito-legal", null, locale));
                put("description",
                        messageSource.getMessage("presupuesto.extras-deposito-legal-descripcion", null, locale));
                put("price", "30");
                put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
            }
        });
        opciones.add(new HashMap<String, String>() {
            {
                put("id", "revision-archivos");
                put("title", messageSource.getMessage("presupuesto.extras-revision-archivos", null, locale));
                put("description", "");
                put("price", "50");
                put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
            }
        });
        opciones.add(new HashMap<String, String>() {
            {
                put("id", "maquetacion-cubierta");
                put("title", messageSource.getMessage("presupuesto.extras-maquetacion-cubierta", null, locale));
                put("description", "");
                put("price", "50");
                put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
            }
        });
        opciones.add(new HashMap<String, String>() {
            {
                put("id", "ferro-digital");
                put("title", messageSource.getMessage("presupuesto.extras-ferro-digital", null, locale));
                put("description", "");
                put("price", "0");
                put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
                put("checked", "true");
                put("allowChange", "false");
                put("ribbonText", messageSource.getMessage("presupuesto.extras-ferro-digital-ribbon", null, locale));
            }
        });
        opciones.add(new HashMap<String, String>() {
            {
                put("id", "ejemplar-prueba");
                put("title", messageSource.getMessage("presupuesto.extras-ejemplar-prueba", null, locale));
                put("description", "");
                if (price_prototipo == 0.0) {
                    put("price", messageSource.getMessage("presupuesto.consultar-soporte", null, locale));
                    put("priceUnit", "");
                } else {
                    put("price", NumberFormat.getNumberInstance(locale)
                            .format(Math.round(price_prototipo * 100.0) / 100.0));
                    put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
                }
            }
        });
        opciones.add(new HashMap<String, String>() {
            {
                put("id", "marcapaginas");
                put("title", messageSource.getMessage("presupuesto.extras-marcapaginas", null, locale));
                put("description", "");
                put("price", messageSource.getMessage("presupuesto.extras-calcular", null, locale));
            }
        });
        opciones.add(new HashMap<String, String>() {
            {
                put("id", "maquetacion");
                put("title", messageSource.getMessage("presupuesto.extras-maquetacion", null, locale));
                put("description", "");
                put("price", messageSource.getMessage("presupuesto.extras-calcular", null, locale));
            }
        });

        Map<String, Object> response = new HashMap<>();
        response.put("servicios_extra", opciones);
        return response;
    }

    private Double obtenerPrototipo(Presupuesto presupuesto) {

        // Obtenemos el precio de 1 unidad para el ejemplar de prueba
        HashMap<String, Object> price = new HashMap<>();
        // make a copy of "presupuesto" to avoid modifying the original object
        Presupuesto presupuestoTemp = presupuesto.clone();
        presupuestoTemp.setTirada1(1);
        presupuestoTemp.setTirada2(null);
        presupuestoTemp.setTirada3(null);
        presupuestoTemp.setTirada4(null);
        if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
            presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.colorhq);
        } else if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.negro) {
            presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.negrohq);
        }
        String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuestoTemp));
        Double price_prototipo = 0.0;
        try {
            price = new ObjectMapper().readValue(priceStr, new TypeReference<>() {
            });
            price_prototipo = ((List<Double>) ((Map<String, Object>) price.get("data")).get("precios")).get(0);
            if (price_prototipo < 25) {
                price_prototipo = 25.0;
            }

        } catch (JsonProcessingException e) {
        } catch (Exception exception) {
        }
        return price_prototipo;
    }

    public HashMap<String, Object> getPrecioMaquetacion(PresupuestoMaquetacion presupuestoMaquetacion, Locale locale) {
        try {
            List<MaquetacionPrecios> lista = maquetacionPreciosRepository.findAll();

            // helper para obtener un precio por clave
            java.util.function.Function<String, Double> price = key -> lista.stream()
                    .filter(p -> key.equals(p.getKey()))
                    .map(MaquetacionPrecios::getValue)
                    .findFirst()
                    .orElse(0.0);

            BigDecimal precio = BigDecimal.ZERO;

            // millar_maquetacion * (numCaracteres / 1000.0)
            BigDecimal millares = BigDecimal.valueOf(presupuestoMaquetacion.getNumCaracteres()).divide(
                    BigDecimal.valueOf(1000), 6,
                    RoundingMode.HALF_UP);
            precio = precio.add(millares.multiply(BigDecimal.valueOf(price.apply("millar_maquetacion"))));

            // Numero de paginas estimado
            int numPaginas = 0;
            Integer matricesPorPagina = maquetacionMatricesRepository.findMatrices(
                    presupuestoMaquetacion.getFormato(),
                    presupuestoMaquetacion.getCuerpoTexto());
            if (matricesPorPagina != null && matricesPorPagina > 0) {
                numPaginas = presupuestoMaquetacion.getNumCaracteres() / matricesPorPagina;
            }
            // Precio por pagina estimado
            BigDecimal precioRedondeado = precio.setScale(2, RoundingMode.HALF_UP);
            double precioPaginaEstimado = 0.0;
            if (numPaginas > 0) {
                precioPaginaEstimado = precioRedondeado
                        .divide(BigDecimal.valueOf(numPaginas), 2, RoundingMode.HALF_UP)
                        .doubleValue();
            }

            // tabla, columna, foto
            precio = precio
                    .add(BigDecimal.valueOf(presupuestoMaquetacion.getNumTablas())
                            .multiply(BigDecimal.valueOf(price.apply("tabla"))));
            precio = precio.add(
                    BigDecimal.valueOf(presupuestoMaquetacion.getNumColumnas())
                            .multiply(BigDecimal.valueOf(price.apply("columnas"))));
            precio = precio
                    .add(BigDecimal.valueOf(presupuestoMaquetacion.getNumFotos())
                            .multiply(BigDecimal.valueOf(price.apply("foto"))));

            if (presupuestoMaquetacion.isCorreccionOrtotipografica()) {
                precio = precio
                        .add(millares.multiply(BigDecimal.valueOf(price.apply("correccion_ortotipografica"))));
            }
            if (presupuestoMaquetacion.isTextoMecanografiado()) {
                precio = precio.add(millares.multiply(BigDecimal.valueOf(price.apply("mecanoescritura_por_millar"))));
            }
            if (presupuestoMaquetacion.isDisenioPortada()) {
                precio = precio.add(BigDecimal.valueOf(price.apply("disenio_portada")));
            }
            if (presupuestoMaquetacion.isEpub()) {
                precio = precio.add(BigDecimal.valueOf(price.apply("epub")));
            }

            // redondeo final
            precioRedondeado = precio.setScale(2, RoundingMode.HALF_UP);

            HashMap<String, Object> out = new HashMap<>();
            out.put("precio", precioRedondeado.doubleValue());
            out.put("numPaginasEstimadas", numPaginas);
            out.put("precioPaginaEstimado", precioPaginaEstimado);
            HashMap<String, String> language = new HashMap<>();
            language.put("num_paginas_estimadas",
                    messageSource.getMessage("presupuesto.maquetacion.num-paginas-estimadas", null, locale));
            language.put("precio_por_pagina_estimado",
                    messageSource.getMessage("presupuesto.maquetacion.precio-por-pagina-estimado", null, locale));
            language.put("add_to_presupuesto",
                    messageSource.getMessage("presupuesto.add-to-presupuesto", null, locale));
            language.put("cancel", messageSource.getMessage("app.cancelar", null, locale));
            language.put("presupuesto_maquetacion", messageSource.getMessage("presupuesto.maquetacion", null, locale));
            out.put("language", language);
            return out;

        } catch (Exception e) {
            System.out.println("Error procesando presupuesto maquetacion: " + e.getMessage());
        }

        HashMap<String, Object> out = new HashMap<>();
        out.put("precio", 0.0);
        out.put("numPaginasEstimadas", 0);
        out.put("precioPaginaEstimado", 0.0);
        return out;
    }

    public HashMap<String, Object> getPrecioMarcapaginas(PresupuestoMarcapaginas presupuestoMarcapaginas,
            Locale locale) {

        try {

            List<Marcapaginas> m = marcapaginasRepository.findPrecios(presupuestoMarcapaginas);
            if (m.isEmpty() || m.get(0) == null) {
                HashMap<String, Object> out = new HashMap<>();
                out.put("precio_unidad", 0.0);
                out.put("precio_total", 0.0);
                return out;
            }

            Marcapaginas marcapaginas = m.get(0);

            Double precio = 0.0;
            Double margen = 0.0;
            Double pvp = 0.0;
            BigDecimal data = BigDecimal.ZERO;

            if (marcapaginas.getUnidades_max() >= presupuestoMarcapaginas.getUnidades()) {

                precio = marcapaginas.getPrecio_unidades_min() +
                        (presupuestoMarcapaginas.getUnidades() - marcapaginas.getUnidades_min())
                                * (marcapaginas.getPrecio_unidades_max() - marcapaginas.getPrecio_unidades_min())
                                / (marcapaginas.getUnidades_max() - marcapaginas.getUnidades_min());

                data = new BigDecimal(precio);
                precio = data.setScale(2, RoundingMode.HALF_UP).doubleValue();

                margen = 1.0 * marcapaginas.getMargen_unidades_min() +
                        (1.0 * presupuestoMarcapaginas.getUnidades() - 1.0 * marcapaginas.getUnidades_min())
                                * (1.0 * marcapaginas.getMargen_unidades_max()
                                        - 1.0 * marcapaginas.getMargen_unidades_min())
                                / (1.0 * marcapaginas.getUnidades_max() - 1.0 * marcapaginas.getUnidades_min());

                data = new BigDecimal(margen);
                margen = data.setScale(2, RoundingMode.HALF_UP).doubleValue();

                pvp = precio + (precio * margen / 100);

                data = new BigDecimal(pvp);
                pvp = data.setScale(2, RoundingMode.HALF_UP).doubleValue();

            } else {

                // precio unidad para el máximo de unidades
                precio = marcapaginas.getPrecio_unidades_max() / marcapaginas.getUnidades_max();
                precio = precio * presupuestoMarcapaginas.getUnidades();
                data = new BigDecimal(precio);
                precio = data.setScale(2, RoundingMode.HALF_UP).doubleValue();

                margen = 1.0 * marcapaginas.getMargen_unidades_max();
                data = new BigDecimal(margen);
                margen = data.setScale(2, RoundingMode.HALF_UP).doubleValue();

                pvp = precio + (precio * margen / 100);
                data = new BigDecimal(pvp);
                pvp = data.setScale(2, RoundingMode.HALF_UP).doubleValue();

            }

            Double precio_unidad = pvp / presupuestoMarcapaginas.getUnidades();
            data = new BigDecimal(precio_unidad);
            precio_unidad = data.setScale(6, RoundingMode.HALF_UP).doubleValue();

            HashMap<String, Object> resultado;
            resultado = new HashMap<>();
            resultado.put("precio_unitario", precio_unidad);
            resultado.put("precio_total", pvp);
            HashMap<String, String> language = new HashMap<>();
            language.put("precio_unidad",
                    messageSource.getMessage("presupuesto.marcapaginas.precio-unidad", null, locale));
            language.put("precio_total",
                    messageSource.getMessage("presupuesto.marcapaginas.precio-total", null, locale));
            language.put("add_to_presupuesto",
                    messageSource.getMessage("presupuesto.add-to-presupuesto", null, locale));
            language.put("cancel", messageSource.getMessage("app.cancelar", null, locale));
            language.put("presupuesto_marcapaginas",
                    messageSource.getMessage("presupuesto.marcapaginas", null, locale));
            resultado.put("language", language);
            return resultado;

        } catch (Exception e) {
            System.out.println("Error procesando presupuesto marcapaginas: " + e.getMessage());
        }
        HashMap<String, Object> out = new HashMap<>();
        out.put("precio_unidad", 0.0);
        out.put("precio_total", 0.0);
        return out;
    }

    public Map<String, Object> getResumen(Presupuesto presupuesto, List<Map<String, Object>> servicios, Locale locale) {
        
        Map<String, Object> resumen = new HashMap<>();
        resumen.put("titulo", presupuesto.getTitulo());

        Presupuesto pressupuestoTemp = presupuesto.clone();

        resumen.put("imagen", "/assets/images/imprimelibros/presupuestador/" + presupuesto.getTipoEncuadernacion() + ".png");
        resumen.put("imagen_alt", messageSource.getMessage("presupuesto." + presupuesto.getTipoEncuadernacion(), null, locale));

        boolean hayDepositoLegal = servicios != null && servicios.stream()
                .map(m -> java.util.Objects.toString(m.get("id"), "")) // null-safe -> String
                .map(String::trim)
                .anyMatch("deposito-legal"::equals);

        if(hayDepositoLegal){
            pressupuestoTemp.setSelectedTirada(presupuesto.getSelectedTirada()+4);
        }
        
        HashMap<String, Object> precios = this.calcularPresupuesto(pressupuestoTemp, locale);
        if(precios.containsKey("error")){
            resumen.put("error", precios.get("error"));
            return resumen;
        }
        
        HashMap<String, Object> linea = new HashMap<>();
        Double precio_unitario = 0.0;
        Double precio_total = 0.0;
        Integer counter = 0;
        linea.put("descripcion", presupuestoFormatter.resumen(presupuesto, servicios, locale));
        linea.put("cantidad", presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : 0);
        precio_unitario = ((List<Double>) ((Map<String, Object>) precios.get("data")).get("precios"))
                .get(0);
        precio_total = precio_unitario * presupuesto.getSelectedTirada();
        linea.put("precio_unitario", precio_unitario);
        linea.put("precio_total", BigDecimal.valueOf(precio_total).setScale(2, RoundingMode.HALF_UP));
        resumen.put("linea" + counter, linea);
        counter++;

        if(hayDepositoLegal) {
            
            linea = new HashMap<>();
            linea.put("descripcion", messageSource.getMessage("presupuesto.resumen-deposito-legal", null, locale));
            linea.put("cantidad", 4);
            linea.put("precio_unitario", precio_unitario);
            linea.put("precio_total", BigDecimal.valueOf(precio_unitario * 4).setScale(2, RoundingMode.HALF_UP));
            resumen.put("linea" + counter, linea);
            counter++;
        }

        List<Map<String, Object>> serviciosExtras = new ArrayList<>();

        if(servicios != null){
            for (Map<String, Object> servicio : servicios) {
                HashMap<String, Object> servicioData = new HashMap<>();
                servicioData.put("id", servicio.get("id"));
                servicioData.put("descripcion", servicio.get("label"));
                servicioData.put("precio", servicio.get("id").equals("marcapaginas") ? 
                    Double.parseDouble(servicio.get("price").toString())/Double.parseDouble(servicio.get("units").toString()) : 
                    servicio.get("price"));
                servicioData.put("unidades", servicio.get("units"));
                serviciosExtras.add(servicioData);
            }
        }
        resumen.put("servicios", serviciosExtras);

        return resumen;
    }

    public HashMap<String, Object> calcularPresupuesto(Presupuesto presupuesto, Locale locale) {

        HashMap<String, Object> price = new HashMap<>();
        String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuesto));

        try {
            price = new ObjectMapper().readValue(priceStr, new TypeReference<>() {
            });
        } catch (JsonProcessingException e) {
            price = new HashMap<>();
            price.put("error", messageSource.getMessage("presupuesto.error-obtener-precio", null, locale));
        }
        
        return price;
    }
}
