package de.assecutor.votianlt.messaging;

import de.assecutor.votianlt.controller.MessageController;
import de.assecutor.votianlt.dto.AppLoginRequest;
import de.assecutor.votianlt.dto.AppLoginResponse;
import de.assecutor.votianlt.service.ClientConnectionService;
import de.assecutor.votianlt.service.LocationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * Configuration for the messaging system. Sets up message routing after
 * application startup.
 */
@Configuration
@Slf4j
public class MessagingConfig {

    private final WebSocketService webSocketService;
    private final ObjectMapper objectMapper;

    public MessagingConfig(WebSocketService webSocketService, ObjectMapper objectMapper) {
        this.webSocketService = webSocketService;
        this.objectMapper = objectMapper;
    }

    /**
     * Set up message routing after application startup.
     */
    @EventListener(ApplicationReadyEvent.class)
    public void setupMessaging(ApplicationReadyEvent event) {
        try {
            MessageController messageController = event.getApplicationContext().getBean(MessageController.class);
            ClientConnectionService clientConnectionService = event.getApplicationContext()
                    .getBean(ClientConnectionService.class);
            LocationService locationService = event.getApplicationContext().getBean(LocationService.class);

            setupSubscriptions(messageController, clientConnectionService, locationService);

            log.info("[Messaging] Message routing configured");

        } catch (Exception e) {
            log.error("[Messaging] Failed to initialize: {}", e.getMessage());
            throw new RuntimeException("Failed to initialize messaging", e);
        }
    }

    /**
     * Setup message subscriptions on the WebSocket service.
     */
    private void setupSubscriptions(MessageController messageController,
            ClientConnectionService clientConnectionService, LocationService locationService) {
        // Login handler: authenticate and register session
        webSocketService.registerMessageHandler("login", (wsSessionId, payload) -> {
            handleLoginMessage(wsSessionId, payload, messageController, clientConnectionService);
        });

        // Task completion handler
        webSocketService.registerMessageHandler("task_completed", (appUserId, payload) -> {
            handlePayload(payload, payloadMap -> {
                String taskType = payloadMap.get("taskType") != null ? payloadMap.get("taskType").toString() : null;
                messageController.handleTaskCompleted(payloadMap, taskType);
            });
        });

        // Station completion handler — marks a job as completed once all mandatory
        // tasks have been finished and the app confirms the station is done.
        webSocketService.registerMessageHandler("station_completed", (appUserId, payload) -> {
            handlePayload(payload, payloadMap -> {
                messageController.handleStationCompleted(appUserId, payloadMap);
            });
        });

        // Chat message handler
        webSocketService.registerMessageHandler("message", (appUserId, payload) -> {
            handlePayload(payload, payloadMap -> {
                messageController.handleIncomingMessage(appUserId, payloadMap);
            });
        });

        // Buffer flushed handler - client is ready to receive pending messages
        webSocketService.registerMessageHandler("buffer_flushed", (appUserId, payload) -> {
            handlePayload(payload, payloadMap -> {
                int messageCount = extractMessageCount(payloadMap);
                clientConnectionService.onBufferFlushed(appUserId, messageCount);
            });
        });

        // Location handler - client sends position updates
        webSocketService.registerMessageHandler("location", (appUserId, payload) -> {
            handlePayload(payload, payloadMap -> {
                locationService.savePosition(appUserId, payloadMap);
            });
        });
    }

    /**
     * Handle login message. The wsSessionId identifies the pending WebSocket
     * session. On success, registers the session under the appUserId and sends an
     * auth response. On failure, sends an error response to the pending session.
     */
    @SuppressWarnings("unchecked")
    private void handleLoginMessage(String wsSessionId, byte[] payload, MessageController messageController,
            ClientConnectionService clientConnectionService) {
        try {
            String json = new String(payload, StandardCharsets.UTF_8);
            Map<String, Object> payloadMap = objectMapper.readValue(json, Map.class);
            AppLoginRequest loginRequest = objectMapper.convertValue(payloadMap, AppLoginRequest.class);
            AppLoginResponse response = messageController.handleAppLogin(loginRequest);

            if (response.isSuccess()) {
                String appUserId = response.getAppUserId();
                log.info("[Messaging] Login successful for appUserId: {}", appUserId);

                webSocketService.registerAuthenticatedSession(wsSessionId, appUserId);

                // Send success response to the now-authenticated session
                // locationTrackingEnabled: true = client should send position updates
                // appUserId: wird an den Client gesendet für Referenz
                Map<String, Object> authResponse = Map.of("success", true, "message", response.getMessage(),
                        "locationTrackingEnabled", true, "appUserId", appUserId);
                byte[] responseBytes = objectMapper.writeValueAsBytes(authResponse);
                log.info("[Messaging] Sending auth response to appUserId: {}", appUserId);
                webSocketService.sendToClient(appUserId, "auth", responseBytes);

                // Register client - pending messages and jobs will be sent after
                // client confirms buffer_flushed
                clientConnectionService.registerClient(appUserId);
            } else {
                log.warn("[Messaging] Login failed: {}", response.getMessage());
                // Send failure response to the pending session
                Map<String, Object> authResponse = Map.of("success", false, "message", response.getMessage());
                byte[] responseBytes = objectMapper.writeValueAsBytes(authResponse);
                webSocketService.sendToSessionById(wsSessionId, "/client/auth", responseBytes);
            }
        } catch (Exception e) {
            log.error("[Messaging] Login handling error: {}", e.getMessage(), e);
        }
    }

    /**
     * Extract message count from buffer_flushed payload.
     */
    private int extractMessageCount(Map<String, Object> payloadMap) {
        try {
            Object countObj = payloadMap.get("messageCount");
            if (countObj instanceof Number) {
                return ((Number) countObj).intValue();
            }
            return 0;
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * Parse payload bytes to a Map and pass to the consumer.
     */
    @SuppressWarnings("unchecked")
    private void handlePayload(byte[] payload, java.util.function.Consumer<Map<String, Object>> handler) {
        try {
            String json = new String(payload, StandardCharsets.UTF_8);
            Map<String, Object> payloadMap = objectMapper.readValue(json, Map.class);
            handler.accept(payloadMap);
        } catch (Exception e) {
            log.error("[Messaging] Error parsing payload: {}", e.getMessage());
        }
    }
}
