package de.assecutor.votianlt.service;

import de.assecutor.votianlt.controller.MessageController;
import de.assecutor.votianlt.messaging.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Service for managing client connections. Connection state is determined
 * directly from the WebSocket session lifecycle.
 */
@Service
@Slf4j
public class ClientConnectionService {

    private final WebSocketService webSocketService;
    private final MessageService messageService;
    private final MessageController messageController;

    // Track clients that are connected but not yet ready (buffer not flushed)
    private final Map<String, PendingClient> pendingClients = new ConcurrentHashMap<>();

    public ClientConnectionService(WebSocketService webSocketService, @Lazy MessageService messageService,
            @Lazy MessageController messageController) {
        this.webSocketService = webSocketService;
        this.messageService = messageService;
        this.messageController = messageController;
    }

    /**
     * Called after successful login. Client is registered but messages/jobs are
     * only sent after buffer_flushed confirmation.
     *
     * @param appUserId
     *            The app user ID (MongoDB ObjectId)
     */
    public void registerClient(String appUserId) {
        if (appUserId == null || appUserId.isBlank()) {
            return;
        }

        log.info("[CLIENT] Registered: {} (waiting for buffer_flushed)", appUserId);

        // Store as pending - messages and jobs will be sent after buffer_flushed
        pendingClients.put(appUserId, new PendingClient(appUserId, Instant.now()));
    }

    /**
     * Called when client confirms that its buffer has been flushed and it's ready
     * to receive messages.
     *
     * @param appUserId
     *            The app user ID
     * @param messageCount
     *            Number of messages client processed (for logging)
     */
    public void onBufferFlushed(String appUserId, int messageCount) {
        if (appUserId == null || appUserId.isBlank()) {
            return;
        }

        PendingClient pending = pendingClients.remove(appUserId);
        if (pending == null) {
            log.debug("[CLIENT] Buffer flushed for {} but not in pending list", appUserId);
        } else {
            log.info("[CLIENT] Buffer flushed for {} (processed {} messages), sending pending data", appUserId,
                    messageCount);
        }

        // Now send pending messages and jobs
        sendPendingMessages(appUserId);
        sendAssignedJobs(appUserId);
    }

    /**
     * Send pending messages to a client.
     *
     * @param appUserId
     *            The app user ID
     */
    private void sendPendingMessages(String appUserId) {
        try {
            int sentCount = messageService.sendPendingMessages(appUserId);
            if (sentCount > 0) {
                log.info("[CLIENT] Sent {} pending messages to {}", sentCount, appUserId);
            }
        } catch (Exception e) {
            log.error("[CLIENT] Error sending pending messages to {}: {}", appUserId, e.getMessage());
        }
    }

    /**
     * Send assigned jobs to a client.
     *
     * @param appUserId
     *            The app user ID
     */
    private void sendAssignedJobs(String appUserId) {
        try {
            log.info("[CLIENT] Sending assigned jobs to appUserId: {}", appUserId);
            messageController.handleGetAssignedJobs(appUserId);
            log.info("[CLIENT] Assigned jobs sent successfully to {}", appUserId);
        } catch (Exception e) {
            log.error("[CLIENT] Error sending assigned jobs to {}: {}", appUserId, e.getMessage(), e);
        }
    }

    /**
     * Checks if a client is currently connected via WebSocket.
     *
     * @param appUserId
     *            The app user ID
     * @return true if the client has an active WebSocket session
     */
    public boolean isClientConnected(String appUserId) {
        return webSocketService.isClientConnected(appUserId);
    }

    /**
     * Gets the number of currently connected clients.
     *
     * @return Number of connected clients
     */
    public int getConnectedClientCount() {
        return webSocketService.getConnectedClientCount();
    }

    /**
     * Internal record to track pending clients waiting for buffer flush.
     */
    private record PendingClient(String appUserId, Instant registeredAt) {
    }
}
