package mx.gob.jumapacelaya.ui;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridVariant;
import com.vaadin.flow.component.grid.HeaderRow;
import com.vaadin.flow.component.grid.dataview.GridListDataView;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.html.H5;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import jakarta.annotation.security.PermitAll;
import mx.gob.jumapacelaya.models.PlanAnual;
import mx.gob.jumapacelaya.services.DatabaseService;
import org.apache.poi.ss.usermodel.*;
import org.springframework.core.env.Environment;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.time.LocalDate;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

import javax.xml.crypto.Data;

import static java.util.regex.Pattern.matches;

@PermitAll
@PageTitle("Plan Anual de Mantenimiento")
@Route(value = "/", layout = MainLayout.class)
@CssImport("./themes/sistema-mantenimiento/styles.css")
public class PlanAnualView extends VerticalLayout {

    private final Environment env;

    HorizontalLayout header = new HorizontalLayout();
    H4 titulo = new H4();
    H5 titulo1 = new H5();
    Button btnInsertar;
    private byte[] fileContent;
    VerticalLayout gridLayout = new VerticalLayout();
    HorizontalLayout uploadLayout = new HorizontalLayout();
    HorizontalLayout checkLayout = new HorizontalLayout();
    private final DatabaseService databaseService;

    public PlanAnualView(DatabaseService databaseService, Environment env) {
        this.databaseService = databaseService;
        this.env = env;
        setupHeader();
        Grid<PlanAnual> planAnualGrid = setupGrid();

        // Notificación de conexión activa
        String perfil = String.join(",", env.getActiveProfiles());
        String url = env.getProperty("db.url");
        Notification.show("Conectado a: " + perfil + "\nURL DB: " + url, 5000, Notification.Position.BOTTOM_START);

        // Obtenemos la lista de items y establecemos un GridListDataView para gestionar
        // los datos
        List<PlanAnual> planAnualItems = databaseService.getPlanAnual();
        GridListDataView<PlanAnual> dataView = planAnualGrid.setItems(planAnualItems);



        // Se crea el filtro para el grid.
        PlanAnualFilter planAnualFilter = new PlanAnualFilter(dataView);
        planAnualFilter.setExcludeRealizado(true);

        HeaderRow headerRow = planAnualGrid.appendHeaderRow();
        headerRow.getCell(planAnualGrid.getColumnByKey("smtColumnKey"))
            .setComponent(createFilterHeader("S.M.T", planAnualFilter::setSmt));
        
        headerRow.getCell(planAnualGrid.getColumnByKey("equipoColumnKey"))
            .setComponent(createFilterHeader("Equipo", planAnualFilter::setEquipo));

        headerRow.getCell(planAnualGrid.getColumnByKey("departamentoColumnKey"))
            .setComponent(createFilterHeader("Departamento", planAnualFilter::setDepartamento));


        
        // Componente UPLOAD para subir archivos
        MemoryBuffer buffer = new MemoryBuffer();
        Upload upload = new Upload(buffer);
        upload.setAcceptedFileTypes(".xls", ".xlsx");
        upload.setMaxFiles(1);
        upload.setDropLabel(new com.vaadin.flow.component.html.Span("Arrastra un archivo Excel aquí o selecciona uno"));

        upload.addSucceededListener(event -> {
            try {
                // Almacena el contenido del archivo en un arreglo de bytes
                try (InputStream inputStream = buffer.getInputStream();
                        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {

                    inputStream.transferTo(byteArrayOutputStream);
                    fileContent = byteArrayOutputStream.toByteArray();
                }

                // Validación del archivo cargado
                List<String> errores = validarExcel(new ByteArrayInputStream(fileContent));
                if (!errores.isEmpty()) {
                    // Crear notificación con mensaje de error y sin duración
                    final Notification ntfError = new Notification();
                    ntfError.setText("Errores en el archivo: " + String.join(", ", errores));
                    ntfError.setDuration(0); // La notificación no se cerrará automáticamente
                    ntfError.setPosition(Notification.Position.MIDDLE);
                    ntfError.addThemeVariants(NotificationVariant.LUMO_ERROR);

                    // Texto del mensaje de error
                    Span errorMessage = new Span("Errores en el archivo: " + String.join(", ", errores));
                    errorMessage.getStyle().set("margin-right", "10px");

                    // Crear botón de cierre
                    final Button closeButton = new Button(new Icon(VaadinIcon.CLOSE));
                    closeButton.addClickListener(e -> ntfError.close());

                    // Agregar el mensaje y el botón de cierre a la notificación
                    HorizontalLayout notificationLayout = new HorizontalLayout(errorMessage, closeButton);
                    ntfError.add(notificationLayout);

                    ntfError.open(); // Abrir la notificación en pantalla

                    // Ocultar el botón de insertar si hay errores
                    btnInsertar.setVisible(false);
                } else {
                    btnInsertar.setVisible(true);
                }
            } catch (IOException e) {
                Notification.show("Error al leer el archivo: " + e.getMessage())
                        .addThemeVariants(NotificationVariant.LUMO_ERROR);
            }
        });

        btnInsertar = new Button("Insertar archivo", event -> insertarDatos());
        btnInsertar.setVisible(false);

        gridLayout.add(planAnualGrid);
        gridLayout.setSizeFull();

        uploadLayout.add(upload, btnInsertar);
        toggleLayouts(dataView);

        RadioButtonGroup<String> radioFiltro = new RadioButtonGroup<>();
        radioFiltro.setItems("Ver realizados", "Ver pendientes");
        radioFiltro.setValue("Ver pendientes");

        radioFiltro.addValueChangeListener(event -> {
            String value = event.getValue();
            if ("Ver pendientes".equals(value)) {
                planAnualFilter.setExcludeRealizado(true);
            } else if ("Ver realizados".equals(value)) {
                planAnualFilter.setExcludeRealizado(false);
            }
        });

        checkLayout = new HorizontalLayout(radioFiltro);
        checkLayout.setWidthFull();
        checkLayout.setMargin(false);
        checkLayout.setSpacing(true);
        checkLayout.setJustifyContentMode(JustifyContentMode.CENTER);

        this.setPadding(false);
        this.setMargin(false);
        this.setSpacing(false);
        this.setSizeFull();
        add(header, checkLayout, gridLayout, uploadLayout);
    }

    private void setupHeader() {
        VerticalLayout headerLayout = new VerticalLayout();
        headerLayout.addClassName("plan-anual-header");

        titulo.addClassName("plan-anual-titulo");
        titulo1.addClassName("plan-anual-titulo1");
        titulo.setText("Plan Anual de Mantenimiento Preventivo de Equipo de Computo");
        titulo1.setText(Year.now().toString());

        TextField nomenclaturaTxt = new TextField();
        nomenclaturaTxt.setValue("FR01-PA-7.1.3-02");
        nomenclaturaTxt.setReadOnly(true);
        nomenclaturaTxt.addClassName("nomenclatura-txt");

        header.setAlignSelf(Alignment.CENTER, titulo, titulo1);
        headerLayout.add(titulo, titulo1);
        add(headerLayout);
    }

    private Grid<PlanAnual> setupGrid() {

        Grid<PlanAnual> planAnualGrid = new Grid<>(PlanAnual.class, false);

        planAnualGrid.addColumn((ValueProvider<PlanAnual, Integer>) PlanAnual::getNumero)
                .setHeader("No.").setSortable(true);

        // Botón condicional
        planAnualGrid.addComponentColumn(planAnual -> {
            String estado = planAnual.getEstado();
            Button btn;

            if ("PENDIENTE".equalsIgnoreCase(estado)) {
                btn = new Button(new Icon(VaadinIcon.EDIT));
                btn.setTooltipText("Realizar mantenimiento");
                btn.getStyle().set("color", "#A02142");

                btn.addClickListener(event -> {
                    int idPlananual = planAnual.getNumero();
                    LocalDate fechaSistema = LocalDate.now();
                    String nomEquipo = planAnual.getNomEquipo();
                    String departamento = planAnual.getDepartamento();

                    btn.getUI().ifPresent(ui -> ui.navigate(
                            "mantenimiento?id=" + idPlananual +
                                    "&fecha=" + fechaSistema +
                                    "&tipo=1" +
                                    "&nomEquipo=" + nomEquipo +
                                    "&departamento=" + departamento));
                });

            } else if ("REALIZADO".equalsIgnoreCase(estado)) {
                btn = new Button(new Icon(VaadinIcon.EYE));
                btn.setTooltipText("Ver detalles");
                btn.getStyle().set("color", "#A02142");

                btn.addClickListener(event -> {
                    int idPlananual = planAnual.getNumero();
                    btn.getUI().ifPresent(ui -> ui.navigate(
                            "detalles?id=" + idPlananual));
                });
            } else {
                btn = new Button("N/A");
                btn.setEnabled(false);
            }

            return btn;
        });

        planAnualGrid.addColumn((ValueProvider<PlanAnual, String>) PlanAnual::getNomEquipo)
                .setHeader("Equipo").setAutoWidth(true).setKey("equipoColumnKey");

        planAnualGrid.addColumn((ValueProvider<PlanAnual, String>) PlanAnual::getDepartamento)
                .setHeader("Departamento").setAutoWidth(true).setKey("departamentoColumnKey");

        planAnualGrid.addThemeVariants(GridVariant.LUMO_WRAP_CELL_CONTENT);
        planAnualGrid.addThemeVariants(GridVariant.LUMO_ROW_STRIPES);

        // Fechas formateadas
        planAnualGrid.addColumn(planAnual -> {
            LocalDate fecha = planAnual.getFechaProgramada();
            return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) : "No programada";
        }).setHeader("Fecha Programada").setAutoWidth(true).setSortable(true);

        planAnualGrid.addColumn(planAnual -> {
            LocalDate fecha = planAnual.getFechaMantenimiento();
            return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) : "";
        }).setHeader("Fecha Realización").setAutoWidth(true).setSortable(true);

        planAnualGrid.addColumn((ValueProvider<PlanAnual, String>) PlanAnual::getEstado)
                .setHeader("Estado").setAutoWidth(true);

        planAnualGrid.addColumn((ValueProvider<PlanAnual, String>) PlanAnual::getSmt)
                .setHeader("S.M.T").setKey("smtColumnKey");
        
        // Iconos en columnas booleanas
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isMonitor())).setHeader("Monitor");
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isTeclado())).setHeader("Teclado");
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isMouse())).setHeader("Mouse");
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isRegulador())).setHeader("Regulador");
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isCpu())).setHeader("CPU");
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isImpresora())).setHeader("Impresora");
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isMiniPrint())).setHeader("MiniPrint");
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isLaptop())).setHeader("Laptop");
        planAnualGrid.addComponentColumn(planAnual -> getIcon(planAnual.isEscaner())).setHeader("Escáner");

        // Cargar datos
        planAnualGrid.setItems(databaseService.getPlanAnual());
        return planAnualGrid;
    }

    /*
     * (∩ ͡° ͜ʖ ͡°)⊃━☆ﾟ. * SUSTITUIR VALORES BOOLEANOS POR UN ICONO (∩ ͡° ͜ʖ
     * ͡°)⊃━☆ﾟ. *
     */
    private Icon getIcon(boolean value) {
        if (value) {
            return new Icon(VaadinIcon.CHECK_CIRCLE);
        } else {
            return new Icon();
        }
    }

    /*
     * (∩ ͡° ͜ʖ ͡°)⊃━☆ﾟ. * METODO PARA VALIDAR QUE EL ARCHIOVO TENGA LA ESTRUCTURA
     * CORRECTA (∩ ͡° ͜ʖ ͡°)⊃━☆ﾟ. *
     */
    private List<String> validarExcel(InputStream inputStream) throws IOException {
        List<String> errores = new ArrayList<>();
        Workbook workbook = WorkbookFactory.create(inputStream);
        Sheet sheet = workbook.getSheetAt(0);

        // Lista de nombres de columnas esperados en el encabezado
        List<String> columnas = Arrays.asList("NOMEQUIPO", "DEPARTAMENTO", "MONITOR", "TECLADO", "MOUSE", "REGULADOR",
                "CPU", "IMPRESORA", "MINIPRINT", "LAPTOP", "ESCANER", "FECHAPROG", "TECNICO", "ESTADO");

        // Obtener la primera fila como encabezado
        Row headerRow = sheet.getRow(0);
        if (headerRow != null) {
            // Validar que cada celda en el encabezado tiene el nombre correcto
            for (int i = 0; i < headerRow.getPhysicalNumberOfCells(); i++) {
                Cell cell = headerRow.getCell(i);
                if (cell != null) { // Verificar si la celda no es null antes de acceder a su valor
                    String columnName = cell.getStringCellValue();
                    if (!columnas.contains(columnName)) {
                        errores.add("Columna inesperada: " + columnName);
                    }
                } else {
                    errores.add("Celda vacía en la columna de índice " + i);
                }
            }
        } else {
            errores.add("La hoja no contiene un encabezado en la primera fila.");
        }

        // Verificar si faltan columnas esperadas en el encabezado
        for (String columnName : columnas) {
            boolean found = false;
            if (headerRow != null) {
                for (int i = 0; i < headerRow.getPhysicalNumberOfCells(); i++) {
                    Cell cell = headerRow.getCell(i);
                    if (cell != null && cell.getStringCellValue().equals(columnName)) {
                        found = true;
                        break;
                    }
                }
            }
            if (!found) {
                errores.add("Falta la columna: " + columnName);
            }
        }

        workbook.close();
        return errores;
    }

    // Valida que haya un archivo cargado en el UPLOAD y si si, llama al metodo para
    // insertar el archivo en la base de datos
    private void insertarDatos() {
        if (fileContent == null) {
            Notification.show("Error: No hay archivo cargado.").addThemeVariants(NotificationVariant.LUMO_ERROR);
            return;
        }

        try (InputStream inputStream = new ByteArrayInputStream(fileContent)) {
            databaseService.insertarDesdeExcel(inputStream);
            Notification.show("Archivo insertado correctamente").addThemeVariants(NotificationVariant.LUMO_SUCCESS);
            btnInsertar.setVisible(false); // Ocultar el botón después de la inserción
        } catch (Exception e) {
            Notification.show("Error al insertar el archivo: " + e.getMessage())
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private static Component createFilterHeader(String labelText, Consumer<String> filterChangeConsumer) {
        TextField textField = new TextField();
        textField.setPlaceholder("Buscar...");
        textField.setValueChangeMode(ValueChangeMode.EAGER);
        textField.setClearButtonVisible(true);
        textField.setWidthFull();
        textField.addValueChangeListener(event -> filterChangeConsumer.accept(event.getValue()));

        VerticalLayout layout = new VerticalLayout(textField);
        layout.setSpacing(false);
        layout.setPadding(false);
        return layout;
    }

    private static class PlanAnualFilter {
        private final GridListDataView<PlanAnual> dataView;
        private String smt;
        private String equipo;
        private String departamento;
        private boolean excludeRealizado = true;

        public PlanAnualFilter(GridListDataView<PlanAnual> dataView) {
            this.dataView = dataView;
            this.dataView.addFilter(this::test);
        }

        public void setSmt(String smt) {
            this.smt = smt;
            this.dataView.refreshAll();
        }

        public void setEquipo(String equipo) {
            this.equipo = equipo;
            this.dataView.refreshAll();
        }

        public void setDepartamento(String departamento) {
            this.departamento = departamento;
            this.dataView.refreshAll();
        }

        public void setExcludeRealizado(boolean excludeRealizado) {
            this.excludeRealizado = excludeRealizado;
            this.dataView.refreshAll();
        }

        public boolean test(PlanAnual planAnual) {
            if (planAnual == null) {
                return false; // Avoid NullPointerException
            }

            boolean matchesSmt = matches(planAnual.getSmt(), smt);
            boolean matchesEquipo = matches(planAnual.getNomEquipo(), equipo);
            boolean matchesDepartamento = matches(planAnual.getDepartamento(), departamento);
            
            if (excludeRealizado) {
                // Mostrar solo los pendientes
                return matchesSmt && matchesEquipo && matchesDepartamento 
                        && (planAnual.getEstado() == null || !"REALIZADO".equalsIgnoreCase(planAnual.getEstado()));
            } else {
                // Mostrar solo realizados
                return matchesSmt && matchesEquipo && matchesDepartamento
                        && "REALIZADO".equalsIgnoreCase(planAnual.getEstado());
            }
        }

        private boolean matches(String value, String searchTerm) {
            return searchTerm == null || searchTerm.isEmpty()
                    || value.toLowerCase().contains(searchTerm.toLowerCase());
        }
    }

    // Aqui validamos que haya registros en el plan anual y si no hay muestra el
    // upload para cargar un nuevo plan anual
    private void toggleLayouts(GridListDataView<PlanAnual> dataView) {
        boolean hasItems = dataView.getItemCount() > 0;

        gridLayout.setVisible(hasItems);
        uploadLayout.setVisible(!hasItems);
    }
}
