package com.imprimelibros.erp.configuracion.margenes_presupuestos;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.context.MessageSource;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.imprimelibros.erp.datatables.DataTable;
import com.imprimelibros.erp.datatables.DataTablesParser;
import com.imprimelibros.erp.datatables.DataTablesRequest;
import com.imprimelibros.erp.datatables.DataTablesResponse;
import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.util.Locale;

import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;

@Controller
@RequestMapping("/configuracion/margenes-presupuesto")
@PreAuthorize("hasRole('SUPERADMIN')")
public class MargenPresupuestoController {

    private final MargenPresupuestoDao repo;
    private final TranslationService translationService;
    private final MessageSource messageSource;

    public MargenPresupuestoController(MargenPresupuestoDao repo, TranslationService translationService,
            MessageSource messageSource) {
        this.repo = repo;
        this.translationService = translationService;
        this.messageSource = messageSource;
    }

    @GetMapping()
    public String listView(Model model, Authentication authentication, Locale locale) {

        List<String> keys = List.of(
                "margenes-presupuesto.delete.title",
                "margenes-presupuesto.delete.text",
                "margenes-presupuesto.eliminar",
                "margenes-presupuesto.delete.button",
                "app.yes",
                "app.cancelar",
                "margenes-presupuesto.delete.ok.title",
                "margenes-presupuesto.delete.ok.text");

        Map<String, String> translations = translationService.getTranslations(locale, keys);
        model.addAttribute("languageBundle", translations);

        return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-list";
    }

    @GetMapping(value = "/datatable", produces = "application/json")
    @ResponseBody
    public DataTablesResponse<Map<String, Object>> datatable(HttpServletRequest request, Authentication authentication,
            Locale locale) {

        DataTablesRequest dt = DataTablesParser.from(request);

        List<String> searchable = List.of(
                "id",
                "tiradaMin", "tiradaMax",
                "margenMin", "margenMax");

        List<String> orderable = List.of(
                "id",
                "tipoEncuadernacion",
                "tipoCubierta",
                "tiradaMin",
                "tiradaMax",
                "margenMin",
                "margenMax");

        Specification<MargenPresupuesto> base = (root, query, cb) -> cb.conjunction();
        long total = repo.count();

        return DataTable
                .of(repo, MargenPresupuesto.class, dt, searchable) // 'searchable' en DataTable.java
                // edita columnas "reales":
                .orderable(orderable)
                .add("actions", (margen) -> {
                    return "<div class=\"hstack gap-3 flex-wrap\">\n" +
                            "                    <a href=\"javascript:void(0);\" data-id=\"" + margen.getId()
                            + "\" class=\"link-success btn-edit-margen fs-15\"><i class=\"ri-edit-2-line\"></i></a>\n"
                            + "                    <a href=\"javascript:void(0);\" data-id=\"" + margen.getId()
                            + "\" class=\"link-danger btn-delete-margen fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>\n"
                            + "                </div>";
                })
                .edit("tipoEncuadernacion", (margen) -> {
                    return messageSource.getMessage("presupuesto." + margen.getTipoEncuadernacion().name(), null,
                            locale);
                })
                .edit("tipoCubierta", (margen) -> {
                    return messageSource.getMessage("presupuesto." + margen.getTipoCubierta().name(), null, locale);
                })
                .where(base)
                // Filtros custom:
                .filter((builder, req) -> {
                    String fEncuadernacion = Optional.ofNullable(req.raw.get("f_encuadernacion")).orElse("").trim();
                    if (!fEncuadernacion.isEmpty()) {
                        boolean added = false;
                        // 1) Si llega el nombre del enum (p.ej. "fresado", "cosido", ...)
                        try {
                            var encEnum = TipoEncuadernacion.valueOf(fEncuadernacion);
                            builder.add((root, q, cb) -> cb.equal(root.get("tipoEncuadernacion"), encEnum));
                            added = true;
                        } catch (IllegalArgumentException ignored) {
                        }
                        // 2) Si llega la clave i18n (p.ej. "presupuesto.fresado", ...)
                        if (!added) {
                            Arrays.stream(TipoEncuadernacion.values())
                                    .filter(e -> e.getMessageKey().equals(fEncuadernacion))
                                    .findFirst()
                                    .ifPresent(encEnum -> builder
                                            .add((root, q, cb) -> cb.equal(root.get("tipoEncuadernacion"), encEnum)));
                        }
                    }

                    // --- Cubierta ---
                    String fCubierta = Optional.ofNullable(req.raw.get("f_cubierta")).orElse("").trim();
                    if (!fCubierta.isEmpty()) {
                        boolean added = false;
                        // 1) Si llega el nombre del enum (p.ej. "tapaBlanda", "tapaDura",
                        // "tapaDuraLomoRedondo")
                        try {
                            var cubEnum = TipoCubierta.valueOf(fCubierta);
                            builder.add((root, q, cb) -> cb.equal(root.get("tipoCubierta"), cubEnum));
                            added = true;
                        } catch (IllegalArgumentException ignored) {
                        }
                        // 2) Si llega la clave i18n (p.ej. "presupuesto.tapa-blanda", ...)
                        if (!added) {
                            Arrays.stream(TipoCubierta.values())
                                    .filter(e -> e.getMessageKey().equals(fCubierta))
                                    .findFirst()
                                    .ifPresent(cubEnum -> builder
                                            .add((root, q, cb) -> cb.equal(root.get("tipoCubierta"), cubEnum)));
                        }
                    }
                })
                .toJson(total);
    }

    @GetMapping("form")
    public String getForm(@RequestParam(required = false) Long id,
            MargenPresupuesto margenPresupuesto,
            BindingResult binding,
            Model model,
            HttpServletResponse response,
            Locale locale) {

        if (id != null) {
            var opt = repo.findById(id);
            if (opt.isEmpty()) {
                binding.reject("usuarios.error.noEncontrado",
                        messageSource.getMessage("usuarios.error.noEncontrado", null, locale));
                response.setStatus(404);
                model.addAttribute("action", "/users/" + id);
                return "imprimelibros/users/user-form :: userForm";
            }

            model.addAttribute("margenPresupuesto", opt.get());
            model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
        } else {
            // Crear: valores por defecto
            model.addAttribute("margenPresupuesto", new MargenPresupuesto());
            model.addAttribute("action", "/configuracion/margenes-presupuesto");
        }
        return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
    }

    @PostMapping
    public String create(
            MargenPresupuesto margenPresupuesto,
            BindingResult binding,
            Model model,
            HttpServletResponse response,
            Locale locale) {

        if (binding.hasErrors()) {
            response.setStatus(422); 
            model.addAttribute("action", "/configuracion/margenes-presupuesto");
            return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
        }

        MargenPresupuesto data = new MargenPresupuesto();
        data.setTipoEncuadernacion(margenPresupuesto.getTipoEncuadernacion());
        data.setTipoCubierta(margenPresupuesto.getTipoCubierta());
        data.setTiradaMin(margenPresupuesto.getTiradaMin());
        data.setTiradaMax(margenPresupuesto.getTiradaMax());
        data.setMargenMax(margenPresupuesto.getMargenMax());
        data.setMargenMin(margenPresupuesto.getMargenMin());

        try {
            repo.save(data);
        } catch (jakarta.validation.ConstraintViolationException vex) {
            // Errores de Bean Validation disparados al flush (incluye tu @NoRangeOverlap)
            vex.getConstraintViolations().forEach(v -> {
                // intenta asignar al campo si existe, si no, error global
                String path = v.getPropertyPath() != null ? v.getPropertyPath().toString() : null;
                String code = v.getMessage() != null ? v.getMessage().trim() : "";

                if (code.startsWith("{") && code.endsWith("}")) {
                    code = code.substring(1, code.length() - 1); // -> "validation.required"
                }

                if (path != null && binding.getFieldError(path) == null) {

                    binding.rejectValue(path, "validation", messageSource.getMessage(code, null, locale));
                } else {
                    binding.reject("validation", messageSource.getMessage(code, null, locale));
                }
            });
            response.setStatus(422);
            model.addAttribute("action", "/configuracion/margenes-presupuesto");
            return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
        }
        response.setStatus(201);
        return null;
    }


    @PutMapping("/{id}")
    public String edit(
            @PathVariable Long id,
            MargenPresupuesto form,
            BindingResult binding,
            Model model,
            HttpServletResponse response,
            Locale locale) {

        var uOpt = repo.findById(id);
        if (uOpt.isEmpty()) {
            binding.reject("usuarios.error.noEncontrado",
                    messageSource.getMessage("usuarios.error.noEncontrado", null, locale));
        }

        if (binding.hasErrors()) {
            response.setStatus(422);
            model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
            return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
        }

        var entity = uOpt.get();

        // 3) Copiar solamente campos editables
        entity.setTipoEncuadernacion(form.getTipoEncuadernacion());
        entity.setTipoCubierta(form.getTipoCubierta());
        entity.setTiradaMin(form.getTiradaMin());
        entity.setTiradaMax(form.getTiradaMax());
        entity.setMargenMax(form.getMargenMax());
        entity.setMargenMin(form.getMargenMin());

        try {
            repo.saveAndFlush(entity);

        } catch (jakarta.validation.ConstraintViolationException vex) {
            // Errores de Bean Validation disparados al flush (incluye tu @NoRangeOverlap)
            vex.getConstraintViolations().forEach(v -> {
                // intenta asignar al campo si existe, si no, error global
                String path = v.getPropertyPath() != null ? v.getPropertyPath().toString() : null;
                String code = v.getMessage() != null ? v.getMessage().trim() : "";

                if (code.startsWith("{") && code.endsWith("}")) {
                    code = code.substring(1, code.length() - 1); // -> "validation.required"
                }

                if (path != null && binding.getFieldError(path) == null) {

                    binding.rejectValue(path, "validation", messageSource.getMessage(code, null, locale));
                } else {
                    binding.reject("validation", messageSource.getMessage(code, null, locale));
                }
            });
            response.setStatus(422);
            model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
            return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";

        } catch (org.springframework.dao.DataIntegrityViolationException dex) {
            // Uniques, FKs, checks… mensajes de la BD
            String msg = dex.getMostSpecificCause() != null ? dex.getMostSpecificCause().getMessage()
                    : dex.getMessage();
            binding.reject("db.error", messageSource.getMessage(msg, null, locale));
            response.setStatus(422);
            model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
            return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
        }

        response.setStatus(204);
        return null;
    }

    @DeleteMapping("/{id}")
    @Transactional
    public ResponseEntity<?> delete(@PathVariable Long id, Authentication auth, Locale locale) {
        
        return repo.findById(id).map(u -> {
            try {
                

                u.setDeleted(true);
                u.setDeletedAt(LocalDateTime.now());
                
                repo.save(u); // ← NO delete(); guardamos el soft delete con deleted_by relleno
                return ResponseEntity.ok(Map.of("message",
                        messageSource.getMessage("margenes-presupuesto.exito.eliminado", null, locale)));
            } catch (Exception ex) {
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                        .body(Map.of("message",
                                messageSource.getMessage("margenes-presupuesto.error.delete-internal-error", null, locale)));
            }
        }).orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(Map.of("message", messageSource.getMessage("margenes-presupuesto.error.not-found", null, locale))));
    }
}
