package de.votian.web.view;

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.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.html.Paragraph;
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.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
import de.votian.web.model.SessionData;
import de.votian.web.service.RestClientService;
import org.springframework.core.ParameterizedTypeReference;

import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Route(value = "exports", layout = MainLayout.class)
@PageTitle("Votian - Datenexport")
public class GenericExportWorkspaceView extends VerticalLayout {

    private final RestClientService restClient;
    private final SessionData sessionData;

    private final ComboBox<CategoryOption> categoryField = new ComboBox<>("Kategorie");
    private final TextField customerFilterField = new TextField("Kundenfilter");
    private final DatePicker fromDate = new DatePicker("Von");
    private final DatePicker toDate = new DatePicker("Bis");
    private final ComboBox<StatusOption> statusFilterField = new ComboBox<>("Status");
    private final TextField delimiterField = new TextField("Delimiter");
    private final TextField fileNameField = new TextField("Dateiname");
    private final Checkbox includeHeadlineField = new Checkbox("Kopfzeile einbauen");
    private final Button runExportButton = new Button("Export erzeugen");

    private final Grid<ProfileRow> profileGrid = new Grid<>(ProfileRow.class, false);
    private final TextField profileNameField = new TextField("Profilname");
    private final TextField fileExtensionField = new TextField("Dateiendung");
    private final TextArea parameterStringField = new TextArea("Parameterstring");
    private final Checkbox profileHeadlineField = new Checkbox("Profil-Kopfzeile");
    private final TextField beginOfLineField = new TextField("BOL");
    private final TextField endOfLineField = new TextField("EOL");
    private final TextField beginOfFileField = new TextField("BOF");
    private final TextField endOfFileField = new TextField("EOF");
    private final Button profileNewButton = new Button("Neu");
    private final Button profileSaveButton = new Button("Speichern");
    private final Button profileDeleteButton = new Button("Loeschen");

    private final Grid<ExportFileRow> fileGrid = new Grid<>(ExportFileRow.class, false);

    private final List<CategoryOption> categories = new ArrayList<>();
    private final List<ProfileRow> profiles = new ArrayList<>();

    private Long selectedProfileId;
    private boolean customerScoped;
    private Long scopedCustomerId;

    public GenericExportWorkspaceView(RestClientService restClient, SessionData sessionData) {
        this.restClient = restClient;
        this.sessionData = sessionData;

        setSizeFull();
        setPadding(true);
        setSpacing(true);

        configureFilters();
        configureProfileGrid();
        configureFileGrid();
        configureProfileEditor();

        add(
                new H3("Generischer Datenexport"),
                buildFilterBar(),
                buildProfileSection(),
                buildFilesSection()
        );
        expand(profileGrid, fileGrid);

        loadBootstrap();
    }

    private void configureFilters() {
        categoryField.setItemLabelGenerator(CategoryOption::label);
        categoryField.setWidth("320px");
        categoryField.addValueChangeListener(event -> {
            clearProfileForm();
            loadProfiles();
        });

        customerFilterField.setWidth("220px");
        customerFilterField.setClearButtonVisible(true);
        fromDate.setValue(LocalDate.now().withDayOfMonth(1));
        toDate.setValue(LocalDate.now());

        statusFilterField.setItems(
                new StatusOption(null, "Alle"),
                new StatusOption(1, "Nur nicht exportierte"),
                new StatusOption(2, "Nur exportierte")
        );
        statusFilterField.setItemLabelGenerator(StatusOption::label);
        statusFilterField.setValue(statusFilterField.getListDataView().getItems().findFirst().orElse(null));
        statusFilterField.setWidth("220px");

        delimiterField.setValue(";");
        delimiterField.setWidth("100px");
        fileNameField.setWidth("260px");

        runExportButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        runExportButton.addClickListener(event -> runExport());
    }

    private void configureProfileGrid() {
        profileGrid.addColumn(ProfileRow::name).setHeader("Profil").setFlexGrow(1).setFrozen(true);
        profileGrid.addColumn(ProfileRow::fileExtension).setHeader("Ext.").setAutoWidth(true);
        profileGrid.addColumn(row -> row.headline() ? "Ja" : "").setHeader("Headline").setAutoWidth(true);
        profileGrid.setHeight("220px");
        profileGrid.asSingleSelect().addValueChangeListener(event -> fillProfileForm(event.getValue()));
    }

    private void configureFileGrid() {
        fileGrid.addColumn(ExportFileRow::storedAt).setHeader("Zeitpunkt").setAutoWidth(true).setFrozen(true);
        fileGrid.addColumn(ExportFileRow::fileName).setHeader("Datei").setFlexGrow(1);
        fileGrid.addColumn(ExportFileRow::customerCode).setHeader("Kunde").setAutoWidth(true);
        fileGrid.addColumn(row -> row.ftpUploaded() ? "Ja" : "").setHeader("FTP").setAutoWidth(true);
        fileGrid.addComponentColumn(this::downloadAnchor).setHeader("Download").setAutoWidth(true);
        fileGrid.addComponentColumn(row -> {
            Button delete = new Button("Loeschen");
            delete.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
            delete.addClickListener(event -> deleteFile(row));
            return delete;
        }).setHeader("Aktion").setAutoWidth(true);
        fileGrid.setSizeFull();
    }

    private void configureProfileEditor() {
        parameterStringField.setWidthFull();
        parameterStringField.setMinHeight("180px");

        profileSaveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        profileDeleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR);

        profileNewButton.addClickListener(event -> clearProfileForm());
        profileSaveButton.addClickListener(event -> saveProfile());
        profileDeleteButton.addClickListener(event -> deleteProfile());
    }

    private HorizontalLayout buildFilterBar() {
        HorizontalLayout layout = new HorizontalLayout(
                categoryField,
                customerFilterField,
                fromDate,
                toDate,
                statusFilterField,
                delimiterField,
                fileNameField,
                includeHeadlineField,
                runExportButton
        );
        layout.setAlignItems(Alignment.BASELINE);
        layout.getStyle().set("flex-wrap", "wrap");
        return layout;
    }

    private VerticalLayout buildProfileSection() {
        FormLayout form = new FormLayout();
        form.setResponsiveSteps(
                new FormLayout.ResponsiveStep("0", 1),
                new FormLayout.ResponsiveStep("900px", 4)
        );
        form.add(profileNameField, fileExtensionField, profileHeadlineField, beginOfLineField, endOfLineField, beginOfFileField, endOfFileField, parameterStringField);
        form.setColspan(parameterStringField, 4);

        HorizontalLayout actions = new HorizontalLayout(profileNewButton, profileSaveButton, profileDeleteButton);
        actions.setAlignItems(Alignment.BASELINE);

        VerticalLayout section = new VerticalLayout(
                new Paragraph("Profile arbeiten direkt auf `exportparameters`. Der Parameterstring bleibt bewusst editierbar, waehrend der NG-Workspace die Exportdateien nativ erzeugt und in `exportfiles` archiviert."),
                new H4("Exportprofile"),
                profileGrid,
                form,
                actions
        );
        section.setPadding(false);
        section.setSpacing(true);
        section.setSizeFull();
        return section;
    }

    private VerticalLayout buildFilesSection() {
        VerticalLayout section = new VerticalLayout(
                new H4("Exportdateien"),
                fileGrid
        );
        section.setPadding(false);
        section.setSpacing(true);
        section.setSizeFull();
        return section;
    }

    private void loadBootstrap() {
        Long headquartersId = sessionData.getHeadquartersId();
        if (headquartersId == null) {
            return;
        }
        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> bootstrap = restClient.get(
                    "/export-workspace/bootstrap?hqId=" + headquartersId,
                    LinkedHashMap.class
            );
            customerScoped = Boolean.TRUE.equals(bootstrap.get("customerScoped"));
            scopedCustomerId = toLong(bootstrap.get("customerId"));
            customerFilterField.setEnabled(!customerScoped);
            categories.clear();
            Object rawCategories = bootstrap.get("categories");
            if (rawCategories instanceof List<?> list) {
                for (Object item : list) {
                    if (item instanceof Map<?, ?> row) {
                        Long id = toLong(row.get("id"));
                        if (id != null) {
                            categories.add(new CategoryOption(id, str(row.get("name"))));
                        }
                    }
                }
            }
            categoryField.setItems(categories);
            if (!categories.isEmpty()) {
                categoryField.setValue(categories.get(0));
            }
            loadProfiles();
            loadFiles();
        } catch (Exception e) {
            showError("Exportworkspace konnte nicht initialisiert werden: " + e.getMessage());
        }
    }

    private void loadProfiles() {
        CategoryOption category = categoryField.getValue();
        Long headquartersId = sessionData.getHeadquartersId();
        if (category == null || headquartersId == null) {
            profileGrid.setItems(List.of());
            return;
        }
        try {
            String path = "/export-workspace/profiles?hqId=" + headquartersId
                    + "&categoryId=" + category.id()
                    + customerScopeQuery();
            List<LinkedHashMap<String, Object>> result = restClient.getList(path, new ParameterizedTypeReference<>() {});
            profiles.clear();
            for (Map<String, Object> row : result) {
                Long id = toLong(row.get("id"));
                if (id == null) {
                    continue;
                }
                profiles.add(new ProfileRow(
                        id,
                        str(row.get("name")),
                        str(row.get("parameterString")),
                        Boolean.TRUE.equals(row.get("headline")),
                        str(row.get("beginOfLineChars")),
                        str(row.get("endOfLineChars")),
                        str(row.get("beginOfFileChars")),
                        str(row.get("endOfFileChars")),
                        str(row.get("fileExtension"))
                ));
            }
            profileGrid.setItems(profiles);
        } catch (Exception e) {
            showError("Exportprofile konnten nicht geladen werden: " + e.getMessage());
        }
    }

    private void loadFiles() {
        Long headquartersId = sessionData.getHeadquartersId();
        if (headquartersId == null) {
            return;
        }
        try {
            String path = "/export-workspace/files?hqId=" + headquartersId + customerScopeQuery();
            List<LinkedHashMap<String, Object>> result = restClient.getList(path, new ParameterizedTypeReference<>() {});
            List<ExportFileRow> rows = new ArrayList<>();
            for (Map<String, Object> row : result) {
                Long id = toLong(row.get("id"));
                if (id == null) {
                    continue;
                }
                rows.add(new ExportFileRow(
                        id,
                        str(row.get("fileName")),
                        str(row.get("storedAt")),
                        Boolean.TRUE.equals(row.get("ftpUploaded")),
                        str(row.get("customerCode"))
                ));
            }
            fileGrid.setItems(rows);
        } catch (Exception e) {
            showError("Exportdateien konnten nicht geladen werden: " + e.getMessage());
        }
    }

    private void runExport() {
        CategoryOption category = categoryField.getValue();
        Long headquartersId = sessionData.getHeadquartersId();
        if (category == null || headquartersId == null) {
            return;
        }
        try {
            Map<String, Object> body = new LinkedHashMap<>();
            body.put("hqId", headquartersId);
            body.put("customerId", scopedCustomerId);
            body.put("categoryId", category.id());
            body.put("profileId", selectedProfileId);
            body.put("from", fromDate.getValue());
            body.put("to", toDate.getValue());
            body.put("statusFilter", statusFilterField.getValue() != null ? statusFilterField.getValue().id() : null);
            body.put("customerCodeFilter", customerFilterField.getValue());
            body.put("delimiter", delimiterField.getValue());
            body.put("fileName", fileNameField.getValue());
            body.put("includeHeadline", includeHeadlineField.getValue());

            @SuppressWarnings("unchecked")
            Map<String, Object> result = restClient.post("/export-workspace/run", body, LinkedHashMap.class);
            showInfo("Export erzeugt: " + toInteger(result.get("rowCount")) + " Zeilen.");
            loadFiles();
        } catch (Exception e) {
            showError("Export konnte nicht erzeugt werden: " + e.getMessage());
        }
    }

    private void saveProfile() {
        CategoryOption category = categoryField.getValue();
        Long headquartersId = sessionData.getHeadquartersId();
        if (category == null || headquartersId == null) {
            return;
        }
        try {
            Map<String, Object> body = new LinkedHashMap<>();
            body.put("hqId", headquartersId);
            body.put("customerId", scopedCustomerId);
            body.put("categoryId", category.id());
            body.put("name", profileNameField.getValue());
            body.put("parameterString", parameterStringField.getValue());
            body.put("headline", profileHeadlineField.getValue());
            body.put("beginOfLineChars", beginOfLineField.getValue());
            body.put("endOfLineChars", endOfLineField.getValue());
            body.put("beginOfFileChars", beginOfFileField.getValue());
            body.put("endOfFileChars", endOfFileField.getValue());
            body.put("fileExtension", fileExtensionField.getValue());

            if (selectedProfileId != null) {
                restClient.put("/export-workspace/profiles/" + selectedProfileId, body);
            } else {
                @SuppressWarnings("unchecked")
                Map<String, Object> saved = restClient.post("/export-workspace/profiles", body, LinkedHashMap.class);
                selectedProfileId = toLong(saved.get("id"));
            }
            loadProfiles();
            showInfo("Exportprofil gespeichert.");
        } catch (Exception e) {
            showError("Exportprofil konnte nicht gespeichert werden: " + e.getMessage());
        }
    }

    private void deleteProfile() {
        CategoryOption category = categoryField.getValue();
        Long headquartersId = sessionData.getHeadquartersId();
        if (selectedProfileId == null || category == null || headquartersId == null) {
            return;
        }
        try {
            restClient.delete("/export-workspace/profiles/" + selectedProfileId
                    + "?hqId=" + headquartersId
                    + "&categoryId=" + category.id()
                    + customerScopeQuery());
            clearProfileForm();
            loadProfiles();
            showInfo("Exportprofil geloescht.");
        } catch (Exception e) {
            showError("Exportprofil konnte nicht geloescht werden: " + e.getMessage());
        }
    }

    private void deleteFile(ExportFileRow row) {
        Long headquartersId = sessionData.getHeadquartersId();
        if (headquartersId == null) {
            return;
        }
        try {
            restClient.delete("/export-workspace/files/" + row.id()
                    + "?hqId=" + headquartersId
                    + customerScopeQuery());
            loadFiles();
            showInfo("Exportdatei geloescht.");
        } catch (Exception e) {
            showError("Exportdatei konnte nicht geloescht werden: " + e.getMessage());
        }
    }

    private Anchor downloadAnchor(ExportFileRow row) {
        String path = "/export-workspace/files/" + row.id()
                + "/content?hqId=" + sessionData.getHeadquartersId()
                + customerScopeQuery();
        StreamResource resource = new StreamResource(
                row.fileName(),
                () -> new ByteArrayInputStream(restClient.getBytes(path))
        );
        Anchor anchor = new Anchor(resource, "Download");
        anchor.getElement().setAttribute("download", true);
        return anchor;
    }

    private void fillProfileForm(ProfileRow row) {
        if (row == null) {
            clearProfileForm();
            return;
        }
        selectedProfileId = row.id();
        profileNameField.setValue(row.name());
        fileExtensionField.setValue(row.fileExtension());
        parameterStringField.setValue(row.parameterString());
        profileHeadlineField.setValue(row.headline());
        beginOfLineField.setValue(row.beginOfLineChars());
        endOfLineField.setValue(row.endOfLineChars());
        beginOfFileField.setValue(row.beginOfFileChars());
        endOfFileField.setValue(row.endOfFileChars());
    }

    private void clearProfileForm() {
        selectedProfileId = null;
        profileGrid.deselectAll();
        profileNameField.clear();
        fileExtensionField.clear();
        parameterStringField.clear();
        profileHeadlineField.setValue(false);
        beginOfLineField.clear();
        endOfLineField.clear();
        beginOfFileField.clear();
        endOfFileField.clear();
    }

    private String customerScopeQuery() {
        return scopedCustomerId != null ? "&customerId=" + scopedCustomerId : "";
    }

    private void showInfo(String message) {
        Notification notification = Notification.show(message, 3000, Notification.Position.TOP_END);
        notification.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
    }

    private void showError(String message) {
        Notification notification = Notification.show(message, 5000, Notification.Position.TOP_END);
        notification.addThemeVariants(NotificationVariant.LUMO_ERROR);
    }

    private static String str(Object value) {
        return value != null ? String.valueOf(value) : "";
    }

    private static Long toLong(Object value) {
        if (value instanceof Number number) {
            return number.longValue();
        }
        if (value != null && !String.valueOf(value).isBlank()) {
            return Long.parseLong(String.valueOf(value));
        }
        return null;
    }

    private static Integer toInteger(Object value) {
        if (value instanceof Number number) {
            return number.intValue();
        }
        if (value != null && !String.valueOf(value).isBlank()) {
            return Integer.parseInt(String.valueOf(value));
        }
        return null;
    }

    private record CategoryOption(Long id, String label) {
    }

    private record StatusOption(Integer id, String label) {
    }

    private record ProfileRow(Long id,
                              String name,
                              String parameterString,
                              boolean headline,
                              String beginOfLineChars,
                              String endOfLineChars,
                              String beginOfFileChars,
                              String endOfFileChars,
                              String fileExtension) {
    }

    private record ExportFileRow(Long id,
                                 String fileName,
                                 String storedAt,
                                 boolean ftpUploaded,
                                 String customerCode) {
    }
}
