package de.assecutor.votianlt.pages.view;

import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.DetachEvent;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.router.HasDynamicTitle;
import com.vaadin.flow.router.Menu;
import com.vaadin.flow.router.Route;
import de.assecutor.votianlt.dto.ClientMessageSummary;
import de.assecutor.votianlt.model.AppUser;
import de.assecutor.votianlt.model.Message;
import de.assecutor.votianlt.model.MessageContentType;
import de.assecutor.votianlt.model.MessageOrigin;
import de.assecutor.votianlt.pages.service.AppUserService;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import de.assecutor.votianlt.service.MessageBroadcaster;
import de.assecutor.votianlt.service.MessageService;
import de.assecutor.votianlt.util.DateTimeFormatUtil;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import com.vaadin.flow.shared.Registration;

@Route(value = "messages", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@Menu(order = 1, icon = "vaadin:envelope", title = "Nachrichten")
@RolesAllowed("USER")
@Slf4j
public class MessagesView extends Main implements HasDynamicTitle {

    private static final int POLL_INTERVAL_MS = 5000;

    private final MessageService messageService;
    private final AppUserService appUserService;
    private final MessageBroadcaster messageBroadcaster;

    private Grid<ClientMessageSummary> clientGrid;
    private final AtomicBoolean loading = new AtomicBoolean(false);
    private Registration pollRegistration;
    private Registration broadcasterRegistration;
    private List<AppUser> cachedAppUsers;

    public MessagesView(MessageService messageService, AppUserService appUserService,
            MessageBroadcaster messageBroadcaster) {
        this.messageService = messageService;
        this.appUserService = appUserService;
        this.messageBroadcaster = messageBroadcaster;
        addClassNames("data-view", "message-hub-view");

        // Create main layout
        VerticalLayout layout = new VerticalLayout();
        layout.setPadding(true);
        layout.setSpacing(true);
        layout.setWidthFull();
        layout.addClassName("form-shell");

        // Add title and action buttons
        ViewToolbar headerLayout = new ViewToolbar(getTranslation("messages.title"));

        // Create client grid
        clientGrid = createClientGrid();
        clientGrid.addClassName("data-grid");

        Div gridPanel = new Div(clientGrid);
        gridPanel.addClassNames("surface-panel", "data-grid-panel");
        gridPanel.setWidthFull();
        gridPanel.setHeight("680px");

        // Add components to layout
        layout.add(headerLayout, gridPanel);

        // Add layout to main view
        add(layout);

        // Load client summaries
        loadClientSummaries();
    }

    private Grid<ClientMessageSummary> createClientGrid() {
        Grid<ClientMessageSummary> grid = new Grid<>(ClientMessageSummary.class, false);
        grid.setWidthFull();
        grid.setHeightFull();

        // Add columns
        grid.addColumn(new ComponentRenderer<>(summary -> {
            Span span = new Span(summary.getUnreadCount() > 0 ? "●" : "○");
            span.getStyle().set("font-size", "20px");
            if (summary.getUnreadCount() > 0) {
                span.getStyle().set("color", "var(--lumo-primary-color)");
            }
            return span;
        })).setHeader(getTranslation("messages.column.status")).setWidth("80px").setFlexGrow(0);

        grid.addColumn(ClientMessageSummary::getClientName).setHeader(getTranslation("messages.column.client"))
                .setAutoWidth(true);
        grid.addColumn(ClientMessageSummary::getClientEmail).setHeader(getTranslation("messages.column.email"))
                .setAutoWidth(true);

        grid.addColumn(new ComponentRenderer<>(summary -> {
            Span span = new Span(String.valueOf(summary.getTotalMessages()));
            return span;
        })).setHeader(getTranslation("messages.column.total")).setWidth("120px").setFlexGrow(0);

        grid.addColumn(new ComponentRenderer<>(summary -> {
            if (summary.getUnreadCount() > 0) {
                Span span = new Span(String.valueOf(summary.getUnreadCount()));
                span.getStyle().set("color", "var(--lumo-primary-color)");
                span.getStyle().set("font-weight", "bold");
                return span;
            }
            return new Span("0");
        })).setHeader(getTranslation("messages.column.unread")).setWidth("100px").setFlexGrow(0);

        grid.addColumn(summary -> summary.getLastMessageDate() != null
                ? DateTimeFormatUtil.formatDateTime(summary.getLastMessageDate())
                : "-").setHeader(getTranslation("messages.column.lastmessage")).setAutoWidth(true);

        grid.addColumn(new ComponentRenderer<>(summary -> {
            String preview = summary.getLastMessagePreview();
            if (preview != null && preview.length() > 50) {
                preview = preview.substring(0, 47) + "...";
            }
            return new Span(preview != null ? preview : "-");
        })).setHeader(getTranslation("messages.column.preview")).setAutoWidth(true);

        // Add click listener to navigate to UserMessagesView
        grid.addItemClickListener(event -> {
            ClientMessageSummary summary = event.getItem();
            UI.getCurrent().navigate("user-messages/" + summary.getClientId());
        });

        return grid;
    }

    private void loadClientSummaries() {
        if (!loading.compareAndSet(false, true)) {
            return;
        }
        try {
            List<AppUser> appUsers;
            try {
                appUsers = Optional.ofNullable(appUserService.findByCurrentUser()).orElseGet(ArrayList::new);
                cachedAppUsers = appUsers;
            } catch (Exception e) {
                log.debug("SecurityContext not available, using cached app users");
                appUsers = cachedAppUsers != null ? cachedAppUsers : new ArrayList<>();
            }
            Map<String, AppUser> appUserLookup = buildAppUserLookup(appUsers);

            List<Message> allMessages = messageService.getAllMessages();
            Map<String, List<Message>> groupedByParticipant = groupMessagesByParticipant(allMessages);
            List<ClientMessageSummary> summaries = buildSummaries(groupedByParticipant, appUserLookup, appUsers);
            clientGrid.setItems(summaries);

        } catch (Exception e) {
            log.error("Error loading client summaries: {}", e.getMessage(), e);
            Notification.show(getTranslation("messages.notification.error"), 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        } finally {
            loading.set(false);
        }
    }

    private Map<String, List<Message>> groupMessagesByParticipant(List<Message> messages) {
        Map<String, List<Message>> grouped = new LinkedHashMap<>();
        for (Message message : messages) {
            String participantKey = resolveParticipantKey(message);
            if (participantKey == null || participantKey.isBlank()) {
                continue;
            }
            grouped.computeIfAbsent(participantKey, key -> new ArrayList<>()).add(message);
        }
        return grouped;
    }

    private List<ClientMessageSummary> buildSummaries(Map<String, List<Message>> groupedMessages,
            Map<String, AppUser> appUserLookup, List<AppUser> appUsers) {
        Map<String, ClientMessageSummary> summaryMap = new LinkedHashMap<>();

        for (Map.Entry<String, List<Message>> entry : groupedMessages.entrySet()) {
            String participantKey = entry.getKey();
            AppUser participant = resolveAppUser(participantKey, appUserLookup);
            if (participant == null) {
                continue; // Only display app users of the current account holder
            }

            String participantId = participant.getIdAsString();
            if (participantId == null || participantId.isBlank()) {
                continue;
            }

            ClientMessageSummary summary = summaryMap.computeIfAbsent(participantId,
                    id -> createEmptySummary(participant));

            List<Message> conversation = entry.getValue();
            if (conversation == null || conversation.isEmpty()) {
                continue;
            }

            conversation.sort(Comparator
                    .comparing(Message::getCreatedAt, Comparator.nullsLast(LocalDateTime::compareTo)).reversed());

            Message latest = conversation.stream().filter(msg -> msg.getCreatedAt() != null).findFirst()
                    .orElse(conversation.get(0));

            LocalDateTime lastDate = latest.getCreatedAt();
            String preview = resolvePreview(latest);
            int totalMessages = conversation.size();
            int unreadCount = (int) conversation.stream()
                    .filter(msg -> msg.getOrigin() == MessageOrigin.CLIENT && !msg.isRead()).count();

            summary.setTotalMessages(summary.getTotalMessages() + totalMessages);
            summary.setUnreadCount(summary.getUnreadCount() + unreadCount);

            LocalDateTime currentLast = summary.getLastMessageDate();
            if (lastDate != null && (currentLast == null || lastDate.isAfter(currentLast))) {
                summary.setLastMessageDate(lastDate);
                summary.setLastMessagePreview(preview);
            }
        }

        for (AppUser appUser : appUsers) {
            if (appUser == null) {
                continue;
            }
            String appUserId = appUser.getIdAsString();
            if (appUserId != null && !summaryMap.containsKey(appUserId)) {
                summaryMap.put(appUserId, createEmptySummary(appUser));
            }
        }

        List<ClientMessageSummary> summaries = new ArrayList<>(summaryMap.values());
        summaries.sort(Comparator
                .comparing(ClientMessageSummary::getLastMessageDate, Comparator.nullsLast(LocalDateTime::compareTo))
                .reversed());
        return summaries;
    }

    private String resolvePreview(Message message) {
        if (message == null) {
            return getTranslation("messages.preview.empty");
        }

        if (message.getContentType() == MessageContentType.IMAGE) {
            return getTranslation("messages.preview.image");
        }

        return Optional.ofNullable(message.getContent()).filter(content -> !content.isBlank())
                .orElse(getTranslation("messages.preview.empty"));
    }

    private String resolveParticipantKey(Message message) {
        if (message == null) {
            return null;
        }
        // All messages now have receiver = AppUser ID (clientId)
        return message.getReceiver();
    }

    private AppUser resolveAppUser(String participantKey, Map<String, AppUser> appUserLookup) {
        if (participantKey == null || participantKey.isBlank()) {
            return null;
        }
        String trimmed = participantKey.trim();
        AppUser directMatch = appUserLookup.get(trimmed);
        if (directMatch != null) {
            return directMatch;
        }
        return appUserLookup.get(trimmed.toLowerCase());
    }

    private String buildClientName(AppUser participant) {
        if (participant == null) {
            return "-";
        }
        String vorname = Optional.ofNullable(participant.getVorname()).orElse("").trim();
        String nachname = Optional.ofNullable(participant.getNachname()).orElse("").trim();

        String fullName = (vorname + " " + nachname).trim();
        if (!fullName.isEmpty()) {
            return fullName;
        }
        return Optional.ofNullable(participant.getBezeichnung()).orElse(participant.getIdAsString());
    }

    private ClientMessageSummary createEmptySummary(AppUser appUser) {
        String clientId = Optional.ofNullable(appUser.getIdAsString()).orElse("-");
        String clientName = buildClientName(appUser);
        String clientEmail = Optional.ofNullable(appUser.getEmail()).orElse("-");
        return new ClientMessageSummary(clientId, clientName, clientEmail, 0, 0, null, "-");
    }

    private Map<String, AppUser> buildAppUserLookup(List<AppUser> appUsers) {
        Map<String, AppUser> lookup = new LinkedHashMap<>();
        for (AppUser appUser : appUsers) {
            if (appUser == null) {
                continue;
            }
            addLookupEntry(lookup, appUser.getIdAsString(), appUser);
            addLookupEntry(lookup, appUser.getEmail(), appUser);
            addLookupEntry(lookup, appUser.getAppCode(), appUser);
        }
        return lookup;
    }

    private void addLookupEntry(Map<String, AppUser> lookup, String key, AppUser appUser) {
        if (key == null) {
            return;
        }
        String trimmed = key.trim();
        if (trimmed.isEmpty()) {
            return;
        }
        lookup.putIfAbsent(trimmed, appUser);
        lookup.putIfAbsent(trimmed.toLowerCase(), appUser);
    }

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        super.onAttach(attachEvent);
        UI ui = attachEvent.getUI();
        ui.setPollInterval(POLL_INTERVAL_MS);
        pollRegistration = ui.addPollListener(event -> loadClientSummaries());
        cachedAppUsers = Optional.ofNullable(appUserService.findByCurrentUser()).orElseGet(ArrayList::new);
        broadcasterRegistration = messageBroadcaster.register(message -> handleIncomingMessage(ui, message));
        requestBrowserPermissions(ui);
        loadClientSummaries();
    }

    private void requestBrowserPermissions(UI ui) {
        ui.getPage()
                .executeJs("if ('Notification' in window && Notification.permission === 'default') {"
                        + "  Notification.requestPermission();" + "}" + "if (!window._votianAudioCtx) {"
                        + "  window._votianAudioCtx = new (window.AudioContext || window.webkitAudioContext)();"
                        + "  document.addEventListener('click', function _resumeAudio() {"
                        + "    window._votianAudioCtx.resume();"
                        + "    document.removeEventListener('click', _resumeAudio);" + "  }, { once: true });" + "}");
    }

    @Override
    protected void onDetach(DetachEvent detachEvent) {
        super.onDetach(detachEvent);
        if (pollRegistration != null) {
            pollRegistration.remove();
            pollRegistration = null;
        }
        if (broadcasterRegistration != null) {
            broadcasterRegistration.remove();
            broadcasterRegistration = null;
        }
        detachEvent.getUI().setPollInterval(-1);
    }

    private void handleIncomingMessage(UI ui, Message message) {
        if (message.getOrigin() != MessageOrigin.CLIENT) {
            return;
        }
        ui.access(() -> {
            String senderName = resolveSenderName(message.getReceiver());
            String preview = resolvePreview(message);

            // Play notification sound
            ui.getPage()
                    .executeJs("try {" + "  const ctx = new (window.AudioContext || window.webkitAudioContext)();"
                            + "  ctx.resume().then(() => {" + "    const osc = ctx.createOscillator();"
                            + "    const gain = ctx.createGain();" + "    osc.connect(gain);"
                            + "    gain.connect(ctx.destination);" + "    osc.frequency.value = 800;"
                            + "    osc.type = 'sine';" + "    gain.gain.setValueAtTime(0.3, ctx.currentTime);"
                            + "    gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.5);"
                            + "    osc.start(ctx.currentTime);" + "    osc.stop(ctx.currentTime + 0.5);" + "  });"
                            + "} catch(e) { console.warn('Notification sound failed:', e); }");

            // Show notification
            Notification notification = Notification.show(
                    getTranslation("messages.notification.new", senderName, preview), 4000,
                    Notification.Position.TOP_END);
            notification.addThemeVariants(NotificationVariant.LUMO_PRIMARY);

            loadClientSummaries();
        });
    }

    private String resolveSenderName(String clientId) {
        if (clientId == null || clientId.isBlank()) {
            return getTranslation("messages.sender.unknown");
        }
        List<AppUser> appUsers = cachedAppUsers != null ? cachedAppUsers : List.of();
        return appUsers.stream().filter(user -> clientId.equals(user.getIdAsString())
                || clientId.equals(user.getEmail()) || clientId.equals(user.getAppCode())).findFirst()
                .map(this::buildClientName).orElse(clientId);
    }

    @Override
    public String getPageTitle() {
        return getTranslation("page.title.messages");
    }
}
