package com.kospo.talk.config.interceptor;

import com.kospo.talk.config.utils.CommonUtils;
import com.kospo.talk.model.AutoReply;
import com.kospo.talk.model.SmUser;
import com.kospo.talk.model.User;
import com.kospo.talk.payload.*;
import com.kospo.talk.service.SmUserService;
import com.kospo.talk.service.TalkMemberService;
import com.kospo.talk.service.TalkService;
import com.kospo.talk.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.session.SessionRegistry;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;


@Configuration
@RequiredArgsConstructor
public class ChatInfoInterceptor implements ChannelInterceptor {
    @Value("${rabbitmq.user.exchange}")
    private String userExchange;
    @Value("${rabbitmq.user.route}")
    private String userRoute;
    @Value("${rabbitmq.work.exchange}")
    private String workExchange;
    @Value("${rabbitmq.work.route}")
    private String workRoute;
    private final UserService userService;
    private final TalkService talkService;
    private final TalkMemberService talkMemberService;
    private final RabbitTemplate workTemplate;
    private final RabbitTemplate userTemplate;
    private final SessionRegistry sessionRegistry;
    private final SmUserService smUserService;
    private Map<String, List<String>> connectList = new HashMap<>();

    /* 온라인 오프라인 처리 헤더 정보 추가 해서 서비스워커에 있는 소켓만 허용*/
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message);
        String destination = headerAccessor.getDestination();
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            String location = headerAccessor.getNativeHeader("location").get(0);
            if (location.equals("SW")) {

                String sessionId = headerAccessor.getSessionId();
                UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) headerAccessor.getHeader("simpUser");
                User user = (User) token.getPrincipal();
                AtomicBoolean online = new AtomicBoolean(false);

                class OnlineThread extends Thread {
                    public void run() {
                        List<SmUser> smUserList = smUserService.findBySabun(user.getSabun());

                        smUserList.stream().forEach((sm) -> {
                            workTemplate.convertAndSend(workExchange, workRoute + sm.getWork().getWorkId(), TalkNotStartDto.builder()
                                    .type("WORK_COUNT")
                                    .notStart((int) talkService.nonStartTalk(sm.getWork().getWorkId()))
                                    .workId(sm.getWork().getWorkId())
                                    .build()
                            );

                            long onlineUserCnt = smUserService.countByOnlineYnAndWorkId(sm.getWork().getWorkId());
//                            long onlineUserCnt = smUserService.countByOnlineYnAndWorkIdExceptSabun(sm.getWork().getWorkId(), user.getSabun());
                            if (onlineUserCnt > 0) online.set(true);
                            if (online.get()) {
                                workTemplate.convertAndSend(workExchange, workRoute + sm.getId().getWorkId(), OnlineDto.builder()
                                        .sabun(user.getSabun())
                                        .type("WORK_ONLINE")
                                        .onlineYn(online.get())
                                        .build()
                                );
                            }
                        });


                        talkMemberService.findBySabunGroupBySabun(user.getSabun()).stream().forEach((sabun) -> {
                            if (!sabun.equals(user.getSabun())) {
                                userTemplate.convertAndSend(userExchange, userRoute + sabun, OnlineDto.builder()
                                        .sabun(user.getSabun())
                                        .type("ONLINE")
                                        .onlineYn(true)
                                        .works(smUserList.stream().map(SmUserDto::to).toList())
                                        .build());
                            }
                        });
                    }
                }

                if (user != null) {
                    if (!user.isOnlineYn()) {
                        user.setOnlineYn(true);
                        userService.save(user);
                        OnlineThread onlineThread = new OnlineThread();
                        onlineThread.setDaemon(true);
                        onlineThread.start();
                        onlineThread = null;
                    }

                }

                try {
                    List<String> currentUserList = connectList.get(user.getSabun());
                    if(currentUserList == null) {
                        currentUserList = new ArrayList<>();
                    }
                    currentUserList.add(sessionId);
                    connectList.put(user.getSabun(), currentUserList);
                } catch (Exception e) {
                    new RuntimeException(e);
                }
            }

        }
        return message;
    }

    @Override
    public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message);
        String destination = headerAccessor.getDestination();
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

        if (StompCommand.DISCONNECT.equals(accessor.getCommand())) {
            UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) headerAccessor.getHeader("simpUser");
            User user = (User) token.getPrincipal();
            String sessionId = headerAccessor.getSessionId();
            List<String> currentUserList = connectList.get(user.getSabun());
            if(currentUserList == null) {
                currentUserList = new ArrayList<>();
            } else {
                currentUserList.remove(sessionId);
            }
            AtomicBoolean online = new AtomicBoolean(true);
            if (currentUserList.size() == 0) {
                connectList.remove(user.getSabun());
                user.setOnlineYn(false);
                userService.save(user);
                List<SmUser> smUserList = smUserService.findBySabun(user.getSabun());

                smUserList.stream().forEach((sm) -> {
                    workTemplate.convertAndSend(workExchange, workRoute + sm.getId().getWorkId(), TalkNotStartDto.builder()
                            .type("WORK_COUNT")
                            .notStart((int) talkService.nonStartTalk(sm.getId().getWorkId()))
                            .workId(sm.getId().getWorkId())
                            .build()
                    );

                    long onlineUserCnt = smUserService.countByOnlineYnAndWorkId(sm.getWork().getWorkId());
                    if (onlineUserCnt == 0) online.set(false);

                    if (!online.get()) {
                        workTemplate.convertAndSend(workExchange, workRoute + sm.getId().getWorkId(), OnlineDto.builder()
                                .sabun(user.getSabun())
                                .type("WORK_OFFLINE")
                                .onlineYn(online.get())
                                .build()
                        );
                    }
                });

                List<ITalkMemberDto> talkList = talkMemberService.findTalkOnlineYnInSabun(user.getSabun());

                talkMemberService.findBySabunGroupBySabun(user.getSabun()).stream().forEach((sabun) -> {
                    if (!sabun.equals(user.getSabun())) {
                        userTemplate.convertAndSend(userExchange, userRoute + sabun, OnlineDto.builder()
                                .sabun(user.getSabun())
                                .type("OFFLINE")
                                .onlineYn(true)
                                .works(smUserList.stream().map(SmUserDto::to).toList())
                                .talks(talkList.stream().map(TalkMemberDto::from).toList())
                                .build());

                    }
                });
            }
            sessionRegistry.removeSessionInformation(sessionId);
        }
    }

}
