package de.assecutor.votianlt.pages.view;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;

import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;

import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.Service;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.model.InvoiceTemplate;
import de.assecutor.votianlt.repository.JobRepository;
import de.assecutor.votianlt.repository.ServiceRepository;
import de.assecutor.votianlt.repository.UserRepository;
import de.assecutor.votianlt.security.SecurityService;
import de.assecutor.votianlt.service.CustomerInvoiceService;
import de.assecutor.votianlt.service.InvoiceTemplateService;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.IFrame;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.VaadinSession;

@PageTitle("Rechnung erstellen")
@Route(value = "create_invoice", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@RolesAllowed({ "USER" })
@Slf4j
public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter<String> {

    private final JobRepository jobRepository;
    private final ServiceRepository serviceRepository;
    private final UserRepository userRepository;
    private final CustomerInvoiceService customerInvoiceService;
    private final InvoiceTemplateService invoiceTemplateService;
    private final SecurityService securityService;
    private User currentUser;
    private Job currentJob;
    private List<ServiceRow> gridRows = new ArrayList<>();
    private Grid<ServiceRow> servicesGrid;
    private Div servicesSection;

    /**
     * Helper class to represent a row in the services grid
     */
    public static class ServiceRow {
        private Service service;

        public ServiceRow() {
            this.service = null;
        }

        public ServiceRow(Service service) {
            this.service = service;
        }

        public Service getService() {
            return service;
        }

        public void setService(Service service) {
            this.service = service;
        }

        public boolean isEmpty() {
            return service == null;
        }
    }

    @Autowired
    public CreateInvoiceView(JobRepository jobRepository, ServiceRepository serviceRepository,
            UserRepository userRepository, CustomerInvoiceService customerInvoiceService,
            InvoiceTemplateService invoiceTemplateService, SecurityService securityService) {
        this.jobRepository = jobRepository;
        this.serviceRepository = serviceRepository;
        this.userRepository = userRepository;
        this.customerInvoiceService = customerInvoiceService;
        this.invoiceTemplateService = invoiceTemplateService;
        this.securityService = securityService;
        setSizeFull();
        setPadding(true);
        setSpacing(true);
    }

    /**
     * Lädt die Services, die beim Job-Erstellen ausgewählt wurden.
     */
    private void loadSelectedServicesFromJob() {
        if (currentJob.getServiceIds() != null && !currentJob.getServiceIds().isEmpty()) {
            gridRows.clear();
            for (String serviceId : currentJob.getServiceIds()) {
                serviceRepository.findById(serviceId).ifPresent(service -> {
                    gridRows.add(new ServiceRow(service));
                });
            }
        }
    }

    @Override
    public void setParameter(BeforeEvent event, String jobIdHex) {
        try {
            ObjectId jobId = new ObjectId(jobIdHex);
            loadJob(jobId);
        } catch (Exception e) {
            log.error("Fehler beim Parsen der Job-ID: " + jobIdHex, e);
            add(new Span("Ungültige Auftrags-ID"));
        }
    }

    public void loadJob(ObjectId jobId) {
        currentJob = jobRepository.findById(jobId).orElse(null);
        if (currentJob == null) {
            add(new Span("Auftrag nicht gefunden"));
            return;
        }

        createInvoiceView();
    }

    private void createInvoiceView() {
        removeAll();

        // Title
        H2 title = new H2("Rechnung erstellen für Auftrag " + currentJob.getJobNumber());
        add(title);

        // Load previously selected services from job
        loadSelectedServicesFromJob();

        // Job Details Section
        Div jobDetailsSection = createJobDetailsSection();
        add(jobDetailsSection);

        // Route Information Section (if available)
        if (currentJob.getRouteDistanceKm() != null && currentJob.getRouteDistanceKm() > 0) {
            Div routeInfoSection = createRouteInfoSection();
            add(routeInfoSection);
        }

        // Services Selection Section
        Div servicesSection = createServicesSelectionSection();
        add(servicesSection);

        // Summary Section
        Div summarySection = createSummarySection();
        add(summarySection);

        // Create Invoice Button
        Button createInvoiceButton = new Button("Rechnung erstellen");
        createInvoiceButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        createInvoiceButton.addClickListener(e -> createInvoice());
        createInvoiceButton.getStyle().set("margin-bottom", "15px");
        add(createInvoiceButton);
    }

    private Div createJobDetailsSection() {
        Div section = new Div();
        section.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
                .set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");

        H3 sectionTitle = new H3("Auftragsdetails");
        section.add(sectionTitle);

        // Job information
        VerticalLayout jobInfo = new VerticalLayout();
        jobInfo.setSpacing(true);
        jobInfo.setWidthFull();

        jobInfo.add(new HorizontalLayout(new Span("Auftragsnummer:"), new Span(currentJob.getJobNumber())));
        jobInfo.add(new HorizontalLayout(new Span("Kunde:"),
                new Span(extractCompanyName(currentJob.getCustomerSelection()))));
        jobInfo.add(new HorizontalLayout(new Span("Status:"), new Span(currentJob.getStatus().toString())));
        jobInfo.add(new HorizontalLayout(new Span("Preis:"), new Span(currentJob.getPrice() + " €")));

        section.add(jobInfo);
        return section;
    }

    private Div createRouteInfoSection() {
        Div section = new Div();
        section.getStyle().set("border", "1px solid var(--lumo-primary-color-50pct)")
                .set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
                .set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box")
                .set("background-color", "var(--lumo-primary-color-10pct)");

        H3 sectionTitle = new H3("Streckeninformation");
        sectionTitle.getStyle().set("color", "var(--lumo-primary-text-color)");
        section.add(sectionTitle);

        VerticalLayout routeInfo = new VerticalLayout();
        routeInfo.setSpacing(true);
        routeInfo.setWidthFull();

        Double distance = currentJob.getRouteDistanceKm();
        if (distance != null) {
            routeInfo.add(new HorizontalLayout(new Span("Berechnete Entfernung:"),
                    new Span(String.format("%.1f km", distance))));
        }

        Integer durationSeconds = currentJob.getRouteDurationSeconds();
        if (durationSeconds != null && durationSeconds > 0) {
            String formattedDuration = formatDuration(durationSeconds);
            routeInfo.add(new HorizontalLayout(new Span("Geschätzte Fahrtzeit:"),
                    new Span(formattedDuration)));
        }

        section.add(routeInfo);
        return section;
    }

    private Div createServicesSelectionSection() {
        servicesSection = new Div();
        servicesSection.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
                .set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");

        H3 sectionTitle = new H3("Leistungen");
        servicesSection.add(sectionTitle);

        // Create grid with read-only rows
        servicesGrid = new Grid<>();
        servicesGrid.setWidthFull();
        servicesGrid.setAllRowsVisible(true);

        // Service name column (read-only)
        servicesGrid.addColumn(row -> {
            if (row.getService() != null) {
                return row.getService().getName();
            }
            return "";
        }).setHeader("Leistung").setAutoWidth(true).setFlexGrow(2);

        // Calculation basis column (read-only)
        servicesGrid.addColumn(row -> {
            if (row.getService() != null && row.getService().getCalculationBasis() != null) {
                return switch (row.getService().getCalculationBasis()) {
                case DISTANCE -> "Gefahrene Kilometer";
                case TIME -> "Zeit";
                case FLAT_RATE -> "Pauschal";
                };
            }
            return "";
        }).setHeader("Berechnungsgrundlage").setAutoWidth(true).setFlexGrow(1);

        // Price column (read-only)
        servicesGrid.addColumn(row -> {
            if (row.getService() != null) {
                BigDecimal price = calculateServicePrice(row.getService());
                if (price != null) {
                    return price.setScale(2, RoundingMode.HALF_UP) + " €";
                }
            }
            return "";
        }).setHeader("Preis").setAutoWidth(true).setFlexGrow(1).setKey("price");

        servicesGrid.setItems(gridRows);
        servicesSection.add(servicesGrid);

        return servicesSection;
    }

    private List<Service> getSelectedServices() {
        return gridRows.stream().filter(row -> row.getService() != null).map(ServiceRow::getService).toList();
    }

    private Div createSummarySection() {
        Div section = new Div();
        section.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
                .set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");

        H3 sectionTitle = new H3("Zusammenfassung");
        section.add(sectionTitle);

        // Calculate totals
        BigDecimal netAmount = calculateNetAmount();
        BigDecimal vatRate = calculateAverageVatRate();
        BigDecimal vatAmount = netAmount.multiply(vatRate);
        BigDecimal totalAmount = netAmount.add(vatAmount);

        VerticalLayout summaryInfo = new VerticalLayout();
        summaryInfo.setSpacing(true);
        summaryInfo.setWidthFull();

        // Show only net sum, VAT sums, and total amount without individual services
        summaryInfo.add(new HorizontalLayout(new Span("Nettosumme:"),
                new Span(netAmount.setScale(2, RoundingMode.HALF_UP) + " €")));
        summaryInfo
                .add(new HorizontalLayout(
                        new Span("Mehrwertsteuer ("
                                + vatRate.multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP) + "%):"),
                        new Span(vatAmount.setScale(2, RoundingMode.HALF_UP) + " €")));
        summaryInfo.add(new HorizontalLayout(new Span("Gesamtbetrag (brutto):"),
                new Span(totalAmount.setScale(2, RoundingMode.HALF_UP) + " €")));

        section.add(summaryInfo);
        return section;
    }

    private BigDecimal calculateServicePrice(Service service) {
        if (service.getCalculationBasis() == null) {
            return null;
        }

        if (service.getCalculationBasis() == Service.CalculationBasis.FLAT_RATE && service.getPrice() != null) {
            return service.getPrice();
        } else if (service.getCalculationBasis() == Service.CalculationBasis.DISTANCE
                && service.getPricePerKilometer() != null && currentJob.getRouteDistanceKm() != null) {
            BigDecimal kilometers = BigDecimal.valueOf(currentJob.getRouteDistanceKm());
            return service.getPricePerKilometer().multiply(kilometers);
        } else if (service.getCalculationBasis() == Service.CalculationBasis.TIME
                && service.getPricePer15Minutes() != null && currentJob.getTimeIn15MinUnits() != null) {
            BigDecimal timeUnits = new BigDecimal(currentJob.getTimeIn15MinUnits());
            return service.getPricePer15Minutes().multiply(timeUnits);
        }

        return null;
    }

    private BigDecimal calculateNetAmount() {
        BigDecimal total = BigDecimal.ZERO;

        for (Service service : getSelectedServices()) {
            if (service.getCalculationBasis() == Service.CalculationBasis.FLAT_RATE && service.getPrice() != null) {
                total = total.add(service.getPrice());
            } else if (service.getCalculationBasis() == Service.CalculationBasis.DISTANCE
                    && service.getPricePerKilometer() != null && currentJob.getRouteDistanceKm() != null) {
                BigDecimal kilometers = BigDecimal.valueOf(currentJob.getRouteDistanceKm());
                BigDecimal serviceTotal = service.getPricePerKilometer().multiply(kilometers);
                total = total.add(serviceTotal);
            } else if (service.getCalculationBasis() == Service.CalculationBasis.TIME
                    && service.getPricePer15Minutes() != null && currentJob.getTimeIn15MinUnits() != null) {
                BigDecimal timeUnits = new BigDecimal(currentJob.getTimeIn15MinUnits());
                BigDecimal serviceTotal = service.getPricePer15Minutes().multiply(timeUnits);
                total = total.add(serviceTotal);
            }
        }

        return total;
    }

    private BigDecimal calculateAverageVatRate() {
        List<Service> selectedServicesList = getSelectedServices();
        if (selectedServicesList.isEmpty()) {
            return new BigDecimal("0.19"); // Default 19% VAT
        }

        BigDecimal totalVat = BigDecimal.ZERO;
        int count = 0;

        for (Service service : selectedServicesList) {
            if (service.getVatRate() != null) {
                totalVat = totalVat.add(service.getVatRate());
                count++;
            }
        }

        if (count > 0) {
            return totalVat.divide(new BigDecimal(count), 4, RoundingMode.HALF_UP);
        }

        return new BigDecimal("0.19"); // Default 19% VAT
    }

    private String extractCompanyName(String customerSelection) {
        if (customerSelection == null || customerSelection.isBlank()) {
            return "";
        }
        // Format: "Firmenname | Vorname Nachname" - extrahiere nur den Firmennamen
        int separatorIndex = customerSelection.indexOf(" | ");
        if (separatorIndex > 0) {
            return customerSelection.substring(0, separatorIndex).trim();
        }
        return customerSelection.trim();
    }

    private void createInvoice() {
        if (getSelectedServices().isEmpty()) {
            Notification.show("Bitte wählen Sie mindestens eine Leistung aus", 3000, Notification.Position.BOTTOM_END);
            return;
        }

        // Save the updated job with kilometers and time
        jobRepository.save(currentJob);

        try {
            // Get current user
            Optional<User> currentUserOpt = securityService.getAuthenticatedUser()
                    .flatMap(auth -> userRepository.findByEmail(auth.getUsername()));
            
            if (currentUserOpt.isEmpty()) {
                Notification.show("Fehler: Benutzer nicht gefunden", 3000, Notification.Position.BOTTOM_END);
                return;
            }
            
            currentUser = currentUserOpt.get();
            
            // Load invoice template from service
            Optional<InvoiceTemplate> templateOpt = invoiceTemplateService.getTemplateByUserId(currentUser.getId().toString());
            if (templateOpt.isEmpty()) {
                Notification.show("Fehler: Kein Rechnungstemplate im Profil hinterlegt", 3000, Notification.Position.BOTTOM_END);
                return;
            }
            
            String templateData = templateOpt.get().getTemplateData();
            System.out.println("DEBUG CreateInvoiceView: Template data length: " + (templateData != null ? templateData.length() : 0));
            System.out.println("DEBUG CreateInvoiceView: Template data preview: " + (templateData != null ? templateData.substring(0, Math.min(200, templateData.length())) : "null"));
            
            if (templateData == null || templateData.isBlank()) {
                Notification.show("Fehler: Kein Rechnungstemplate im Profil hinterlegt", 3000, Notification.Position.BOTTOM_END);
                return;
            }
            
            // Generate PDF with template and actual job data
            byte[] pdfBytes = generateInvoicePdfFromTemplate(templateData, currentUser);
            System.out.println("DEBUG CreateInvoiceView: PDF bytes generated: " + (pdfBytes != null ? pdfBytes.length : 0));
            
            // Show PDF in dialog
            showPdfInDialog(pdfBytes, "Rechnung " + currentJob.getJobNumber());
            
        } catch (Exception ex) {
            log.error("Fehler beim Erstellen der Rechnung", ex);
            Notification.show("Fehler beim Erstellen der Rechnung: " + ex.getMessage(), 5000, Notification.Position.BOTTOM_END);
        }
    }
    
    /**
     * Generates an invoice PDF from the template with actual job data.
     */
    private byte[] generateInvoicePdfFromTemplate(String templateData, User currentUser) throws Exception {
        // Calculate totals
        BigDecimal netAmount = calculateNetAmount();
        BigDecimal vatRate = calculateAverageVatRate();
        BigDecimal vatAmount = netAmount.multiply(vatRate);
        BigDecimal totalAmount = netAmount.add(vatAmount);
        
        // Build variable substitution map
        Map<String, String> variables = new HashMap<>();
        
        // Master data (from user profile)
        variables.put("masterdata.company_name", safe(currentUser.getCompany()));
        variables.put("masterdata.contact_name", safe(currentUser.getFirstname()) + " " + safe(currentUser.getName()));
        variables.put("masterdata.street", safe(currentUser.getStreet()) + " " + safe(currentUser.getHouseNumber()));
        variables.put("masterdata.city", safe(currentUser.getZip()) + " " + safe(currentUser.getCity()));
        variables.put("masterdata.email", safe(currentUser.getEmail()));
        variables.put("masterdata.phone", safe(currentUser.getPhone()));
        
        // Customer data (from job)
        String customerSelection = currentJob.getCustomerSelection();
        if (customerSelection != null && !customerSelection.isBlank()) {
            // Format: "Firmenname | Vorname Nachname, Straße Hausnummer, PLZ Ort"
            String[] parts = customerSelection.split("\\|");
            String companyName = parts.length > 0 ? parts[0].trim() : "";
            variables.put("customer.company_name", companyName);
            
            // Extract other customer info if available
            if (parts.length > 1) {
                String remaining = parts[1].trim();
                // Try to extract contact name (before first comma)
                int firstComma = remaining.indexOf(",");
                if (firstComma > 0) {
                    variables.put("customer.contact_name", remaining.substring(0, firstComma).trim());
                    // Rest is address
                    String addressPart = remaining.substring(firstComma + 1).trim();
                    // Split address into street and city
                    String[] addressParts = addressPart.split(",");
                    if (addressParts.length >= 1) {
                        variables.put("customer.street", addressParts[0].trim());
                    }
                    if (addressParts.length >= 2) {
                        variables.put("customer.city", addressParts[1].trim());
                    }
                } else {
                    variables.put("customer.contact_name", remaining);
                }
            }
        } else {
            variables.put("customer.company_name", "");
            variables.put("customer.contact_name", "");
            variables.put("customer.street", "");
            variables.put("customer.city", "");
        }
        
        // Invoice data
        variables.put("invoice.number", currentJob.getJobNumber() + "-" + System.currentTimeMillis());
        variables.put("invoice.date", java.time.LocalDate.now().toString());
        variables.put("invoice.net_total", netAmount.setScale(2, RoundingMode.HALF_UP).toString().replace(".", ",") + " €");
        variables.put("invoice.vat_total", vatAmount.setScale(2, RoundingMode.HALF_UP).toString().replace(".", ",") + " €");
        variables.put("invoice.gross_total", totalAmount.setScale(2, RoundingMode.HALF_UP).toString().replace(".", ",") + " €");
        variables.put("invoice.vat_rate", vatRate.multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP) + "%");
        
        // Job data
        if (currentJob.getRouteDistanceKm() != null) {
            variables.put("job.distance_km", String.format("%.1f", currentJob.getRouteDistanceKm()));
        } else {
            variables.put("job.distance_km", "-");
        }
        
        // Services data - add as JSON array for the template
        List<Map<String, String>> servicesData = new ArrayList<>();
        for (Service service : getSelectedServices()) {
            Map<String, String> serviceData = new HashMap<>();
            serviceData.put("name", service.getName());
            
            // Calculate price based on calculation basis
            BigDecimal price = calculateServicePrice(service);
            if (price != null) {
                serviceData.put("netAmount", price.setScale(2, RoundingMode.HALF_UP).toString().replace(".", ","));
            } else {
                serviceData.put("netAmount", "0,00");
            }
            
            // VAT rate
            if (service.getVatRate() != null) {
                serviceData.put("vatRate", service.getVatRate().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP) + "%");
            } else {
                serviceData.put("vatRate", "19%");
            }
            
            servicesData.add(serviceData);
        }
        
        // Serialize services data to JSON and store in variables
        if (!servicesData.isEmpty()) {
            com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
            variables.put("services.json", mapper.writeValueAsString(servicesData));
            variables.put("services.count", String.valueOf(servicesData.size()));
        } else {
            variables.put("services.json", "[]");
            variables.put("services.count", "0");
        }
        
        // Generate PDF using CustomerInvoiceService
        return customerInvoiceService.generatePdfFromCanvasTemplateWithData(templateData, variables, currentUser);
    }
    
    private String safe(String value) {
        return value != null ? value : "";
    }
    
    private void showPdfInDialog(byte[] pdfBytes, String title) {
        // Create a stream resource for the PDF
        StreamResource resource = new StreamResource(title.replaceAll("[^a-zA-Z0-9\\-]", "_") + ".pdf", 
                () -> new java.io.ByteArrayInputStream(pdfBytes));
        resource.setContentType("application/pdf");
        resource.setCacheTime(0);

        // Store resource in session for access
        VaadinSession.getCurrent().setAttribute("currentInvoicePdf", resource);
        
        // Create dialog
        Dialog pdfDialog = new Dialog();
        pdfDialog.setHeaderTitle(title);
        pdfDialog.setWidth("90vw");
        pdfDialog.setHeight("90vh");

        // Create iframe for PDF
        IFrame pdfFrame = new IFrame();
        pdfFrame.setWidth("100%");
        pdfFrame.setHeight("100%");
        
        // Use data URL for PDF display
        String base64Pdf = java.util.Base64.getEncoder().encodeToString(pdfBytes);
        String dataUrl = "data:application/pdf;base64," + base64Pdf;
        pdfFrame.getElement().setAttribute("src", dataUrl);
        pdfFrame.getStyle().set("border", "none");

        // Close button
        Button closeButton = new Button("Schließen", e -> pdfDialog.close());
        closeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        // Download button
        Button downloadButton = new Button("Herunterladen", e -> {
            getElement()
                    .executeJs("const link = document.createElement('a');" + 
                            "link.href = 'data:application/pdf;base64," + base64Pdf + "';" +
                            "link.download = '" + title.replaceAll("[^a-zA-Z0-9\\-]", "_") + ".pdf';" +
                            "link.click();");
        });
        downloadButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        pdfDialog.add(pdfFrame);
        pdfDialog.getFooter().add(downloadButton, closeButton);
        pdfDialog.open();
    }

    private String formatDuration(int durationSeconds) {
        int hours = durationSeconds / 3600;
        int minutes = (durationSeconds % 3600) / 60;

        if (hours > 0) {
            return String.format("%d Std. %d Min.", hours, minutes);
        } else {
            return String.format("%d Min.", minutes);
        }
    }
}