package com.arms.api.bbs.service;

import com.arms.api.bbs.model.dto.BbsDTO;
import com.arms.api.bbs.model.entity.BbsEntity;
import com.arms.api.bbs.model.vo.BbsVO;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.SearchDocDTO;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.SortDTO;
import com.arms.egovframework.javaservice.esframework.model.vo.DocumentResultWrapper;
import com.arms.egovframework.javaservice.esframework.repository.common.EsCommonRepositoryWrapper;
import com.arms.egovframework.javaservice.esframework.esquery.SimpleQuery;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.List;

import lombok.AllArgsConstructor;

/*
Match 쿼리 사용:
자연어 텍스트 검색 (제목, 내용, 설명 등)
사용자 검색어 입력
부분 일치 허용

Term 쿼리 사용:
정확한 값 필터링 (상태, 카테고리, ID)
열거형(enum) 값 검색
숫자, 날짜, boolean 값
 */
@Component
@AllArgsConstructor
public class BbsServiceImpl implements BbsService {

    private final EsCommonRepositoryWrapper<BbsEntity> esCommonRepositoryWrapper;

    @Override
    public String postMessage(BbsDTO bbsDTO) {

        BbsEntity save = esCommonRepositoryWrapper.save(bbsDTO.toBbsEntityWithCreate());

        save.viewCountInit();

        return save.getId();
    }

    @Override
    public String replyMessage(BbsDTO bbsDTO) {

        DocumentResultWrapper<BbsEntity> hits = esCommonRepositoryWrapper.findHits(
            SimpleQuery.termQueryFilter("group_id", bbsDTO.getPath().split("/")[0])
        );

        if(hits.toDocs().isEmpty()){
            throw new IllegalArgumentException("삭제된 게시글입니다.");
        }

        BbsEntity save = esCommonRepositoryWrapper.save(bbsDTO.toBbsEntityWithCreate());

        save.viewCountInit();

        return save.getId();
    }

    @Override
    public String checkPassword(BbsDTO bbsDTO) {
        BbsEntity findBbsEntity = esCommonRepositoryWrapper.findDocById(bbsDTO.getId());

        if(findBbsEntity==null){
            throw new IllegalArgumentException("삭제된 게시글입니다.");
        }

        if(findBbsEntity.getPassword() != null && !findBbsEntity.getPassword().equals(bbsDTO.getPassword())){
            throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
        }

        return findBbsEntity.getId();
    }

    @Override
    public String updateBbsAndNewGroupId(BbsDTO bbsDTO){

        String bbsId = updateBbsAndGetId(bbsDTO);
        BbsEntity bbsEntity = esCommonRepositoryWrapper.findDocById(bbsId);
        bbsEntity.getPostType().checkPostType();

        BbsDTO sourceBbsDTO = bbsDTO.toBbsDtoWithBoardTypeAndGroupId(bbsEntity);
        BbsVO bbsVO = this.bbsList(sourceBbsDTO);

        bbsEntity.generateGroupId();

        bbsVO.getBbsEntities().forEach(
                a -> {
                    a.updateGroupIdAndPath(bbsEntity.getGroupId());
                    esCommonRepositoryWrapper.save(a);
                }
        );

        return bbsDTO.getId();

    }

    @Override
    public String updateBbsAndGetId(BbsDTO bbsDTO) {

        BbsEntity findBbsEntity = esCommonRepositoryWrapper.findDocById(bbsDTO.getId());

        if(findBbsEntity==null){
            throw new IllegalArgumentException("삭제된 게시글입니다.");
        }

        findBbsEntity.updateBbsEntity(bbsDTO.toBbsEntityWithUpdate());

        BbsEntity save = esCommonRepositoryWrapper.save(findBbsEntity);

        return save.getId();
    }

    @Override
    public String deleteBbsAndGetId(String deleteId) {

        BbsEntity bbsEntity = esCommonRepositoryWrapper.findDocById(deleteId);

        if(bbsEntity==null){
            throw new IllegalArgumentException("게시글이 삭제 되어서 찾을수 없습니다..");
        }
        List<BbsEntity> docs = esCommonRepositoryWrapper.findHits(SimpleQuery.wildQueryQueryFilter("path", bbsEntity.getPath()+"*")).toDocs();

        for (BbsEntity doc : docs) {
            esCommonRepositoryWrapper.deleteById(doc.getId());
        }

        return deleteId;
    }

    @Override
    public BbsVO bbsList(BbsDTO bbsDTO) {

        DocumentResultWrapper<BbsEntity> docsBySearchAfter = esCommonRepositoryWrapper.findHits(
                SimpleQuery.search(bbsDTO)
                    .andTermQueryFilter("board_type", bbsDTO.getBoardType())
                    .andTermQueryFilter("post_type", bbsDTO.getPostType())
                    .andTermQueryFilter("group_id", bbsDTO.getGroupId())
                    .andTermQueryFilter("subject_name", bbsDTO.getSubjectName())
                    .orderBy(
                            SortDTO.builder().field("group_id").sortType("desc").build(),
                            SortDTO.builder().field("path").sortType("asc").build()
                    ));

        List<BbsEntity> docs = docsBySearchAfter.toDocs();

        return BbsVO.builder().bbsEntities(docs)
                .totalHits(docsBySearchAfter.getTotalHits())
                .searchAfter(docsBySearchAfter.getLastSortValue()).build();

    }

    private BbsVO getList(SimpleQuery<SearchDocDTO> simpleQuery) {

        DocumentResultWrapper<BbsEntity> docsBySearchAfter = esCommonRepositoryWrapper.findHits(simpleQuery);

        List<BbsEntity> docs = docsBySearchAfter.toDocs();

        return BbsVO.builder().bbsEntities(docs)
                .totalHits(docsBySearchAfter.getTotalHits())
                .searchAfter(docsBySearchAfter.getLastSortValue()).build();

    }

    @Override
    public BbsVO searchBbs(BbsDTO bbsDTO) {
        return getList(
                SimpleQuery
                    .search(bbsDTO)
                    .andMatchQueryFilter("subject_name", bbsDTO.getSubjectName())
                    .orderBy(SortDTO.builder().field("group_id").sortType("desc").build(),
                            SortDTO.builder().field("path").sortType("asc").build()));
    }

    @Override
    public BbsVO sortBbs(BbsDTO bbsDTO, String field, String sortType) {
        return getList(
                SimpleQuery
                    .termQueryFilter("board_type", bbsDTO.getBoardType())
                    .andTermQueryFilter("post_type", bbsDTO.getPostType())
                    .andTermQueryFilter("group_id", bbsDTO.getGroupId())
                    .orderBy(
                        SortDTO.builder().field("group_id").sortType("desc").build(),
                        SortDTO.builder().field(field).sortType(sortType).build()));
    }

    @Override
    public List<BbsEntity> findPathById(String id) {

        BbsEntity bbsEntity = esCommonRepositoryWrapper.findDocById(id);

        if(bbsEntity==null){
            throw new IllegalArgumentException("게시글이 삭제 되어서 찾을수 없습니다..");
        }

        List<BbsEntity> docs = esCommonRepositoryWrapper.findHits(
                SimpleQuery.termQueryFilter("board_type", bbsEntity.getBoardType())
                    .andTermQueryFilter("group_id", bbsEntity.getGroupId())
                    .orderBy(
                        SortDTO.builder().field("group_id").sortType("desc").build(),
                        SortDTO.builder().field("path").sortType("asc").build()))
                .toDocs();

        if(docs.isEmpty()){
            throw new IllegalArgumentException("게시글이 삭제 되어서 찾을수 없습니다..");
        }

        return docs;
    }

    @Async
    @Override
    public void asyncViewCountUp(String id){
        BbsEntity bbsEntity = esCommonRepositoryWrapper.findDocById(id);
        bbsEntity.viewCountUp();
        esCommonRepositoryWrapper.save(bbsEntity);
    }

}

