package com.icomsys.main_vm.biz.common.system.service;


import com.icomsys.main_vm.biz.advice.excep.CustomNotFoundException;
import com.icomsys.main_vm.biz.common.common.service.SequenceService;
import com.icomsys.main_vm.biz.common.login.res.OprmngCodeRes;
import com.icomsys.main_vm.biz.common.login.res.UserVo;
import com.icomsys.main_vm.biz.common.login.service.LoginService;
import com.icomsys.main_vm.biz.common.system.vo.*;
import com.icomsys.main_vm.common.code.EnumToMap;
import com.icomsys.main_vm.common.code.enumresource.RoleResource;
import com.icomsys.main_vm.db.jpa.entity.system.TbServiceAuthGroup;
import com.icomsys.main_vm.db.jpa.entity.system.TbServiceGroupPolicy;
import com.icomsys.main_vm.db.jpa.entity.system.TbUserAuthGroup;
import com.icomsys.main_vm.db.jpa.entity.system.TbUserCust;
import com.icomsys.main_vm.db.jpa.entity.system.id.TbServiceGroupPolicyId;
import com.icomsys.main_vm.db.jpa.entity.system.id.TbUserCustId;
import com.icomsys.main_vm.db.jpa.repo.system.*;
import com.querydsl.core.QueryResults;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class SystemGroupAuthService {

    private final LoginService loginService;
    private final TbServiceAuthGroupRepo tbServiceAuthGroupRepo;
    private final TbCustPolicyRepo tbCustPolicyRepo;
    private final TbServiceGrouopRepo tbServiceGrouopRepo;
    private final SequenceService sequenceService;
    private final TbServiceGroupPolicyRepo tbServiceGroupPolicyRepo;
    private final TbBotUserRepo tbBotUserRepo;
    private final TbUserAuthGroupRepo tbUserAuthGroupRepo;
    private final TbUserCustRepo tbUserCustRepo;

    @Transactional
    public QueryResults<SystemServiceAuthGroupRes> GroupAuthList(SystemServiceAuthGroupReq dto) {
        return tbServiceAuthGroupRepo.ServiceAuthGroupListView(dto.ofUserVo(loginService.getUserVo()));
    }

    @Transactional
    public SystemServiceGroupTreeRes GroupAuthNewTree(String serviceGroup) {

//        UserVo uv = loginService.getUserVo();

        List<String> pol = tbCustPolicyRepo.findByServiceGroup(serviceGroup).stream()
                .map(f -> f.getPolicyVal()).collect(Collectors.toList());

        return SystemServiceGroupTreeRes.builder()
                .custCode(tbServiceGrouopRepo.findByServiceGroup(serviceGroup).stream().findFirst().map(e -> e.getCustCode()).get())
                .mainPolicyVal(pol)
                .mainMenu(loginService.authToAuthList(pol, 10000000))
                .policyParent(loginService.jstreeParentConvertToPolicy())
                .build();
    }

    @Transactional
    public void GroupAuthUpdateInsert(SystemServiceGroupInsertReq dto) {
        UserVo uv = loginService.getUserVo();
        String custCode = tbServiceGrouopRepo.findByServiceGroup(dto.getInsertServiceGroup()).stream().findFirst().map(e -> e.getCustCode()).get();
        TbServiceAuthGroup saveResult = tbServiceAuthGroupRepo.save(TbServiceAuthGroup.builder()
//                .authGroupSeq(sequenceService.ServiceAuthGroupSeqNext())
                .serviceGroup(dto.getInsertServiceGroup())
                .custCode(custCode)
                .policyGroupName(dto.getInsertGroupName())
                .policyGroupDesc(dto.getInsertGroupDesc())
                .useYn(dto.getSubUseYn())
                .registId(uv.getUserId())
                .registDate(LocalDateTime.now())
                .updateId(uv.getUserId())
                .updateDate(LocalDateTime.now())
                .build());

        List<TbServiceGroupPolicy> policyList = new ArrayList<>();

        for (String e : dto.getSubPolicyVal()) {
            policyList.add(TbServiceGroupPolicy.builder()
                    .authGroupSeq(saveResult.getAuthGroupSeq())
                    .serviceGroup(dto.getInsertServiceGroup())
                    .policyVal(e)
                    .custCode(custCode)
                    .build());
            if (!EnumToMap.PolicyToParentMap.get(e).equals("#")) {
                policyList.add(TbServiceGroupPolicy.builder()
                        .authGroupSeq(saveResult.getAuthGroupSeq())
                        .serviceGroup(dto.getInsertServiceGroup())
                        .policyVal(EnumToMap.PolicyToParentMap.get(e))
                        .custCode(custCode)
                        .build());
            }
        }

        tbServiceGroupPolicyRepo.saveAll(policyList);

    }

    @Transactional
    public SystemServiceAuthGroupUpdateRes GroupAuthUpdateData(Long authGroupSeq) throws CustomNotFoundException {

        TbServiceAuthGroup tsag = tbServiceAuthGroupRepo.findByAuthGroupSeq(authGroupSeq)
                .orElseThrow(() -> new CustomNotFoundException());
        List<String> pol = tbCustPolicyRepo.findByServiceGroup(tsag.getServiceGroup()).stream()
                .map(f -> f.getPolicyVal()).collect(Collectors.toList());
        List<SystemBotUserRes> userRes = new ArrayList<>();
        if (tbUserAuthGroupRepo.existsByAuthGroupSeq(authGroupSeq)) {
            userRes = tbBotUserRepo.AuthGroupUserList(authGroupSeq);
        }
        List<String> authVal = tbServiceGroupPolicyRepo.findByAuthGroupSeq(authGroupSeq).stream().map(e -> e.getPolicyVal()).collect(Collectors.toList());
        SystemServiceAuthGroupUpdateRes result = SystemServiceAuthGroupUpdateRes.builder()
                .authGroupSeq(tsag.getAuthGroupSeq())
                .serviceGroup(tsag.getServiceGroup())
                .custCode(tsag.getCustCode())
                .policyGroupName(tsag.getPolicyGroupName())
                .policyGroupDesc(tsag.getPolicyGroupDesc())
                .userList(userRes)
                .policyVal(pol)
                .authPolicyVal(authVal)
                .mainMenu(loginService.authToAuthList(pol, 10000000))
                .policyParent(loginService.jstreeParentConvertToPolicy())
                .build();
        return result;
    }


    @Transactional
    public List<SystemServiceAuthGroupUserSearchRes> GroupAuthUpdateDataUserSearch(Long authGroupSeq, SystemServiceAuthGroupUserSearchReq dto) throws CustomNotFoundException {
        List<SystemServiceAuthGroupUserSearchRes> result = new ArrayList<>();
        dto.setAuthGroupSeq(authGroupSeq);
        result = tbBotUserRepo.GroupAuthUpdateDataUserSearch(dto);
        return result;
    }

    @Transactional
    public void GroupAuthModityUpdate(SystemServiceAuthGroupModifyReq dto) throws CustomNotFoundException {
        UserVo uv = loginService.getUserVo();
        TbServiceAuthGroup tbServiceAuthGroup = tbServiceAuthGroupRepo.findByAuthGroupSeq(dto.getAuthGroupSeq()).orElseThrow(() -> new CustomNotFoundException());
        String serviceGroup = tbServiceAuthGroup.getServiceGroup();
        tbServiceAuthGroup.TbServiceAuthGroupModify(TbServiceAuthGroup.builder()
                .authGroupSeq(dto.getAuthGroupSeq())
                .serviceGroup(serviceGroup)
                .custCode(dto.getCustCode())
                .policyGroupName(dto.getPolicyGroupName())
                .policyGroupDesc(dto.getPolicyGroupDesc())
                .updateId(uv.getUserId())
                .updateDate(LocalDateTime.now())
                .build());

        //소속사용자는 다저장
        tbUserAuthGroupRepo.saveAll(dto.getTheUser().stream().map(e -> TbUserAuthGroup.builder()
                .userSeq(e)
                .authGroupSeq(dto.getAuthGroupSeq())
                .serviceGroup(serviceGroup)
                .custCode(dto.getCustCode())
                .build()).collect(Collectors.toList())
        );

        //소속제외 사용자는 다 삭제
        tbUserAuthGroupRepo.deleteAll(dto.getDelUser().stream().map(e -> TbUserAuthGroup.builder()
                .userSeq(e)
                .authGroupSeq(dto.getAuthGroupSeq())
                .serviceGroup(serviceGroup)
                .custCode(dto.getCustCode())
                .build()).collect(Collectors.toList())
        );

        //소속되면 유저커스트에도 추가해줘야함.
        tbUserCustRepo.saveAll(dto.getTheUser().stream().map(e -> TbUserCust.builder()
                .custCode(tbServiceAuthGroup.getCustCode())
                .serviceGroup(tbServiceAuthGroup.getServiceGroup())
                .userSeq(e)
                .build()).collect(Collectors.toList()));

        //소속제외되면 유저커스트에 삭제를 시켜야하는데. 다른 권한그룹에 같은 서비스그룹이 있는지 판단해야함.
        List<TbUserCustId> delUserCust = new ArrayList<>();
        for (Long e : dto.getDelUser()) {
            if (!tbUserAuthGroupRepo.existsByServiceGroupAndUserSeqAndAuthGroupSeqNot(serviceGroup, e, dto.getAuthGroupSeq())) {
                delUserCust.add(TbUserCustId.builder()
                        .custCode(tbServiceAuthGroup.getCustCode())
                        .serviceGroup(serviceGroup)
                        .userSeq(e)
                        .build());
            }
        }
        tbUserCustRepo.deleteAllById(delUserCust);

        //js트리 처리
        List<String> befor = tbServiceGroupPolicyRepo.findByAuthGroupSeq(dto.getAuthGroupSeq()).stream().map(e -> e.getPolicyVal()).collect(Collectors.toList());
        List<String> base = new ArrayList<>();
        List<String> insertVal = new ArrayList<>();
        List<TbServiceGroupPolicyId> deleteId = new ArrayList<>();
        List<String> deleteVal = new ArrayList<>();
        List<TbServiceGroupPolicy> insertId = new ArrayList<>();
        base = dto.getThePolicy();
        insertVal = dto.getThePolicy();

        for (String e : befor) {
            if (base.contains(e)) {
                insertVal.remove(e);
            } else {
                deleteVal.add(e);
                deleteId.add(TbServiceGroupPolicyId
                        .builder()
                        .authGroupSeq(dto.getAuthGroupSeq())
                        .serviceGroup(serviceGroup)
                        .policyVal(e)
                        .custCode(dto.getCustCode())
                        .build());
            }
        }
        for (String e : insertVal) {
            insertId.add(TbServiceGroupPolicy.builder()
                    .authGroupSeq(dto.getAuthGroupSeq())
                    .serviceGroup(serviceGroup)
                    .policyVal(e)
                    .custCode(dto.getCustCode())
                    .build());
            //jquery jstree 상위메뉴가없어질경우 방어로직.
            if (!EnumToMap.PolicyToParentMap.get(e).equals("#")) {
                insertId.add(TbServiceGroupPolicy.builder()
                        .authGroupSeq(dto.getAuthGroupSeq())
                        .serviceGroup(serviceGroup)
                        .policyVal(EnumToMap.PolicyToParentMap.get(e))
                        .custCode(dto.getCustCode())
                        .build());
            }
        }
        tbServiceGroupPolicyRepo.deleteByAuthGroupSeqAndPolicyValIn(dto.getAuthGroupSeq(), deleteVal);
        tbServiceGroupPolicyRepo.saveAll(insertId);
    }

    @Transactional
    public List<OprmngCodeRes> GroupInsertOpr() {
        UserVo uv = loginService.getUserVo();
        if (uv.getRoleVal().equals(RoleResource.SYSTEMADMIN.getName())) {
            return tbServiceGrouopRepo.findAll().stream().map(e -> OprmngCodeRes.builder()
                    .serviceGroup(e.getServiceGroup())
                    .serviceGroupName(e.getServiceGroupName())
                    .build()).collect(Collectors.toList());
        } else {
            return tbServiceGrouopRepo.findByServiceGroupUpper(uv.getLastUseServiceGroup()).stream().map(e -> OprmngCodeRes.builder()
                    .serviceGroup(e.getServiceGroup())
                    .serviceGroupName(e.getServiceGroupName())
                    .build()).collect(Collectors.toList());
        }
    }

}
