package com.alttd.altitudeweb.services.chat;

import com.alttd.altitudeweb.controllers.chat.ChatMessage;
import com.alttd.altitudeweb.controllers.chat.ChatMessageMapper;
import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.chat.ChatLogMapper;
import com.alttd.altitudeweb.model.PermissionClaimDto;
import com.alttd.altitudeweb.services.chat.event_publisher.EventPublisher;
import com.alttd.altitudeweb.setup.Connection;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ChatService {

    private static final Duration MAX_AGE = Duration.ofHours(1);
    private final NavigableMap<Instant, ChatMessage> chatMessages = new TreeMap<>();
    private final EventPublisher eventPublisher;

    @Scheduled(cron = "0 * * * * *")
    public void clearOldMessages() {
        Instant cutoff = Instant.now().minus(MAX_AGE);
        int currentSize = chatMessages.size();

        chatMessages.headMap(cutoff, false).clear();

        log.info("Clearing old chat messages. Removed {}/{} messages",
                currentSize - chatMessages.size(), currentSize);
    }

    @EventListener(ApplicationReadyEvent.class)
    public void loadMessagesFromDb() {
        Connection.getConnection(Databases.CHAT)
                .runQuery(sqlSession -> {
                    log.debug("Loading uuid search result count");
                    try {
                        sqlSession.getMapper(ChatLogMapper.class)
                                .getChatLogs(Instant.now().minus(Duration.ofHours(1)).toEpochMilli())
                                .stream().map(ChatMessageMapper::fromDao)
                                .forEach(this::putChatMessage);
                    } catch (Exception e) {
                        log.error("Failed to load history count", e);
                    }
                });
    }

    private void putChatMessage(ChatMessage chatMessage) {
        chatMessages.put(chatMessage.getTimestamp(), chatMessage);
    }

    public void addChatMessage(List<ChatMessage> chatMessageList) {
        chatMessageList.forEach(this::putChatMessage);
        sendMessagesToListeners(chatMessageList);
    }

    private void sendMessagesToListeners(List<ChatMessage> chatMessageList) {
        String jsonMessageListIncludingBlocked = chatMessageList.stream()
                .map(ChatMessageMapper::toJson)
                .collect(Collectors.joining(",", "[", "]"));
        String jsonMessageList = chatMessageList.stream()
                .filter(ChatMessage::isNotBlocked)
                .map(ChatMessageMapper::toJson)
                .collect(Collectors.joining(",", "[", "]"));

        eventPublisher.sendToUsers("chat", (eventUser) -> {
            if (eventUser.hasPermission(PermissionClaimDto.HEAD_MOD)) {
                return jsonMessageListIncludingBlocked;
            }
            return jsonMessageList;
        });
    }

    public List<ChatMessage> getMessagesSince(Instant instant) {
        return chatMessages.tailMap(instant, false).values().stream().toList();
    }

}
