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

import com.google.gson.Gson;
import com.icomsys.main_vm.biz.advice.excep.CustomNotFoundException;
import com.icomsys.main_vm.biz.common.common.service.RestTemplateService;
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.MenuVo;
import com.icomsys.main_vm.common.code.enumresource.PolicyResource;
import com.icomsys.main_vm.db.jpa.entity.conversation.TbProjectSetting;
import com.icomsys.main_vm.db.jpa.entity.system.*;
import com.icomsys.main_vm.db.jpa.entity.system.id.TbCustPolicyId;
import com.icomsys.main_vm.db.jpa.repo.conversation.TbProjectSettingRepo;
import com.icomsys.main_vm.db.jpa.repo.system.*;
import com.querydsl.core.QueryResults;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.stream.Streams;
import org.apache.el.lang.FunctionMapperImpl;
import org.apache.http.conn.HttpHostConnectException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class SystemCustService {
    private final LoginService loginService;
    private final TbIcsCustInfoRepo tbIcsCustInfoRepo;
    private final TbServiceGrouopRepo tbServiceGrouopRepo;
    private final TbIcsOprMngInfoRepo tbIcsOprMngInfoRepo;
    private final TbCustPolicyRepo tbCustPolicyRepo;
    private final TbUserCustRepo tbUserCustRepo;
    private final TbServiceGroupPolicyRepo tbServiceGroupPolicyRepo;
    private final RestTemplateService restTemplateService;
    private final TbProjectSettingRepo tbProjectSettingRepo;
    @Value("${feign.url.nlp}")
    private String wisepath;


    public QueryResults<SystemIcsCustInfoRes> CustList(SystemIcsCustInfoReq dto) {
//        return tbIcsCustInfoRepo.findAll().stream().map(e -> new SystemIcsCustInfoRes().toIcsCustInfoRes(e)).collect(Collectors.toList());
        return tbIcsCustInfoRepo.SelectCustInfo(dto);
    }

    public List<MenuVo> CustPolicyBase() {
        List<MenuVo> result = loginService.authToAuthList(
                PolicyResource.ALL.stream().map(e -> e.getName()).collect(Collectors.toList()), 10000000);
        return result;
    }

    public String CustDoubleCheck(String custCode) {
        String result = "N";

        if (tbIcsCustInfoRepo.existsByCustCode(custCode)) {
            return result;
        } else {
            return "Y";
        }
    }

    public String CustServiceDoubleCheck(String serviceGroup) {
        String result = "N";
        if (tbServiceGrouopRepo.existsByServiceGroup(serviceGroup)) {
            return result;
        } else {
            return "Y";
        }
    }

    @Transactional
    public void CustSave(SystemCustSaveReq dto) throws CustomNotFoundException {
        log.info("CustSave dto - {}", new Gson().toJson(dto));
        UserVo uv = loginService.getUserVo();
        tbIcsCustInfoRepo.save(dto.otCust(uv));
        tbServiceGrouopRepo.saveAll(dto.toServiceGroup(uv));
        tbIcsOprMngInfoRepo.saveAll(dto.toOpr(uv));
        tbCustPolicyRepo.saveAll(dto.toPolicy());
        tbUserCustRepo.saveAll(dto.toSystemAdmin(uv));
//        tbProjectSettingRepo.saveAll(WisenutInsert(dto.toWisenutInsert()));
    }

    public List<TbProjectSetting> WisenutInsert(List<WisenutInsertReqVo> dto) throws CustomNotFoundException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        List<TbProjectSetting> projectSettins = new ArrayList<>();
        UserVo uv = loginService.getUserVo();
        log.info("===================WisenutInsert===============");
//        log.info("data ===== {}", new Gson().toJson(dto));

        ResponseEntity callRes = null;
        for (WisenutInsertReqVo e : dto) {
                callRes = Optional.of(restTemplateService
                                .PostCall(UriComponentsBuilder.newInstance().uri(URI.create(wisepath + "/proj/insert"))
                                        .build(), headers, new Gson().toJson(e)))
                        .orElseThrow(() -> new CustomNotFoundException("Wisenut 연결에 실패하였습니다."))
                ;
            if (callRes.getStatusCode().equals(HttpStatus.OK)) {
                WisenutInsertResVo callVo = new Gson().fromJson(callRes.getBody().toString(), WisenutInsertResVo.class);
                projectSettins.add(TbProjectSetting.builder()
                        .ifMappingId(callVo.getProjectId())
                        .oprMngCode(e.getName())
                        .similarityCriteria(e.getSimThreshold())
                        .classCriteria(e.getConfidentThreshold())
                        .classResultCriteria(e.getConfidentThresholdGap())
                        .registId(uv.getUserId())
                        .updateId(uv.getUserId())
                        .registDate(LocalDateTime.now())
                        .updateDate(LocalDateTime.now())
                        .build());

            } else {
                throw new CustomNotFoundException("Wisenut 연결에 실패하였습니다.");
            }
        }

            return projectSettins;
    }

    @Transactional(readOnly = true)
    public SystemCustSelectRes custSelect(String custCode) throws CustomNotFoundException {

        TbIcsCustInfo custResult = tbIcsCustInfoRepo.findById(custCode)
                .orElseThrow(() -> new CustomNotFoundException());

        Map<String, List<String>> polMap = new HashMap<>();
        List<SystemCustSaveSubReq> systemCustSaveSubReqs = tbServiceGrouopRepo.findByCustCodeAndServiceType(custCode, "M").stream().map(e -> {
                            polMap.put(e.getServiceGroup(), new ArrayList<>());
                            return SystemCustSaveSubReq.builder()
                                    .addServiceGroupName(e.getServiceGroupName())
                                    .addServiceGroup(e.getServiceGroup())
                                    .addOprMngCode(custCode + "_RCP_" + e.getServiceGroup())
                                    .addServiceGroupDesc(e.getServiceGroupDesc())
                                    .addUseYn(e.getUseYn())
                                    .build();
                        }
                ).distinct()
                .collect(Collectors.toList());

        tbCustPolicyRepo.findByServiceGroupIn(polMap.keySet().stream().collect(Collectors.toList()))
                .stream().map(e -> {
                            List<String> addpol = polMap.get(e.getServiceGroup());
                            addpol.add(e.getPolicyVal());
                            polMap.put(e.getServiceGroup(), addpol);
                            return false;
                        }
                ).collect(Collectors.toList());

        SystemCustSelectRes result = SystemCustSelectRes.builder()
                .custName(custResult.getCustName())
                .custCode(custResult.getCustCode())
                .custUseYn(custResult.getUseYn())
                .addServiceAuthList(systemCustSaveSubReqs)
                .servicePol(polMap)
                .mainMenu(loginService.authToAuthList(PolicyResource.ALL.stream().map(e -> e.getName()).collect(Collectors.toList()), 10000000))
                .policyParent(loginService.jstreeParentConvertToPolicy())
                .build();
        return result;
    }

    @Transactional
    public void CustModify(SystemCustSaveReq dto) throws CustomNotFoundException {
        log.info("CustSave dto - {}", new Gson().toJson(dto));

        UserVo uv = loginService.getUserVo();
        TbIcsCustInfo custResult = tbIcsCustInfoRepo.findById(dto.getCustCode())
                .orElseThrow(() -> new CustomNotFoundException())
                .UpdateCust(dto, loginService.getUserVo());
        Map<String, List<TbCustPolicy>> custPolBase = tbCustPolicyRepo.findByServiceGroupIn(tbServiceGrouopRepo
                        .findByCustCodeAndServiceType(dto.getCustCode(), "M")
                        .stream()
                        .map(e -> e.getServiceGroup())
                        .collect(Collectors.toList()))
                .stream()
                .collect(Collectors.groupingBy(TbCustPolicy::getServiceGroup));

        List<TbServiceGrouop> saveService = new ArrayList<>();
        List<TbIcsOprMngInfo> saveOpr = new ArrayList<>();

        List<TbCustPolicy> insertPol = new ArrayList<>();
        Map<List<String>, List<String>> delUpperVal = new HashMap<>();
        for (SystemCustSaveSubReq e : dto.getAddServiceAuthList()) {
            List<String> delVal = new ArrayList<>();
            String serviceGroup = e.getAddServiceGroup();
            List<TbCustPolicy> beforPol = custPolBase.get(serviceGroup);
            List<String> insertVal = e.getAuthList();
            log.info("modiiiiiiii - {}", new Gson().toJson(e));
            //수정할 데이터
            if (custPolBase.keySet().stream().collect(Collectors.toList()).contains(e.getAddServiceGroup())) {
                TbServiceGrouop tsg = tbServiceGrouopRepo.findByServiceGroup(serviceGroup)
                        .orElseThrow(() -> new CustomNotFoundException())
                        .toUpdateMainService(e, uv);
                for (TbCustPolicy f : beforPol) {
                    if (e.getAuthList().contains(f.getPolicyVal())) {
                        insertVal.remove(f.getPolicyVal());
                    } else {
                        delVal.add(f.getPolicyVal());
                    }
                }
                if (delVal.size() > 0) {
                    delUpperVal.put(
                            tbServiceGrouopRepo.findByServiceGroupUpper(e.getAddServiceGroup())
                                    .stream().map(g -> g.getServiceGroup())
                                    .distinct()
                                    .collect(Collectors.toList()), delVal);
                }
                for (String h : insertVal) {
                    insertPol.add(TbCustPolicy.builder()
                            .custCode(dto.getCustCode())
                            .serviceGroup(e.getAddServiceGroup())
                            .policyVal(h)
                            .build());
                }
            }
            //저장할 데이터
            else {
                for (String h : e.getAuthList()) {
                    insertPol.add(TbCustPolicy.builder()
                            .custCode(custResult.getCustCode())
                            .serviceGroup(e.getAddServiceGroup())
                            .policyVal(h)
                            .build());
                }
                log.info(".getAddUseYn()  --- " + e.getAddUseYn());
                saveService.add(e.toService(custResult.getCustCode(), uv));
//                saveService.add(e.toService(custResult.getCustCode(), uv));
                saveOpr.add(e.toOprmngCode(custResult.getCustCode(), uv));
            }
        }

        for (List<String> e : delUpperVal.keySet().stream().collect(Collectors.toList())) {
            tbCustPolicyRepo.deleteByServiceGroupInAndPolicyValIn(e, delUpperVal.get(e));
            tbServiceGroupPolicyRepo.deleteByServiceGroupInAndPolicyValIn(e, delUpperVal.get(e));
        }
        log.info(".saveService()  --- {}" , new Gson().toJson(saveService));
        tbServiceGrouopRepo.saveAll(saveService);
        tbCustPolicyRepo.saveAll(insertPol);
        tbIcsOprMngInfoRepo.saveAll(saveOpr);
        //프로젝트세팅 테이블이 저장되어야 해야된다해서 나중에 추가된 코드
        //1차에선 데이터를 DB에서 SQL로 인서트했다고함.
        if (saveService.size() > 0) {
            List<WisenutInsertReqVo> saveProject = new ArrayList<>();
            for (TbServiceGrouop e : saveService) {
                saveProject.add(WisenutInsertReqVo.builder()
                        .oprMngCode(e.getCustCode() + "_RCP_" + e.getServiceGroup())
                        .serviceGroupDesc(e.getServiceGroupDesc())
                        .build());
            }
//            tbProjectSettingRepo.saveAll(WisenutInsert(saveProject));
        }

    }


}
