package com.imprimelibros.erp.presupuesto;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.MediaType;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.imprimelibros.erp.externalApi.skApiClient;
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas;
import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups;

import jakarta.validation.Valid;

@Controller
@RequestMapping("/presupuesto")
public class PresupuestoController {

    @Autowired
    protected PresupuestoService presupuestoService;

    @Autowired
    protected skApiClient apiClient;

    @Autowired
    protected MessageSource messageSource;

    private final ObjectMapper objectMapper;

    public PresupuestoController(ObjectMapper objectMapper){
        this.objectMapper = objectMapper;
    }

    @PostMapping("/public/validar/datos-generales")
    public ResponseEntity<?> validarDatosGenerales(
            @Validated(PresupuestoValidationGroups.DatosGenerales.class) Presupuesto presupuesto,
            BindingResult result, Locale locale) {

        Map<String, String> errores = new HashMap<>();

        // errores de campos individuales
        result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));

        // errores globales (@ConsistentTiradas...)
        result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));

        if (!errores.isEmpty()) {
            return ResponseEntity.badRequest().body(errores);
        }

        // opciones color
        Map<String, Object> resultado = presupuestoService.obtenerOpcionesColor(presupuesto, locale);

        // opciones papel interior
        resultado.putAll(presupuestoService.obtenerOpcionesPapelInterior(presupuesto, locale));

        // opciones gramaje interior
        resultado.putAll(presupuestoService.obtenerOpcionesGramajeInterior(presupuesto));
        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/validar/interior")
    public ResponseEntity<?> validarInterior(
            @Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto,
            BindingResult result, Locale locale) {

        Map<String, String> errores = new HashMap<>();

        // errores de campos individuales
        result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));

        // errores globales (@ConsistentTiradas...)
        result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));

        if (!errores.isEmpty()) {
            return ResponseEntity.badRequest().body(errores);
        }
        Map<String, Object> resultado = new HashMap<>();
        resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto)));
        resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale));
        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/validar/cubierta")
    public ResponseEntity<?> validarCubierta(
            @Validated(PresupuestoValidationGroups.Cubierta.class) Presupuesto presupuesto,
            BindingResult result,
            @RequestParam(name = "calcular", defaultValue = "true") boolean calcular,
            Locale locale) {

        Map<String, String> errores = new HashMap<>();

        // errores de campos individuales
        result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));

        // errores globales (@ConsistentTiradas...)
        result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));

        if (!errores.isEmpty()) {
            return ResponseEntity.badRequest().body(errores);
        }

        if (calcular) {

            HashMap<String, Object> price = presupuestoService.calcularPresupuesto(presupuesto, locale);

            if (!price.containsKey("data")) {
                return ResponseEntity.badRequest()
                        .body(messageSource.getMessage("presupuesto.error-obtener-precio", null, locale));
            }
            return ResponseEntity.ok(price.get("data"));

        }
        return ResponseEntity.ok().build();
    }

    @PostMapping("/public/validar/seleccion-tirada")
    public ResponseEntity<?> validarSeleccionTirada(
            Presupuesto presupuesto,
            BindingResult result, Locale locale) {

        Map<String, String> errores = new HashMap<>();

        // errores de campos individuales
        result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));

        // errores globales (@ConsistentTiradas...)
        result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));

        if (!errores.isEmpty()) {
            return ResponseEntity.badRequest().body(errores);
        }

        Map<String, Object> resultado = new HashMap<>();
        // servicios extra
        resultado.putAll(presupuestoService.obtenerServiciosExtras(presupuesto, locale));
        Map<String, String> language = new HashMap<>();
        language.put("calcular", messageSource.getMessage("presupuesto.calcular", null, locale));
        resultado.put("language", language);

        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/get-papel-interior")
    public ResponseEntity<?> getPapelInterior(
            @Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto,
            BindingResult result, Locale locale) {

        Map<String, String> errores = new HashMap<>();

        // errores de campos individuales
        result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));

        if (!errores.isEmpty()) {
            return ResponseEntity.badRequest().body(errores);
        }

        // opciones color
        Map<String, Object> resultado = presupuestoService.obtenerOpcionesPapelInterior(presupuesto, locale);

        // opciones gramaje interior
        resultado.putAll(presupuestoService.obtenerOpcionesGramajeInterior(presupuesto));
        List<String> opciones = (List<String>) resultado.get("opciones_gramaje_interior");

        if (opciones != null && !opciones.isEmpty()) {
            String gramajeActual = presupuesto.getGramajeInterior().toString();
            if (!opciones.contains(gramajeActual)) {
                presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción
            }
        }

        resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto)));
        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/get-gramaje-interior")
    public ResponseEntity<?> getGramajeInterior(
            @Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto,
            BindingResult result) {

        Map<String, String> errores = new HashMap<>();

        // errores de campos individuales
        result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));

        if (!errores.isEmpty()) {
            return ResponseEntity.badRequest().body(errores);
        }

        Map<String, Object> resultado = presupuestoService.obtenerOpcionesGramajeInterior(presupuesto);
        List<String> opciones = (List<String>) resultado.get("opciones_gramaje_interior");

        if (opciones != null && !opciones.isEmpty()) {
            String gramajeActual = presupuesto.getGramajeInterior().toString();
            if (!opciones.contains(gramajeActual)) {
                presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción
            }
        }
        resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto)));
        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/get-max-solapas")
    public ResponseEntity<?> getMaxSolapas(
            @Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto,
            BindingResult result) {

        Map<String, String> errores = new HashMap<>();

        // errores de campos individuales
        result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));

        if (!errores.isEmpty()) {
            return ResponseEntity.badRequest().body(errores);
        }

        Map<String, Object> resultado = new HashMap<>();
        resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto)));
        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/get-papel-cubierta")
    public ResponseEntity<?> getPapelCubierta(
            Presupuesto presupuesto,
            BindingResult result, Locale locale) {

        Map<String, Object> resultado = new HashMap<>();
        Map<String, Object> papelesCubierta = presupuestoService.obtenerOpcionesPapelCubierta(presupuesto, locale);
        List<ImagenPresupuesto> opciones = (List<ImagenPresupuesto>) presupuestoService
                .obtenerOpcionesPapelCubierta(presupuesto, locale)
                .get("opciones_papel_cubierta");

        if (opciones != null && opciones.stream().noneMatch(
                o -> o.getExtra_data().get("sk-id").equals(String.valueOf(presupuesto.getPapelCubiertaId())))) {
            presupuesto.setPapelCubiertaId(Integer.valueOf(opciones.get(0).getExtra_data().get("sk-id")));
        }
        resultado.putAll(papelesCubierta);

        resultado.putAll(presupuestoService.obtenerOpcionesGramajeCubierta(presupuesto));
        List<String> gramajesCubierta = (List<String>) resultado.get("opciones_gramaje_cubierta");
        if (gramajesCubierta != null && !gramajesCubierta.isEmpty()) {
            String gramajeActual = presupuesto.getGramajeCubierta().toString();
            if (!gramajesCubierta.contains(gramajeActual)) {
                presupuesto.setGramajeCubierta(Integer.parseInt(gramajesCubierta.get(0))); // Asignar primera opción
            }
        }
        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/get-gramaje-cubierta")
    public ResponseEntity<?> getGramajeCubierta(
            Presupuesto presupuesto,
            BindingResult result) {

        Map<String, Object> resultado = presupuestoService.obtenerOpcionesGramajeCubierta(presupuesto);
        List<String> gramajesCubierta = (List<String>) resultado.get("opciones_gramaje_cubierta");
        if (gramajesCubierta != null && !gramajesCubierta.isEmpty()) {
            String gramajeActual = presupuesto.getGramajeCubierta().toString();
            if (!gramajesCubierta.contains(gramajeActual)) {
                presupuesto.setGramajeCubierta(Integer.parseInt(gramajesCubierta.get(0))); // Asignar primera opción
            }
        }
        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/get-acabados-cubierta")
    public ResponseEntity<?> getAcabadosCubierta(
            Presupuesto presupuesto,
            BindingResult result, Locale locale) {

        Map<String, Object> resultado = presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale);

        return ResponseEntity.ok(resultado);
    }

    @PostMapping("/public/get-price")
    public ResponseEntity<?> getPrice(
            Presupuesto presupuesto,
            BindingResult result, Locale locale) {

        Map<String, String> errores = new HashMap<>();
        // errores de campos individuales
        result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
        // errores globales (@ConsistentTiradas...)
        result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));
        if (!errores.isEmpty()) {
            return ResponseEntity.badRequest().body(errores);
        }
        String price = apiClient.getPrice(presupuestoService.toSkApiRequest(presupuesto));
        if (price == null || price.isEmpty()) {
            return ResponseEntity.badRequest().body("No se pudo obtener el precio. Intente nuevamente.");
        }
        return ResponseEntity.ok(price);

    }

    @GetMapping(value = "/public/maquetacion/form", produces = MediaType.TEXT_HTML_VALUE)
    public String getMaquetacionForm(Model model) {
        model.addAttribute("presupuestoMaquetacion", new PresupuestoMaquetacion());
        return "imprimelibros/presupuestos/presupuesto-maquetacion-form :: maquetacionForm";
    }

    @GetMapping("/public/maquetacion")
    public ResponseEntity<?> getPresupuestoMaquetacion(
            @Valid @ModelAttribute PresupuestoMaquetacion presupuestoMaquetacion,
            BindingResult result,
            Locale locale) {

        if (result.hasErrors()) {
            // Construimos un mapa field -> mensaje para tu AJAX
            Map<String, String> errores = result.getFieldErrors().stream()
                    .collect(java.util.stream.Collectors.toMap(
                            fe -> fe.getField(),
                            fe -> fe.getDefaultMessage(),
                            (a, b) -> a));
            return ResponseEntity.badRequest().body(errores);
        }

        Map<String, Object> resultado = presupuestoService.getPrecioMaquetacion(presupuestoMaquetacion, locale);
        if ((Double) resultado.get("precio") == 0.0 && (Integer) resultado.get("numPaginasEstimadas") == 0
                && (Double) resultado.get("precioPaginaEstimado") == 0.0) {
            return ResponseEntity.badRequest()
                    .body(messageSource.getMessage("presupuesto.errores.presupuesto-maquetacion", null, locale));
        }
        return ResponseEntity.ok(resultado);
    }

    @GetMapping(value = "/public/marcapaginas/form", produces = MediaType.TEXT_HTML_VALUE)
    public String getMarcapaginasForm(Model model) {
        model.addAttribute("presupuestoMarcapaginas", new PresupuestoMarcapaginas());
        return "imprimelibros/presupuestos/presupuesto-marcapaginas-form :: marcapaginasForm";
    }

    @GetMapping("/public/marcapaginas")
    public ResponseEntity<?> getPresupuestoMarcapaginas(
            @Valid @ModelAttribute PresupuestoMarcapaginas presupuestoMarcapaginas,
            BindingResult result,
            Locale locale) {

        if (result.hasErrors()) {
            // Construimos un mapa field -> mensaje para tu AJAX
            Map<String, String> errores = result.getFieldErrors().stream()
                    .collect(java.util.stream.Collectors.toMap(
                            fe -> fe.getField(),
                            fe -> fe.getDefaultMessage(),
                            (a, b) -> a));
            return ResponseEntity.badRequest().body(errores);
        }

        Map<String, Object> resultado = presupuestoService.getPrecioMarcapaginas(presupuestoMarcapaginas, locale);
        if ((Double) resultado.get("precio_total") == 0.0 && (Double) resultado.get("precio_unitario") == 0.0) {
            return ResponseEntity.badRequest()
                    .body(messageSource.getMessage("presupuesto.errores.presupuesto-marcapaginas", null, locale));
        }
        return ResponseEntity.ok(resultado);
    }

    // Se hace un post para no tener problemas con la longitud de la URL
    @PostMapping("/public/resumen")
    public ResponseEntity<?> getResumen(@RequestBody Map<String, Object> body, Locale locale) {
        Presupuesto p = objectMapper.convertValue(body.get("presupuesto"), Presupuesto.class);

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> serviciosList = (List<Map<String, Object>>) body.getOrDefault("servicios", List.of());

        return ResponseEntity.ok(presupuestoService.getResumen(p, serviciosList, locale));
    }

}
