package pwc.taxtech.atms.service.impl;

import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pwc.taxtech.atms.common.CommonUtils;
import pwc.taxtech.atms.constant.ActiveStatus;
import pwc.taxtech.atms.constant.StandAccountConstant;
import pwc.taxtech.atms.constant.enums.StdAccountEnum;
import pwc.taxtech.atms.dao.*;
import pwc.taxtech.atms.dto.stdaccount.StandardAccountDto;
import pwc.taxtech.atms.dto.stdaccount.StdAccountFancyTreeDto;
import pwc.taxtech.atms.entity.*;
import pwc.taxtech.atms.exception.ServiceException;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class StdAccountServiceImpl extends BaseService {

    @Autowired
    private StandardAccountMapper standardAccountMapper;

    @Autowired
    private OrganizationMapper organizationMapper;
    @Autowired
    private StandardAccountDao standardAccountDao;
    @Autowired
    private AccountMappingMapper accountMappingMapper;
    @Autowired
    private EnterpriseAccountMapper enterpriseAccountMapper;

    public List<StdAccountFancyTreeDto> getStdAccountHierarchy(String orgId) throws ServiceException {
        List<StdAccountFancyTreeDto> resultList;
        try {
            if (StringUtils.isBlank(orgId)) {
                return Collections.emptyList();
            }
            Organization organization = organizationMapper.selectByPrimaryKey(orgId);
            if (null == organization) {
                return Collections.emptyList();
            }
            String industryId = organization.getIndustryId();
            resultList = new ArrayList<>();
            if (StringUtils.isNotBlank(industryId)) {
                List<StdAccountFancyTreeDto> dtoList = Lists.newArrayList();
                StandardAccountExample example = new StandardAccountExample();
                StandardAccountExample.Criteria criteria = example.createCriteria();
                criteria.andIndustryIdEqualTo(industryId).andRuleTypeEqualTo(2).andIsActiveEqualTo(true);
                List<StandardAccount> standardAccountList = standardAccountMapper.selectByExample(example);
                List<StandardAccount> topList = standardAccountList.stream().filter(x -> StringUtils.isBlank(x.getParentCode()))
                        .collect(Collectors.toList());
                for (StandardAccount topNode : topList) {
                    StdAccountFancyTreeDto dto = rotateToDto(topNode);
                    getSubStdAccount(dto, standardAccountList);
                    dtoList.add(dto);
                }
                Map<String, List<StdAccountFancyTreeDto>> map = dtoList.stream().collect(Collectors.groupingBy(x -> x.getAcctProp()));
                for (Map.Entry<String, List<StdAccountFancyTreeDto>> entry : map.entrySet()) {
                    StdAccountFancyTreeDto root = new StdAccountFancyTreeDto();
                    root.setExpanded(false);
                    root.setHasChildren(CollectionUtils.isNotEmpty(entry.getValue()));
                    root.setTitle(StdAccountEnum.AcctProp.MAPPING.get(entry.getKey()));
                    List<StdAccountFancyTreeDto> temp = entry.getValue();
                    root.setChildren(temp.stream().sorted(Comparator.comparing(StdAccountFancyTreeDto::getCode)).collect(Collectors.toList()));
//                    root.setChildren(entry.getValue());
                    resultList.add(root);
                }
            }
        } catch (Exception e) {
            throw new ServiceException("getStdAccountHierarchy error.", e);
        }
        return resultList;
    }

    public List<StandardAccountDto> GetStdAccountLinkEtsAccount(String orgId, String accountSetId) {
        if (StringUtils.isBlank(orgId)) {
            return Collections.emptyList();
        }
        //todo
        Organization organization = organizationMapper.selectByPrimaryKey(orgId);
        if (organization == null) {
            return Collections.emptyList();
        }
        String industryId = StringUtils.defaultString(organization.getIndustryId(), "0");
        AccountMappingExample example = new AccountMappingExample();
        example.createCriteria().andOrganizationIdEqualTo(orgId).andEnterpriseAccountSetIdEqualTo(accountSetId).andIndustryIdEqualTo(industryId);
        List<AccountMapping> mappingList = accountMappingMapper.selectByExample(example);

        if (CollectionUtils.isEmpty(mappingList)) {
            return Collections.emptyList();
        }

        StandardAccountExample stdExample = new StandardAccountExample();
        stdExample.createCriteria().andCodeIn(mappingList.stream().map(AccountMapping::getStandardAccountCode).collect(Collectors.toList()));
        List<StandardAccount> stdList = standardAccountMapper.selectByExample(stdExample);

        EnterpriseAccountExample etExample = new EnterpriseAccountExample();
        etExample.createCriteria().andEnterpriseAccountSetIdEqualTo(accountSetId).andCodeIn(mappingList.stream()
                .map(AccountMapping::getEnterpriseAccountCode).collect(Collectors.toList()));
        List<EnterpriseAccount> etList = enterpriseAccountMapper.selectByExample(etExample);

        Map<String, AccountMapping> mappingMap = mappingList.stream().collect(Collectors.toMap(AccountMapping::getStandardAccountCode, a -> a, (k1, k2) -> k1));
        Map<String, EnterpriseAccount> etMappingMap = etList.stream().collect(Collectors.toMap(EnterpriseAccount::getCode, a -> a, (k1, k2) -> k1));

        return stdList.stream().map(o -> {
            StandardAccountDto dto = new StandardAccountDto();
            beanUtil.copyProperties(o, dto);
            String etsCode = mappingMap.get(o.getCode()).getEnterpriseAccountCode();
            dto.setEtsCode(etsCode);
            dto.setEtsName(etMappingMap.get(etsCode).getName());
            return dto;
        }).collect(Collectors.toList());
    }

    public List<StandardAccountDto> getStdAccountByIndustry(String industryId) {
        StandardAccountExample example = new StandardAccountExample();
        example.createCriteria().andRuleTypeEqualTo((int) StandAccountConstant.TWO).andIsActiveEqualTo(ActiveStatus.Active).andIndustryIdEqualTo(industryId);

        example.setOrderByClause("Code");

        List<StandardAccount> items = standardAccountMapper.selectByExample(example);
        List<StandardAccountDto> dtos = new ArrayList<>();

        for (StandardAccount item : items) {
            StandardAccountDto dto = new StandardAccountDto();
            CommonUtils.copyProperties(item, dto);
            dto.setTitle(item.getCode() + "-" + (item.getDirection().equals(1) ? "借" : "贷") + "-" + item.getName());
            dtos.add(dto);
        }
        return dtos;
    }

    private void getSubStdAccount(StdAccountFancyTreeDto node, List<StandardAccount> allList) {
        List<StdAccountFancyTreeDto> childList = allList.stream().filter(x -> StringUtils.equals(node.getCode(), x.getParentCode()))
                .map(this::rotateToDto).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(childList)) {
            node.setChildren(childList);
            node.setHasChildren(true);
            for (StdAccountFancyTreeDto tmp : childList) {
                getSubStdAccount(tmp, allList);
            }
        }
    }

    private StdAccountFancyTreeDto rotateToDto(StandardAccount account) {
        StdAccountFancyTreeDto dto = new StdAccountFancyTreeDto();
        dto.setExpanded(false);
        dto.setHasChildren(false);
        CommonUtils.copyProperties(account, dto);
        dto.setTitle(dto.getCode() + "-" + (StringUtils.equals(dto.getDirection(), "1") ? "借" : "贷") + "-" + dto.getName());
        return dto;
    }
}