package com.kospo.talk.service.impl;

import com.kospo.talk.model.*;
import com.kospo.talk.payload.*;
import com.kospo.talk.repository.*;
import com.kospo.talk.service.TalkMessageService;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;


@Service
@RequiredArgsConstructor
public class TalkMessageServiceImpl implements TalkMessageService {
    @Value("${rabbitmq.user.exchange}")
    private String userExchange;
    @Value("${rabbitmq.user.route}")
    private String userRoute;
    private final TalkRepository talkRepository;
    private final TalkMessageRepository talkMessageRepository;
    private final TalkReadRepository talkReadRepository;
    private final TalkAttachRepository talkAttachRepository;
    private final WorkRepository workRepository;
    private final TalkMemberRepository talkMemberRepository;
    private final RabbitTemplate userTemplate;


    @Override
    public List<TalkMessage> findAll(String talkId) {
        return talkMessageRepository.findByIdTalkId(talkId);
    }

    @Override
    public TalkMessage save(TalkMessage talkMessage) {
        return talkMessageRepository.save(talkMessage);
    }

    @Override
    public void externalSave(ExternalMessageDto externalMessageDto) {
        Work work = workRepository.findByTalkId(externalMessageDto.getTalkId());
        MessageDto messageDto = MessageDto.builder()
            .type("STATUS")
            .message(externalMessageDto.getMessage())
            .talkId(externalMessageDto.getTalkId())
                .workId(work.getWorkId())
                .workNm(work.getWorkNm())
                .insSabun("info")
                .insName("시스템")
                .searchYn(false)
                .insDate(LocalDateTime.now())
            .reqType(externalMessageDto.getType())
            .build();
        talkMessageRepository.save(TalkMessage.builder()
                .id(TalkMessageId.builder().talkId(messageDto.getTalkId()).sabun(messageDto.getInsSabun()).insDate(messageDto.getInsDate()).build())
                .message(messageDto.getMessage())
                .searchYn(false)
                .insName(messageDto.getInsName()).build()
        );


        talkMemberRepository.findByIdTalkId(messageDto.getTalkId()).stream().forEach(talkMember -> {
            talkMember.getId().getSabun();
            /*메시지 전송 대상자가 해당 톡방을 삭제 햇는지 확인(삭제시 미전송)*/
            if(!talkMember.getDeleteYn()) {
                userTemplate.convertAndSend(userExchange, userRoute + talkMember.getId().getSabun(), messageDto);
            }
        });

        Talk talk = talkRepository.findById(messageDto.getTalkId()).get();
        /*요청 완료 처리 이후 만족사 조사 처리*/
        if(externalMessageDto.getType().equals("9000")) {
            talk.setCompYn(true);
            talk.setLastMessage(messageDto.getMessage());
            talk.setLastMessageDate(messageDto.getInsDate());
        } else {
            talk.setLastMessage(messageDto.getMessage());
            talk.setLastMessageDate(messageDto.getInsDate());
        }
        talkRepository.save(talk);
    }

    @Override
    public void saveAll(List<TalkMessage> talkMessages) {
        talkMessageRepository.saveAll(talkMessages);
    }

    @Override
    @Transactional
    public boolean read(String talkId, String sabun) {
        /*메시지 저장 간격과 동일 하게 쓰레드 적용*/
        class ReadThread extends Thread {
            public void run() {
                try {
                    Thread.sleep(300);
                    talkReadRepository.updateTalkReadYn(talkId, sabun);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }

        ReadThread readThread = new ReadThread();
        readThread.setDaemon(true);
        readThread.start();
        readThread = null;
//        List<ITalkMessageDto> unReads = talkMessageRepository.unReadContent(talkId, sabun);
//        talkReadRepository.saveAll(unReads.stream().map((ur) ->
//            TalkRead.builder()
//                .id(TalkReadId.builder().talkId(talkId).sabun(sabun).insDate(ur.getInsDate()).build())
//                .readYn(true).build()
//        ).toList());

        return true;
    }

    @Override
    @Transactional
    public boolean allRead(String talkId) {
        List<ITalkMessageDto> unReads = talkMessageRepository.allUnReadContent(talkId);
        talkReadRepository.saveAll(unReads.stream().map((ur) ->
            TalkRead.builder()
                .id(TalkReadId.builder().talkId(talkId).sabun(ur.getSabun()).insDate(ur.getInsDate()).build())
                .readYn(true).build()
        ).toList());

        return true;
    }

    @Transactional
    @Override
    public List<TalkDto> getMessageAll(String sabun) {
        List<String> talks =  talkRepository.findTalkIdListBySabun(sabun);
        return talks.stream().map((talkId) -> {
            List<MessageDto> dtos = new java.util.ArrayList<>(talkMessageRepository.findByLine(talkId, 0, 30, LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"))).stream().map((c) -> MessageDto.builder()
                    .talkId(c.getTalkId())
                    .insSabun(c.getSabun())
                    .insName(c.getInsName())
                    .message(c.getMessage())
                    .line(c.getLine())
                    .insDate(c.getInsDate())
                    .searchYn(c.getSearchYn())
                    .talkAttachDtos(
                            talkAttachRepository.findByList(
                                    c.getTalkId(),
                                    c.getSabun(),
                                    c.getInsDate()).stream().map(TalkAttachDto::to
                            ).toList()
                    ).build()).toList());

            dtos.sort(Comparator.comparing(MessageDto::getInsDate));

            return TalkDto.builder().talkMessages(TalkReceiveDto.builder()
                .talks(dtos)
                .total(talkMessageRepository.findByTalkIdCount(talkId)).build()).talkId(talkId).build();
        }).toList();

    }


    @Transactional
    @Override
    public List<TalkDto> getMessageAllMore(String sabun, String insDate) {
        List<String> talks =  talkRepository.findTalkIdListBySabunMore(sabun, insDate);
        return talks.stream().map((talkId) -> {
            List<MessageDto> dtos = new java.util.ArrayList<>(talkMessageRepository.findByLineMore(talkId, 0, 300, insDate)).stream().map((c) -> MessageDto.builder()
                    .talkId(c.getTalkId())
                    .insSabun(c.getSabun())
                    .insName(c.getInsName())
                    .message(c.getMessage())
                    .line(c.getLine())
                    .insDate(c.getInsDate())
                    .searchYn(c.getSearchYn())
                    .talkAttachDtos(
                            talkAttachRepository.findByList(
                                    c.getTalkId(),
                                    c.getSabun(),
                                    c.getInsDate()).stream().map(TalkAttachDto::to
                            ).toList()
                    ).build()).toList();


            return TalkDto.builder().talkMessages(TalkReceiveDto.builder()
                .talks(dtos)
                .total(talkMessageRepository.findByTalkIdCount(talkId)).build()).talkId(talkId).build();
        }).toList();

    }

    @Transactional
    @Override
    public TalkReceiveDto getMessage(String talkId) {
        List<MessageDto> dtos = new java.util.ArrayList<>(talkMessageRepository.findByLine(talkId, 0, 30, LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"))).stream().map((c) -> MessageDto.builder()
                .talkId(c.getTalkId())
                .insSabun(c.getSabun())
                .insName(c.getInsName())
                .message(c.getMessage())
                .line(c.getLine())
                .insDate(c.getInsDate())
                .searchYn(c.getSearchYn())
                .talkAttachDtos(
                        talkAttachRepository.findByList(
                                c.getTalkId(),
                                c.getSabun(),
                                c.getInsDate()).stream().map(TalkAttachDto::to
                        ).toList()
                ).build()).toList());

        dtos.sort(Comparator.comparing(MessageDto::getInsDate));

        return TalkReceiveDto.builder()
                .talks(dtos)
                .total(talkMessageRepository.findByTalkIdCount(talkId)).build();

    }

    @Transactional
    @Override
    public TalkReceiveDto getMessageLine(String talkId, Integer start, Integer end, String insDate) {
        if(Objects.equals(insDate, "")) insDate = LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"));
        List<MessageDto> dtos = new java.util.ArrayList<>(talkMessageRepository.findByLine(talkId, start, end, insDate).stream().map((c) -> MessageDto.builder()
                .talkId(c.getTalkId())
                .insSabun(c.getSabun())
                .insName(c.getInsName())
                .message(c.getMessage())
                .line(c.getLine())
                .insDate(c.getInsDate())
                .searchYn(c.getSearchYn())
                .talkAttachDtos(
                        talkAttachRepository.findByList(
                                c.getTalkId(),
                                c.getSabun(),
                                c.getInsDate()).stream().map(TalkAttachDto::to
                        ).toList()
                ).build()).toList());

        dtos.sort(Comparator.comparing(MessageDto::getInsDate));

        return TalkReceiveDto.builder().talks(dtos).total(talkMessageRepository.findByTalkIdCount(talkId)).build();
    }

    @Transactional
    @Override
    public List<MessageDto> getMessage(String talkId, String insDate) {

        List<MessageDto> dtos = talkMessageRepository.findByIdTalkIdAndIdInsDate(talkId, insDate).stream().map((c) -> MessageDto.builder()
                .talkId(c.getId().getTalkId())
                .insSabun(c.getId().getSabun())
                .insName(c.getInsName())
                .message(c.getMessage())
                .insDate(c.getId().getInsDate())
                .searchYn(c.getSearchYn())
                .talkAttachDtos(
                        talkAttachRepository.findByList(
                                c.getId().getTalkId(),
                                c.getId().getSabun(),
                                c.getId().getInsDate()).stream().map(TalkAttachDto::to
                                ).toList()
                ).build()
        ).toList();


        return dtos;
    }

    @Transactional
    @Override
    public TalkReceiveDto getMessageWeek(String talkId, String week) {
        List<MessageDto> dtos = null;
        if(week.equals("1")) {
            dtos = talkMessageRepository.findByIdTalkIdAndWeekFirst(talkId, week).stream().map((c) -> MessageDto.builder()
                    .talkId(c.getTalkId())
                    .insSabun(c.getSabun())
                    .insName(c.getInsName())
                    .message(c.getMessage())
                    .insDate(c.getInsDate())
                    .line(c.getLine())
                    .searchYn(c.getSearchYn())
                    .talkAttachDtos(
                            talkAttachRepository.findByList(
                                    c.getTalkId(),
                                    c.getSabun(),
                                    c.getInsDate()).stream().map(TalkAttachDto::to
                                    ).toList()
                    )
                    .build()
            ).toList();
        } else {
            dtos = talkMessageRepository.findByIdTalkIdAndWeek(talkId, week).stream().map((c) -> MessageDto.builder()
                    .talkId(c.getTalkId())
                    .insSabun(c.getSabun())
                    .insName(c.getInsName())
                    .message(c.getMessage())
                    .insDate(c.getInsDate())
                    .line(c.getLine())
                    .talkAttachDtos(
                            talkAttachRepository.findByList(
                                    c.getTalkId(),
                                    c.getSabun(),
                                    c.getInsDate()).stream().map(TalkAttachDto::to
                                    ).toList()
                    )
                    .build()
            ).toList();
        }



        return TalkReceiveDto.builder().talks(dtos).total(talkMessageRepository.findByTalkIdCount(talkId)).build();

    }

    @Transactional
    @Override
    public TalkReceiveDto getSearchMessage(String talkId, String search) {

        List<MessageDto> dtos = talkMessageRepository.findSearchContent(talkId, search).stream().map((c) -> MessageDto.builder()
                .talkId(c.getTalkId())
                .insSabun(c.getSabun())
                .insName(c.getInsName())
                .message(c.getMessage())
                .insDate(c.getInsDate())
                .line(c.getLine())
                .searchYn(c.getSearchYn())
                .talkAttachDtos(
                        talkAttachRepository.findByList(
                                c.getTalkId(),
                                c.getSabun(),
                                c.getInsDate()).stream().map(TalkAttachDto::to
                                ).toList()
                ).build()
        ).toList();


        return TalkReceiveDto.builder().talks(dtos).total(talkMessageRepository.findByTalkIdCount(talkId)).build();
    }

    @Transactional
    @Override
    public List<MessageDto> getAllSearchContent(String search, String sabun) {
        return talkMessageRepository.findAllSearchContent(search, sabun).stream().map((c) -> MessageDto.builder()
                .talkId(c.getTalkId())
                .insSabun(c.getSabun())
                .insName(c.getInsName())
                .message(c.getMessage())
                .insDate(c.getInsDate())
                .searchYn(c.getSearchYn())
                .line(c.getLine()).build()
        ).toList();
    }



    @Override
    public void saveContent(TalkMessage talkMessage) {
        talkMessageRepository.save(talkMessage);
    }

    @Transactional
    @Override
    public List<MessageDto> getMessage(String talkId, Pageable pageable) {

        List<MessageDto> dtos = talkMessageRepository.findByIdTalkId(talkId, pageable).getContent().stream().map((c) -> MessageDto.builder()
                .talkId(c.getId().getTalkId())
                .insSabun(c.getId().getSabun())
                .insName(c.getInsName())
                .message(c.getMessage())
                .insDate(c.getId().getInsDate())
                .searchYn(c.getSearchYn())
                .talkAttachDtos(
                        talkAttachRepository.findByList(
                                c.getId().getTalkId(),
                                c.getId().getSabun(),
                                c.getId().getInsDate()).stream().map(TalkAttachDto::to
                                ).toList()
                ).build()
        ).toList();


        return dtos;
    }



    @Override
    public ITalkMessageDto getLastContent(String talkId) {
        return talkMessageRepository.findLastMessage(talkId);
    }

    @Override
    public int unreadCountBySabun(String talkId, String sabun) {
        return talkMessageRepository.unreadCountBySabun(talkId, sabun);
    }
}
