package com.kospo.talk.service.impl;

import com.kospo.talk.model.Talk;
import com.kospo.talk.model.TalkMember;
import com.kospo.talk.model.TalkMemberId;
import com.kospo.talk.model.TalkMessage;
import com.kospo.talk.payload.*;
import com.kospo.talk.repository.*;
import com.kospo.talk.service.TalkService;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class TalkServiceImpl implements TalkService {

    private final TalkRepository talkRepository;
    private final TalkMemberRepository talkMemberRepository;
    private final TalkMessageRepository talkMessageRepository;
    private final TalkReadRepository talkReadRepository;
    private final TalkAttachRepository talkAttachRepository;
    private final SmUserRepository smUserRepository;

    @Override
    public List<Talk> findByUserNameSimple(String sabun) {
        return talkRepository.findByMemberSabun(sabun);
    }

    @Transactional
    @Override
    public List<TalkDto> findByMemberSabun(String sabun) {
        List<TalkDto> talkDtos = talkRepository.findByMemberSabun(sabun).stream().map(TalkDto::to).toList();
        talkDtos.forEach((cd) -> {
            List<ITalkMemberDto> talkMembers = talkMemberRepository.findByTalkId(cd.getTalkId());
            /*본인 멤버 정보로 마크 여부 삭제 여부 조회*/
            talkMembers.stream().forEach((cm) -> {
                if(cm.getSabun().equals(sabun)) {
                    cd.setMarkYn(cm.getMarkYn());
                    cd.setDeleteYn(cm.getDeleteYn());
                }
            });
            cd.setTalkMembers(
                    talkMembers.stream().map(TalkMemberDto::from).toList()
            );

            cd.setMessageParams(TalkParamDto.builder().talkId(cd.getTalkId()).start(0).row(30).build());
            cd.setUnReadCnt(talkMessageRepository.unreadCountBySabun(cd.getTalkId(), sabun));
        });
        return talkDtos.stream().sorted(Comparator.comparing(TalkDto::getMarkYn).thenComparing(TalkDto::getLastMessageDate).reversed()).toList();
    }

    @Transactional
    @Override
    public TalkDto findByTalkId(String talkId) {
        try {
            return TalkDto.to(talkRepository.findById(talkId).get());
        } catch (NoSuchElementException e) {
            return null;
        }
    }

    @Transactional
    @Override
    public List<TalkDto> findTalkGroup(String sabun, int start, int end) {
        List<TalkDto> talkDtos = talkRepository.findTalkGroup(sabun, start, end).stream().map(TalkDto::to).toList();
        talkDtos.forEach((cd) -> {
            List<ITalkMemberDto> talkMembers = talkMemberRepository.findByTalkId(cd.getTalkId());
            /*본인 멤버 정보로 마크 여부 삭제 여부 조회*/
            talkMembers.stream().forEach((cm) -> {
                if(cm.getSabun().equals(sabun)) {
                    cd.setMarkYn(cm.getMarkYn());
                    cd.setDeleteYn(cm.getDeleteYn());
                }
            });
            cd.setTalkMembers(
                    talkMembers.stream().map(TalkMemberDto::from).toList()
            );

            cd.setMessageParams(TalkParamDto.builder().talkId(cd.getTalkId()).start(0).row(30).build());
            cd.setUnReadCnt(0);

        });
        return talkDtos.stream().sorted(Comparator.comparing(TalkDto::getLastMessageDate).reversed()).toList();
    }



    @Transactional
    @Override
    public TalkDto findTalkGroupByTalkId(String sabun, String talkId) {
        TalkDto talkDto = TalkDto.to(talkRepository.findById(talkId).get());

        List<ITalkMemberDto> talkMembers = talkMemberRepository.findByTalkId(talkId);
        talkMembers.stream().forEach((cm) -> {
            if(cm.getSabun().equals(sabun)) {
                talkDto.setMarkYn(cm.getMarkYn());
                talkDto.setDeleteYn(cm.getDeleteYn());
            }
        });
        talkDto.setTalkMembers(
                talkMembers.stream().map(TalkMemberDto::from).toList()
        );

        talkDto.setMessageParams(TalkParamDto.builder().talkId(talkId).start(0).row(30).build());
        talkDto.setUnReadCnt(talkMessageRepository.unreadCountBySabun(talkId, sabun));
        talkDto.setTalkMessages(TalkReceiveDto.builder()
                .talks(talkMessageRepository.findByLineAsc(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())
                .total(talkMessageRepository.findByTalkIdCount(talkId))
                .build());
        return talkDto;

    }

    @Transactional
    @Override
    public TalkDto findByIdAndSabun(String talkId, String sabun) {
        try {
            Optional<Talk> talk = talkRepository.findById(talkId);
            if(talk.isPresent()) {
                return TalkDto.to(talk.get());
            }
            return null;
        } catch(NoClassDefFoundError e) {
            return null;
        }
    }

    @Override
    public Talk findByOne(String talkId) {
        return talkRepository.findById(talkId).get();
    }
    @Override
    public Talk findByTalkIdAndInsSabun(String talkId, String sabun) {
        return talkRepository.findByTalkIdAndInsSabun(talkId, sabun);
    }

    @Override
    public Talk createTalk(Talk talk) {
        talkRepository.save(talk);
        return talk;
    }

    @Override
    public void ratingUpdate(String reqId) {
        Talk talk = talkRepository.findByReqId(reqId);
        talk.setRatingYn(true);
        talkRepository.save(talk);
    }

    @Override
    public void appendMember(Talk talk) {
        talkMemberRepository.save(
                TalkMember.builder()
                        .id(
                                TalkMemberId.builder()
                                .talkId(talk.getTalkId())
                                .sabun(talk.getInsSabun()).build()
                        )
                        .markYn(false)
                        .deleteYn(false)
                        .name(talk.getInsName()).build()
        );
    }

    @Override
    public void deleteTalk(String talkId) {
        talkRepository.deleteById(talkId);
    }

    @Override
    public void delete(Talk talk) {
        talkRepository.delete(talk);
    }


    @Override
    @Transactional
    public void dataCopy(String pTalkId, String talkId, boolean readForce) {
        talkMemberRepository.dataCopy(pTalkId, talkId);
        talkMessageRepository.dataCopy(pTalkId, talkId);
        if(readForce) {
            talkReadRepository.dataCopyReadForce(pTalkId, talkId);
        } else {
            talkReadRepository.dataCopyReadOri(pTalkId, talkId);
        }
        talkAttachRepository.dataCopy(pTalkId, talkId);
    }


    @Override
    public void updateDummy(String talkId, String sabun) {
        talkMemberRepository.dummyUpdate(talkId, sabun);
    }

    @Override
    public long nonStartTalk(String workId) {
        long notStart = talkRepository.countByWorkIdAndStartYn(workId, false);
        long smUserCnt = smUserRepository.countByIdWorkId(workId);
        if(smUserCnt == 0) smUserCnt = 1;
        return notStart / smUserCnt;
    }

}
