package com.kospo.talk.controller;

import com.google.gson.JsonObject;
import com.kospo.talk.config.exception.ErrorCode;
//import com.kospo.talk.config.flux.FluxClient;
import com.kospo.talk.config.filter.BadWordFiltering;
import com.kospo.talk.config.utils.CommonUtils;
import com.kospo.talk.exception.CustomException;
import com.kospo.talk.model.*;
import com.kospo.talk.payload.*;
import com.kospo.talk.repository.TalkMemberRepository;
import com.kospo.talk.repository.TalkReadRepository;
import com.kospo.talk.service.*;
import io.swagger.v3.oas.annotations.Hidden;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Controller;

import java.io.IOException;
import java.security.Principal;
import java.sql.Array;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

@Controller
@Hidden
@RequiredArgsConstructor
@Slf4j
public class StompRabbitController {
    private final TalkMemberRepository talkMemberRepository;
    private final QuestionService questionService;
    private final TalkReadRepository talkReadRepository;
    @Value("${rabbitmq.talk.exchange}")
    private String talkExchange;
    @Value("${rabbitmq.user.exchange}")
    private String userExchange;
    @Value("${rabbitmq.file.exchange}")
    private String fileExchange;
    @Value("${rabbitmq.work.exchange}")
    private String workExchange;
    @Value("${rabbitmq.talk.route}")
    private String talkRoute;
    @Value("${rabbitmq.user.route}")
    private String userRoute;
    @Value("${rabbitmq.file.route}")
    private String fileRoute;
    @Value("${rabbitmq.work.route}")
    private String workRoute;
    private final RabbitTemplate talkTemplate;
    private final RabbitTemplate userTemplate;
    private final RabbitTemplate fileTemplate;
    private final RabbitTemplate workTemplate;
    private final UserService userService;
    private final WorkService workService;
    private final TalkService talkService;
    private final TalkMessageService talkMessageService;
    private final TalkMemberService talkMemberService;
    private final AutoReplyService autoReplyService;
    private final BadWordFiltering badWordFiltering;
//    private final FluxClient fluxClient;

    private HashMap<String, Boolean> allowFile;

    @MessageMapping("talk.ping")
    public void ping(SimpMessageHeaderAccessor accessor) {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) accessor.getMessageHeaders().get("simpUser");;
        List<String> auths = token.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
        User currentUser = (User) token.getPrincipal();
        userTemplate.convertAndSend(userExchange, userRoute + currentUser.getSabun(), "PONG");
    }


    @MessageMapping("talk.enter.{talkId}")
    public void enter(TalkDto talkDto, @DestinationVariable String talkId, SimpMessageHeaderAccessor accessor) {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) accessor.getMessageHeaders().get("simpUser");;
        List<String> auths = token.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
        User currentUser = (User) token.getPrincipal();

        String oriMessage = talkDto.getMessage();

        /*일반 사용자가 방 생성시 모든 유지보수 담당자에게 방생성*/
        List<TalkMemberDto> memberList = new LinkedList<>();
        WorkDto currentWork = workService.findByWorkId(talkDto.getWorkId());

        talkService.createTalk(Talk.builder()
                        .talkId(talkId)
                        .closeYn(false)
                        .compYn(false)
                        .startYn(false)
                        .workId(currentWork.getWorkId())
                        .insSabun(currentUser.getSabun())
                        .insDate(LocalDateTime.now())
                        .insName(currentUser.getName())
                        .lastMessageDate(LocalDateTime.now())
                        .build()
        );

        memberList.add(TalkMemberDto.builder()
                .talkId(talkId)
                .sabun(currentUser.getSabun())
                .name(currentUser.getName())
                .markYn(false)
                .deleteYn(false)
                .dummyYn(false)
                .onlineYn(true).build());

        memberList.add(TalkMemberDto.builder()
                .talkId(talkId)
                .sabun("info")
                .name("시스템")
                .markYn(false)
                .deleteYn(false)
                .dummyYn(false)
                .onlineYn(true).build());

        AtomicBoolean isSmOnlineYn = new AtomicBoolean(false);

        List<User> smUsers = userService.findSM(currentWork.getWorkId());
        smUsers.forEach((user -> {
            if(user.isOnlineYn()) isSmOnlineYn.set(true);
            memberList.add(TalkMemberDto.builder()
                            .talkId(talkId)
                            .sabun(user.getSabun())
                            .name(user.getName())
                            .markYn(false)
                            .deleteYn(false)
                            .dummyYn(false)
                            .onlineYn(user.isOnlineYn()).build());

        }));

        talkDto.setTalkMessages(new TalkReceiveDto());

        talkDto.setTalkMembers(memberList);

        memberList.forEach((member) -> {
            MessagePostProcessor messagePostProcessor = message -> {
                message.getMessageProperties().setHeader("sabun", member.getSabun());
                message.getMessageProperties().setHeader("name", member.getName());
                return message;
            };
//            userTemplate.convertAndSend(userExchange, userRoute + member.getSabun(), talkDto, messagePostProcessor);
            userTemplate.convertAndSend(userExchange, userRoute + member.getSabun(), TalkDto.builder()
                            .type("JOIN")
                            .talkId(talkId)
                            .markYn(false)
                            .closeYn(false)
                            .compYn(false)
                            .startYn(false)
                            .insSabun(currentUser.getSabun())
                            .insName(currentUser.getName())
                            .workId(currentWork.getWorkId())
                            .workNm(currentWork.getWorkNm())
                            .insDate(LocalDateTime.now())
                            .message(talkDto.getMessage())
                            .introOwner(talkDto.getIntroOwner())
                            .unReadCnt(0)
                            .talkMembers(memberList)
                            .work(currentWork)
                            .messageParams(talkDto.getMessageParams())
                            .talkMessages(talkDto.getTalkMessages())
                            .tabId(talkDto.getTabId()).build()
                    , messagePostProcessor);

        });

        workTemplate.convertAndSend(workExchange, workRoute + currentWork.getWorkId(), TalkNotStartDto.builder()
                .type("WORK_COUNT")
                .notStart((int) talkService.nonStartTalk(currentWork.getWorkId()))
                .workId(currentWork.getWorkId())
                .build()
        );

        List<AutoReply> autoReplies = autoReplyService.findByTalkId(talkId);

        class AutoReplySend extends Thread {
            public void run() {
                try {
                    Thread.sleep(500);

                    String message = "";
                    if(isSmOnlineYn.get()) {

                        AutoReply autoReply = autoReplies.stream().filter((ar) -> ar.getId().getType().equals("0010")).findFirst().orElse(null);
                        if(autoReply == null) {
                            message = "무엇을 도와드릴까요?";
                        } else {
                            message = autoReply.getContent();
                        }
                    } else {
                        AutoReply autoReply = autoReplies.stream().filter((ar) -> ar.getId().getType().equals("0020")).findFirst().orElse(null);
                        if(autoReply == null) {
                            message = "담당자 부재중이에요.";
                        } else {
                            message = autoReply.getContent();
                        }
                    }
//                    talkDto.setType("INFO_MESSAGE");
//                    talkDto.setMessage(message);
//                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkId, talkDto);
                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkId, TalkDto.builder()
                            .type("INFO_MESSAGE")
                            .talkId(talkId)
                            .insSabun(currentUser.getSabun())
                            .insName(currentUser.getName())
                            .workId(currentWork.getWorkId())
                            .workNm(currentWork.getWorkNm())
                            .insDate(LocalDateTime.now())
                            .message(message)
                            .readYn(false)
                            .tabId(talkDto.getTabId()).build()
                    );
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }

        /*방 생서후 메시지 전송 */
        class IntroSend extends Thread {
            public void run() {
                try {
                    Thread.sleep(500);
                    talkDto.setInsSabun(currentUser.getSabun());
                    talkDto.setInsName(currentUser.getName());
                    talkDto.setType("QUESTION");
                    talkDto.setReadYn(true);
                    talkDto.setMessage(oriMessage);
                    talkDto.setWorkNm(currentWork.getWorkNm());
                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkDto.getTalkId(), TalkDto.builder()
                            .type("QUESTION")
                            .talkId(talkId)
                            .insSabun(currentUser.getSabun())
                            .insName(currentUser.getName())
                            .workId(currentWork.getWorkId())
                            .workNm(currentWork.getWorkNm())
                            .insDate(LocalDateTime.now())
                            .readYn(true)
                            .message(oriMessage)
                            .build()
                    );

                    Thread.sleep(300);
                    Question question = questionService.findByWorkIdAndContent(currentWork.getWorkId(), oriMessage);
                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkDto.getTalkId(), TalkDto.builder()
                            .type("QUESTION")
                            .talkId(talkDto.getTalkId())
                            .insSabun(smUsers.get(0).getSabun())
                            .insName(smUsers.get(0).getName())
                            .insDate(LocalDateTime.now())
                            .readYn(true)
                            .message(question.getAsk())
                            .workId(currentWork.getWorkId())
                            .workNm(currentWork.getWorkNm())
                            .build()
                    );

                    Thread.sleep(300);
                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkDto.getTalkId(), TalkDto.builder()
                            .type("QUESTION")
                            .talkId(talkDto.getTalkId())
                            .insSabun("info")
                            .insName("시스템")
                            .insDate(LocalDateTime.now())
                            .readYn(true)
                            .message("추가 질문이 있으시면 채팅창에 문의해 주세요.\n추가 질문이 없으시면 뒤로가기 시 현재 톡방은 삭제 돼요.")
                            .workId(currentWork.getWorkId())
                            .workNm(currentWork.getWorkNm())
                            .build()
                    );


                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }

        /*자주하는 질문 문의시 */
        if(talkDto.getIntroOwner().equals("mine")) {
            IntroSend send = new IntroSend();
            send.setDaemon(true);
            send.start();
            send = null;
        /*일반 문의 시작시*/
        } else {
            AutoReplySend autoReplySend = new AutoReplySend();
            autoReplySend.setDaemon(true);
            autoReplySend.start();
            autoReplySend = null;
        }
    }

    @Transactional
    @MessageMapping("talk.close.{talkId}")
    public void close(TalkDto talkDto, @DestinationVariable String talkId, SimpMessageHeaderAccessor accessor) {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) accessor.getMessageHeaders().get("simpUser");;
        List<String> auths = token.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
        User currentUser = (User) token.getPrincipal();
        Talk talk = talkService.findByOne(talkId);

        Work work = talk.getWork();
        if(!work.getSmUsers().stream().map((sm) -> sm.getId().getSabun()).toList().contains(currentUser.getSabun())) {
            talkDto.setType("ERROR");
            talkDto.setMessage("["+ ErrorCode.NO_EXIST_TALK_CLOSE.getCode() + "]" +ErrorCode.NO_EXIST_TALK_CLOSE.getMessage());
            userTemplate.convertAndSend(userExchange, userRoute + currentUser.getSabun(), talkDto);
            throw new CustomException(ErrorCode.NO_EXIST_TALK_CLOSE);
        }


        if(talkDto.getForce() == null) talkDto.setForce(false);
        if(!talkDto.getForce()) {
            talkTemplate.convertAndSend(talkExchange, talkRoute + talkId,  TalkDto.builder()
                    .talkId(talkId)
                    .type("MESSAGE")
                    .insSabun("info")
                    .insName("시스템")
                    .message("상담이 종료 되었어요.\n만족도 조사에 참여해 주세요.")
                    .workNm(work.getWorkNm())
                    .workId(work.getWorkId())
                    .insDate(LocalDateTime.now()).build());
        } else {
            talkTemplate.convertAndSend(talkExchange, talkRoute + talkId,  TalkDto.builder()
                    .talkId(talkId)
                    .type("MESSAGE")
                    .insSabun("info")
                    .insName("시스템")
                    .message("상담이 종료 되었어요.")
                    .workNm(work.getWorkNm())
                    .workId(work.getWorkId())
                    .insDate(LocalDateTime.now()).build());
        }


        /* 메시지 전송 후 상담종료 */
        class CloseMessageSend extends Thread {
            public void run() {
                try {
                    Thread.sleep(1000);
                    talkDto.setType("CLOSE");
                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkId,  TalkDto.builder()
                            .talkId(talkId)
                            .type("CLOSE")
                            .force(talkDto.getForce())
                            .insSabun(currentUser.getSabun())
                            .insName(currentUser.getName())
                            .insDate(LocalDateTime.now()).build()
                    );
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }
        CloseMessageSend cms = new CloseMessageSend();
        cms.setDaemon(true);
        cms.start();
        cms = null;

    }

    @Transactional
    @MessageMapping("talk.start.{talkId}")
    public void start(TalkDto talkDto, @DestinationVariable String talkId, SimpMessageHeaderAccessor accessor) {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) accessor.getMessageHeaders().get("simpUser");;
        List<String> auths = token.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
        User currentUser = (User) token.getPrincipal();
        List<AutoReply> autoReplies = autoReplyService.findByTalkId(talkId);
//        System.out.println("talk start Message start talkId : " + talkId);

        String counselSabun = currentUser.getSabun();

        List<ITalkMemberDto> allMembers = talkMemberService.findByTalkId(talkId);

        /*톡멤버 중에 본인이 없을시 오류발생*/
        if(!allMembers.stream().map((tmd) -> tmd.getSabun()).toList().contains(counselSabun)) {
            talkDto.setType("ERROR");
            talkDto.setMessage("["+ ErrorCode.NO_EXIST_TALK_START.getCode() + "]" +ErrorCode.NO_EXIST_TALK_START.getMessage());
            userTemplate.convertAndSend(userExchange, userRoute + currentUser.getSabun(), talkDto);
            throw new CustomException(ErrorCode.NO_EXIST_TALK_START);
        }

        Talk currentTalk = talkService.findByOne(talkId);

        String owner = currentTalk.getInsSabun();

        List<ITalkMemberDto> delMembers = allMembers.stream().filter((tmd) -> (!tmd.getSabun().equals("info") && !tmd.getSabun().equals(owner) && !tmd.getSabun().equals(counselSabun))).toList();
        /* 상담자 와 문의자 제외한 나머지 멤버 삭제 메시지 전송 및 삭제*/
        delMembers.stream().forEach((dm) -> {
            talkDto.setType("LEAVE");
//            System.out.println("talk leave : " + dm.getSabun());
            userTemplate.convertAndSend(userExchange, userRoute + dm.getSabun(), talkDto);
            talkMemberService.deleteMember(talkId, dm.getSabun());
        });

        /*채팅방 재정의 용 사용자 맵핑 되고 채팅이 시작됐다는 정보*/
        talkDto.setType("START");
        talkDto.setInsSabun(currentUser.getSabun());
        talkDto.setInsName(currentUser.getName());
        userTemplate.convertAndSend(userExchange, userRoute + owner, talkDto);
        userTemplate.convertAndSend(userExchange, userRoute + counselSabun, talkDto);

        AutoReply autoReply = autoReplies.stream().filter((ar) -> ar.getId().getType().equals("0030")).findFirst().orElse(null);
        String message = "";
        if(autoReply == null) {
            message = "상담을 시작할게요.";
        } else {
            message = autoReply.getContent();
        }

        Work work = currentTalk.getWork();
        /* 시스템 배정된 담당자 알림 */
        talkTemplate.convertAndSend(talkExchange, talkRoute + talkId, TalkDto.builder()
                .talkId(talkId)
                .type("MESSAGE")
                .insSabun("info")
                .insName("시스템")
                .message(message)
                .startYn(true)
                .workNm(work.getWorkNm())
                .workId(work.getWorkId())
                .insDate(LocalDateTime.now()).build()
        );
    }

    @Transactional
    @MessageMapping("talk.change.{talkId}")
    public void change(TalkDto talkDto, @DestinationVariable String talkId, SimpMessageHeaderAccessor accessor) {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) accessor.getMessageHeaders().get("simpUser");;
        List<String> auths = token.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
        User currentUser = (User) token.getPrincipal();

        /*기존 데이터 호출*/
        /*데이터 복사*/
        Talk parentTalk = talkService.findByOne(talkId);
        Work parentWork = parentTalk.getWork();



        /*일반 사용자가 방 생성시 모든 유지보수 담당자에게 방생성*/
        List<TalkMemberDto> memberList = new LinkedList<>();

        WorkDto newWork = workService.findByWorkId(talkDto.getChangeTalk().getWorkId());

        talkService.createTalk(Talk.builder()
                .talkId(talkDto.getChangeTalk().getTalkId())
                .closeYn(false)
                .compYn(false)
                .startYn(false)
                .workId(newWork.getWorkId())
                .insSabun(parentTalk.getInsSabun())
                .insDate(LocalDateTime.now())
                .insName(parentTalk.getInsName())
                .build()
        );

        /*이전 대화 복사 start*/
        if(talkDto.getGubun() == 0 || talkDto.getGubun() == 1) {
            talkService.dataCopy(talkId, talkDto.getChangeTalk().getTalkId(), false);
        } else {
            talkService.dataCopy(talkId, talkDto.getChangeTalk().getTalkId(), true);
        }

        /* 현재 문의자 더미 해제 */
        talkService.updateDummy(talkDto.getChangeTalk().getTalkId(), parentTalk.getInsSabun());

        /*상담구분에 따른 톡방 삭제
         * 0 : 단순문의
         * 1 : 상담
         * 2 : pi 승인
         * 3 : scm
         * */
        if(talkDto.getGubun() == 0) {
            // 멤버 전체 톡방 삭제
            talkMemberService.disableTalkId(talkId);
            talkMemberService.findDeleteTarget(talkId).stream().forEach((member) -> {
                userTemplate.convertAndSend(userExchange, userRoute + member.getId().getSabun(), MessageDto.builder()
                        .type("DELETE")
                        .talkId(talkId)
                        .build());
            });
        } else if(talkDto.getGubun() == 1) {
            // 유지보수자를 제외한 사용자만 톡방 삭제
            talkMemberService.disableTalkId(talkId);
            talkMemberService.enableTalkIdAndSabun(talkId, talkDto.getInsSabun());
            talkMemberService.findDeleteTarget(talkId).stream().forEach((member) -> {
                if(!member.getId().getSabun().equals(talkDto.getInsSabun())) {
                    userTemplate.convertAndSend(userExchange, userRoute + member.getId().getSabun(), MessageDto.builder()
                            .type("DELETE")
                            .talkId(talkId)
                            .build());
                }

            });

            talkTemplate.convertAndSend(talkExchange, talkRoute+ talkId, MessageDto.builder()
                    .type("MESSAGE")
                    .talkId(talkId)
                    .insSabun("info")
                    .insName("시스템")
                    .insDate(LocalDateTime.now())
                    .message("["+ newWork.getWorkNm()+ "](으)로 업무 변경").build()
            );
        } else {
            talkTemplate.convertAndSend(talkExchange, talkRoute+ talkId, MessageDto.builder()
                    .type("MESSAGE")
                    .talkId(talkId)
                    .insSabun("info")
                    .insName("시스템")
                    .insDate(LocalDateTime.now())
                    .message("["+ newWork.getWorkNm()+ "](으)로 업무 변경").build()
            );
        }


        String talkMessage = "["+ parentWork.getWorkNm()+ "]에서 ["+newWork.getWorkNm()+"] 업무 변경";
        LocalDateTime talkMessageDate = LocalDateTime.now();
        AtomicBoolean onlineYn = new AtomicBoolean(false);

        userService.findSM(talkDto.getChangeTalk().getWorkId()).forEach((user -> {

            if(user.isOnlineYn()) onlineYn.set(true);
            memberList.add(TalkMemberDto.builder()
                    .talkId(talkDto.getChangeTalk().getTalkId())
                    .sabun(user.getSabun())
                    .name(user.getName())
                    .markYn(false)
                    .deleteYn(false)
                    .dummyYn(false)
                    .onlineYn(user.isOnlineYn()).build());

        }));

        memberList.add(TalkMemberDto.builder()
                .talkId(talkDto.getChangeTalk().getTalkId())
                .sabun(parentTalk.getInsSabun())
                .name(parentTalk.getInsName())
                .markYn(false)
                .deleteYn(false)
                .dummyYn(false)
                .onlineYn(true).build());

        memberList.add(TalkMemberDto.builder()
                .talkId(talkDto.getChangeTalk().getTalkId())
                .sabun("info")
                .name("시스템")
                .markYn(false)
                .deleteYn(false)
                .dummyYn(false)
                .onlineYn(true).build());

//        talkDto.setTalkMembers(memberList);
        TalkReceiveDto talkMessages = talkMessageService.getMessage(parentTalk.getTalkId());

        talkMemberService.saveAll(
                memberList.stream().map(TalkMember::to).toList()
        );

        memberList.forEach((member) -> {
            if(member.getOnlineYn()) {
                /*톡 메시지 호출*/
                MessagePostProcessor messagePostProcessor = message -> {
                    message.getMessageProperties().setHeader("sabun", member.getSabun());
                    message.getMessageProperties().setHeader("name", member.getName());
                    return message;
                };
                /*이전대화 읽음 처리*/
//            talkMessageService.read(talkId, member.getSabun());
                int unreadCnt = talkMessageService.unreadCountBySabun(talkDto.getChangeTalk().getTalkId(), member.getSabun());
                userTemplate.convertAndSend(userExchange, userRoute + member.getSabun(), TalkDto.builder()
                                .type("CHANGE")
                                .talkId(talkDto.getChangeTalk().getTalkId())
                                .markYn(false)
                                .insSabun(parentTalk.getInsSabun())
                                .insName(parentTalk.getInsName())
                                .insDate(LocalDateTime.now())
                                .message(talkMessage)
                                .unReadCnt(unreadCnt)
                                .readYn(false)
                                .deleteYn(false)
                                .ratingYn(false)
                                .work(newWork)
                                .talkMessages(talkMessages)
                                .talkMembers(memberList)
                                .lastMessage(talkMessage)
                                .lastMessageDate(talkMessageDate)
                                .changeTalk(talkDto.getChangeTalk())
                                .prevTalkId(parentTalk.getTalkId()).build()
                        , messagePostProcessor);
            }
        });

        workTemplate.convertAndSend(workExchange, workRoute + newWork.getWorkId(), TalkNotStartDto.builder()
                .type("WORK_COUNT")
                .notStart((int) talkService.nonStartTalk(newWork.getWorkId()))
                .workId(newWork.getWorkId())
                .build()
        );



        AtomicBoolean isSmOnlineYn = new AtomicBoolean(false);
        List<User> smUsers = userService.findSM(talkDto.getChangeTalk().getWorkId());
        smUsers.forEach((user -> {
            if(user.isOnlineYn()) isSmOnlineYn.set(true);
        }));

        /*방 생서후 메시지 전송 */
        class IntroSend extends Thread {
            public void run() {
                try {
                    Thread.sleep(1000);
                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkDto.getChangeTalk().getTalkId(), TalkDto.builder()
                            .type("MESSAGE")
                            .talkId(talkDto.getChangeTalk().getTalkId())
                            .markYn(false)
                            .insSabun("info")
                            .insName("시스템")
                            .workId(newWork.getWorkId())
                            .workNm(newWork.getWorkNm())
                            .insDate(LocalDateTime.now())
                            .message(talkMessage)
                            .readYn(false)
                            .unReadCnt(0)
                            .deleteYn(false)
                            .ratingYn(false)
                            .talkMessages(talkMessages)
                            .talkMembers(memberList)
                            .lastMessage(talkMessage)
                            .lastMessageDate(talkMessageDate)
//                            .changeTalk(talkDto.getChangeTalk())
                            .build()
                    );

                    Thread.sleep(500);
                    List<AutoReply> autoReplies = autoReplyService.findByTalkId(talkDto.getChangeTalk().getTalkId());
                    String message = "";
                    if(isSmOnlineYn.get()) {

                        AutoReply autoReply = autoReplies.stream().filter((ar) -> ar.getId().getType().equals("0010")).findFirst().orElse(null);
                        if(autoReply == null) {
                            message = "무엇을 도와드릴까요?";
                        } else {
                            message = autoReply.getContent();
                        }
                    } else {
                        AutoReply autoReply = autoReplies.stream().filter((ar) -> ar.getId().getType().equals("0020")).findFirst().orElse(null);
                        if(autoReply == null) {
                            message = "담당자 부재중이에요.";
                        } else {
                            message = autoReply.getContent();
                        }
                    }

                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkDto.getChangeTalk().getTalkId(), TalkDto.builder()
                            .type("INFO_MESSAGE")
                            .talkId(talkDto.getChangeTalk().getTalkId())
                            .insSabun(parentTalk.getInsSabun())
                            .insName(parentTalk.getInsName())
                            .workId(newWork.getWorkId())
                            .workNm(newWork.getWorkNm())
                            .insDate(LocalDateTime.now())
                            .message(message)
                            .readYn(false)
                            .tabId(talkDto.getTabId()).build()
                    );
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }
        IntroSend send = new IntroSend();
        send.setDaemon(true);
        send.start();
        send = null;
    }

    @Transactional
    @MessageMapping("talk.sm.change.{talkId}")
    public void smChange(TalkDto talkDto, @DestinationVariable String talkId, SimpMessageHeaderAccessor accessor) {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) accessor.getMessageHeaders().get("simpUser");;
        List<String> auths = token.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
        User currentUser = (User) token.getPrincipal();

        /*기존 데이터 호출*/
        /*데이터 복사*/
        Talk parentTalk = talkService.findByOne(talkId);
        Work parentWork = parentTalk.getWork();

//        parentWork.setSmUsers(parentWork.getSmUsers().stream().filter((user) -> user.getId().getSabun().equals(talkDto.getChangeTalk().getSabun())).toList());
        /*일반 사용자가 방 생성시 모든 유지보수 담당자에게 방생성*/
        List<TalkMemberDto> memberList = new LinkedList<>();

        talkService.createTalk(Talk.builder()
                .talkId(talkDto.getChangeTalk().getTalkId())
                .closeYn(false)
                .compYn(false)
                .startYn(false)
                .workId(parentTalk.getWorkId())
                .insSabun(parentTalk.getInsSabun())
                .insDate(LocalDateTime.now())
                .insName(parentTalk.getInsName())
                .build()
        );

        /*이전 대화 복사 start*/
        if(talkDto.getGubun() == 0 || talkDto.getGubun() == 1) {
            talkService.dataCopy(talkId, talkDto.getChangeTalk().getTalkId(), false);
        } else {
            talkService.dataCopy(talkId, talkDto.getChangeTalk().getTalkId(), true);
        }

        /* 현재 문의자 더미 해제 */
        talkService.updateDummy(talkDto.getChangeTalk().getTalkId(), parentTalk.getInsSabun());

        /* 변경 대상 담당자 조회*/
        User chgUser = userService.findByWorkIdAndSabun(parentTalk.getWorkId(), talkDto.getChangeTalk().getSabun());

        /*상담구분에 따른 톡방 삭제
         * 0 : 단순문의
         * 1 : 상담
         * 2 : pi 승인
         * 3 : scm
         * */ 
        if(talkDto.getGubun() == 0) {
            // 멤버 전체 톡방 삭제
            talkMemberService.disableTalkId(talkId);
            talkMemberService.findDeleteTarget(talkId).stream().forEach((member) -> {
                userTemplate.convertAndSend(userExchange, userRoute + member.getId().getSabun(), MessageDto.builder()
                        .type("DELETE")
                        .talkId(talkId)
                        .build());
            });
        } else if(talkDto.getGubun() == 1) {
            // 유지보수자를 제외한 사용자만 톡방 삭제
            talkMemberService.disableTalkId(talkId);
            talkMemberService.enableTalkIdAndSabun(talkId, talkDto.getInsSabun());
            talkMemberService.findDeleteTarget(talkId).stream().forEach((member) -> {
                if(!member.getId().getSabun().equals(talkDto.getInsSabun())) {
                    userTemplate.convertAndSend(userExchange, userRoute + member.getId().getSabun(), MessageDto.builder()
                            .type("DELETE")
                            .talkId(talkId)
                            .build());
                }
            });

            talkTemplate.convertAndSend(talkExchange, talkRoute+ parentTalk.getTalkId(), MessageDto.builder()
                    .type("MESSAGE")
                    .talkId(parentTalk.getTalkId())
                    .insSabun("info")
                    .insName("시스템")
                    .insDate(LocalDateTime.now())
                    .message("["+ chgUser.getName() + "] 담당자 변경").build()
            );
        } else {
            talkTemplate.convertAndSend(talkExchange, talkRoute+ parentTalk.getTalkId(), MessageDto.builder()
                    .type("MESSAGE")
                    .talkId(parentTalk.getTalkId())
                    .insSabun("info")
                    .insName("시스템")
                    .insDate(LocalDateTime.now())
                    .message("["+ chgUser.getName() + "] 담당자 변경").build()
            );
        }


        String talkMessage = "["+ parentTalk.getWork().getWorkNm() + "]에서 담당자["+chgUser.getName()+"]으로 변경";
        LocalDateTime talkMessageDate = LocalDateTime.now();

        AtomicBoolean onlineYn = new AtomicBoolean(false);

        if(chgUser.isOnlineYn()) onlineYn.set(true);
        memberList.add(TalkMemberDto.builder()
                .talkId(talkDto.getChangeTalk().getTalkId())
                .sabun(chgUser.getSabun())
                .name(chgUser.getName())
                .markYn(false)
                .deleteYn(false)
                .dummyYn(false)
                .onlineYn(chgUser.isOnlineYn()).build());

        memberList.add(TalkMemberDto.builder()
                .talkId(talkDto.getChangeTalk().getTalkId())
                .sabun(parentTalk.getInsSabun())
                .name(parentTalk.getInsName())
                .markYn(false)
                .deleteYn(false)
                .dummyYn(false)
                .onlineYn(true).build());

        memberList.add(TalkMemberDto.builder()
                .talkId(talkDto.getChangeTalk().getTalkId())
                .sabun("info")
                .name("시스템")
                .markYn(false)
                .deleteYn(false)
                .dummyYn(false)
                .onlineYn(true).build());

//        talkDto.setTalkMembers(memberList);
        TalkReceiveDto talkMessages = talkMessageService.getMessage(parentTalk.getTalkId());


        talkMemberService.saveAll(
                memberList.stream().map(TalkMember::to).toList()
        );

        memberList.forEach((member) -> {
            if(member.getOnlineYn()) {
                /*사용자 로그인이 아닐시 메시지 제외*/
                MessagePostProcessor messagePostProcessor = message -> {
                    message.getMessageProperties().setHeader("sabun", member.getSabun());
                    message.getMessageProperties().setHeader("name", member.getName());
                    return message;
                };
                /*이전대화 읽음 처리*/
//            talkMessageService.read(talkId, member.getSabun());
                int unreadCnt = talkMessageService.unreadCountBySabun(talkDto.getChangeTalk().getTalkId(), member.getSabun());
                userTemplate.convertAndSend(userExchange, userRoute + member.getSabun(), TalkDto.builder()
                                .type("CHANGE")
                                .talkId(talkDto.getChangeTalk().getTalkId())
                                .markYn(false)
                                .insSabun(parentTalk.getInsSabun())
                                .insName(parentTalk.getInsName())
                                .insDate(LocalDateTime.now())
                                .message(talkMessage)
                                .unReadCnt(unreadCnt)
                                .readYn(false)
                                .deleteYn(false)
                                .ratingYn(false)
                                .work(WorkDto.to(parentWork))
                                .talkMessages(talkMessages)
                                .talkMembers(memberList)
                                .lastMessage(talkMessage)
                                .lastMessageDate(talkMessageDate)
                                .prevTalkId(parentTalk.getTalkId())
                                .changeTalk(talkDto.getChangeTalk()).build()
                        , messagePostProcessor);
            }
        });

//        workTemplate.convertAndSend(workExchange, workRoute + talkDto.getWorkId(), TalkNotStartDto.builder()
//                .type("WORK_COUNT")
//                .notStart((int) talkService.nonStartTalk(talkDto.getWorkId()))
//                .workId(parentTalk.getWorkId())
//                .build()
//        );

        AtomicBoolean isSmOnlineYn = new AtomicBoolean(false);
        List<User> smUsers = userService.findSM(talkDto.getChangeTalk().getWorkId());
        smUsers.forEach((user -> {
            if(user.isOnlineYn()) isSmOnlineYn.set(true);
        }));

        /*방 생서후 메시지 전송 */
        class IntroSend extends Thread {
            public void run() {
                try {
                    Thread.sleep(1000);
                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkDto.getChangeTalk().getTalkId(), TalkDto.builder()
                            .type("MESSAGE")
                            .talkId(talkDto.getChangeTalk().getTalkId())
                            .markYn(false)
                            .insSabun("info")
                            .insName("시스템")
                            .workId(parentWork.getWorkId())
                            .workNm(parentWork.getWorkNm())
                            .insDate(LocalDateTime.now())
                            .message(talkMessage)
                            .readYn(false)
                            .unReadCnt(0)
                            .deleteYn(false)
                            .ratingYn(false)
                            .talkMessages(talkMessages)
                            .talkMembers(memberList)
                            .lastMessage(talkMessage)
                            .lastMessageDate(talkMessageDate)
//                            .changeTalk(talkDto.getChangeTalk())
                            .build()
                    );

                    Thread.sleep(500);
                    List<AutoReply> autoReplies = autoReplyService.findByTalkId(talkDto.getChangeTalk().getTalkId());
                    String message = "";
                    if(isSmOnlineYn.get()) {

                        AutoReply autoReply = autoReplies.stream().filter((ar) -> ar.getId().getType().equals("0010")).findFirst().orElse(null);
                        if(autoReply == null) {
                            message = "무엇을 도와드릴까요?";
                        } else {
                            message = autoReply.getContent();
                        }
                    } else {
                        AutoReply autoReply = autoReplies.stream().filter((ar) -> ar.getId().getType().equals("0020")).findFirst().orElse(null);
                        if(autoReply == null) {
                            message = "담당자 부재중이에요.";
                        } else {
                            message = autoReply.getContent();
                        }
                    }

                    talkTemplate.convertAndSend(talkExchange, talkRoute + talkDto.getChangeTalk().getTalkId(), TalkDto.builder()
                            .type("INFO_MESSAGE")
                            .talkId(talkDto.getChangeTalk().getTalkId())
                            .insSabun(parentTalk.getInsSabun())
                            .insName(parentTalk.getInsName())
                            .workId(parentWork.getWorkId())
                            .workNm(parentWork.getWorkNm())
                            .insDate(LocalDateTime.now())
                            .message(message)
                            .readYn(false)
                            .tabId(talkDto.getTabId()).build()
                    );
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }
        IntroSend send = new IntroSend();
        send.setDaemon(true);
        send.start();
        send = null;
    }

    @MessageMapping("talk.message.{talkId}")
    public void send(MessageDto messageDto, @DestinationVariable String talkId, Principal principal) {
        if(!talkId.isEmpty() && !talkId.isBlank()) {
            messageDto.setInsSabun(principal.getName());
            messageDto.setInsDate(LocalDateTime.now());
            messageDto.setTalkId(talkId);
            messageDto.setMessage(badWordFiltering.change(messageDto.getMessage()));
            talkTemplate.convertAndSend(talkExchange, talkRoute + talkId, messageDto);

            if(messageDto.getType().equals("START") || messageDto.getType().equals("CLOSE")) {
                workTemplate.convertAndSend(workExchange, workRoute + messageDto.getWorkId(), TalkNotStartDto.builder()
                        .type("WORK_COUNT")
                        .notStart((int) talkService.nonStartTalk(messageDto.getWorkId()))
                        .workId(messageDto.getWorkId())
                        .build()
                );
            }
        }

//        List<PushDto> pushDtos = new LinkedList<>();
//
//
//        if(talkDto.getType().equals("MESSAGE")) {
//            List<TalkMember> talkMembers = talkMemberService.findAllByTalkId(talkId);
//            talkMembers.forEach((talkMember -> {
//                if(!talkMember.getId().getSabun().equals(principal.getName()) && !talkMember.getId().getSabun().equals("info")) {
//                    pushDtos.add(PushDto.builder()
//                                    .workNm(talkDto.getWorkNm())
//                                    .insName(talkDto.getInsName())
//                                    .talkId(talkId)
//                                    .userId(talkMember.getId().getSabun())
//                                    .message(talkDto.getMessage()).build());
//                    pushDtos.add(PushDto.builder()
//                                    .workNm(talkDto.getWorkNm())
//                                    .insName(talkDto.getInsName())
//                                    .talkId(talkId)
//                                    .userId(talkMember.getId().getSabun())
//                                    .message(talkDto.getMessage()).build());
//                    fluxClient.sendPush(pushDtos);
//                }
//            }));
//
//        }

    }


    @MessageMapping("talk.leave.{talkId}")
    public void leave(TalkDto talkDto, @DestinationVariable String talkId, Principal principal) {
        talkDto.setType("LEAVE");
        talkDto.setInsDate(LocalDateTime.now());
        List<ITalkMemberDto> members = talkMemberService.findByTalkId(talkId);
        members.forEach((member) -> {
            talkDto.setInsSabun(member.getSabun());
            userTemplate.convertAndSend(userExchange, userRoute + member.getSabun(), talkDto);
        });


        workTemplate.convertAndSend(workExchange, workRoute + talkDto.getWorkId(), TalkNotStartDto.builder()
                .type("WORK_COUNT")
                .notStart((int) talkService.nonStartTalk(talkDto.getWorkId()))
                .workId(talkDto.getWorkId())
                .build()
        );



        class TalkDelete extends Thread {
            public void run() {
                try {
                    Thread.sleep(3000);
                    Talk talk = talkService.findByOne(talkId);
                    if(talk.getInsSabun().equals(principal.getName())) {
                        talkService.deleteTalk(talkId);
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        TalkDelete talkDelete = new TalkDelete();
        talkDelete.setDaemon(true);
        talkDelete.start();
        talkDelete = null;
    }

    @MessageMapping("talk.attach.{talkId}")
    @Transactional
    public void attach(MessageDto messageDto, @DestinationVariable String talkId, Principal principal) throws IOException {
        messageDto.setInsSabun(principal.getName());
        if(messageDto.getType().equals("INIT")) {
//            System.out.println("ATTACH_INIT");
            messageDto.setType("ATTACH_INIT");
            messageDto.setInsDate(messageDto.getInsDate());
            messageDto.setTalkAttachDtos(messageDto.getTalkAttachDtos().stream().filter((att) -> CommonUtils.checkAllowExt(att.getOnm())).toList());
            messageDto.setUploading(true);
            if(!messageDto.getTalkAttachDtos().isEmpty()) {
                talkTemplate.convertAndSend(talkExchange, talkRoute + talkId, messageDto);
            }
        } else if(messageDto.getType().equals("START")) {
//            System.out.println("ATTACH_START");
            if(CommonUtils.checkAllowExt(messageDto.getAttach().getOnm())) {
//                System.out.println(messageDto.getAttach().getVnm());
                messageDto.setInsDate(messageDto.getInsDate());
                messageDto.getAttach().setVnm(messageDto.getAttach().getVnm());
                messageDto.setUploading(true);
                messageDto.setType("ATTACH_START");
                fileTemplate.convertAndSend(fileExchange, fileRoute + talkId, messageDto);
            }

        } else if(messageDto.getType().equals("PROGRESS")){
//            System.out.println("ATTACH_PROGRESS");
            int currentSize = messageDto.getAttach().getData().length;
            long fileSize = messageDto.getAttach().getSize() + currentSize;
            long progress = (fileSize * 100) / messageDto.getAttach().getTotalSize();

            messageDto.setType("ATTACH_PROGRESS");
            messageDto.getAttach().setPercent(progress + "%");
            messageDto.getAttach().setVnm(messageDto.getAttach().getVnm());
            messageDto.setUploading(true);
            if(CommonUtils.checkAllowExt(messageDto.getAttach().getOnm())) {
                fileTemplate.convertAndSend(fileExchange, fileRoute + talkId, messageDto);
            }
//            System.out.println(progress);
            if(progress >= 100) {
//                System.out.println("ATTACH_END");
                messageDto.setType("ATTACH_END");
                messageDto.setUploading(false);
                fileTemplate.convertAndSend(fileExchange, fileRoute + talkId, messageDto);
//                talkTemplate.convertAndSend(talkExchange, talkRoute + talkId, messageDto);
            }
        }
    }

}
