package pwc.taxtech.atms.service.impl;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import pwc.taxtech.atms.common.AuthUserHelper;
import pwc.taxtech.atms.common.CommonConstants;
import pwc.taxtech.atms.common.CommonUtils;
import pwc.taxtech.atms.common.OperateLogType;
import pwc.taxtech.atms.common.OperationAction;
import pwc.taxtech.atms.common.OperationModule;
import pwc.taxtech.atms.common.message.ErrorMessageCN;
import pwc.taxtech.atms.dao.OrganizationStructureMapper;
import pwc.taxtech.atms.dto.*;
import pwc.taxtech.atms.entity.OrganizationStructure;
import pwc.taxtech.atms.entity.OrganizationStructureExample;
import pwc.taxtech.atms.exception.ApplicationException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;

@Service
public class OrganizationStructureServiceImpl {

    private static final Logger logger = LoggerFactory.getLogger(OrganizationStructureServiceImpl.class);

    @Autowired
    private OrganizationStructureMapper organizationStructureMapper;

    @Autowired
    private OperationLogServiceImpl operationLogService;

    @Autowired
    private OrganizationServiceImpl organizationService;

    @Autowired
    private AuthUserHelper authUserHelper;

    public List<OrganizationStructureDto> getOrganizationStructures() {
        logger.debug("OrganizationStructureService getOrganizationStructures");
        OrganizationStructureExample organizationStructureExample = new OrganizationStructureExample();
        organizationStructureExample.setOrderByClause("name ASC");
        List<OrganizationStructure> organizationStructureList = organizationStructureMapper
                .selectByExample(organizationStructureExample);
        return rotateOrganizationStructureList(organizationStructureList);
    }

    public ApiResultDto addOrganizationStructures(List<OrganizationStructureInputDto> organizationStructureDtoList) {
        logger.debug("OrganizationStructureService addOrganizationStructure");
        logger.debug("organization structure to add: {}", JSON.toJSONString(organizationStructureDtoList, true));
        ApiResultDto res = ApiResultDto.success();
        if (organizationStructureDtoList.isEmpty()) {
            return ApiResultDto.fail(CommonConstants.JSONNULLOBJECT);
        }

        for (OrganizationStructureInputDto organizationStructureDto : organizationStructureDtoList) {
            res = addOrganizationStructure(organizationStructureDto);
            if(ApiResultDto.FAILED == res.getCode()) break;
        }
        return res;
    }

    public ApiResultDto addOrganizationStructure(OrganizationStructureInputDto organizationStructureDto) {
        OrganizationStructureExample example = new OrganizationStructureExample();
        example.createCriteria().andNameEqualTo(organizationStructureDto.getName());
        if(organizationStructureMapper.countByExample(example)>0){
            return ApiResultDto.fail(ErrorMessageCN.StrctureRepeat);
        }
        OrganizationStructure organizationStructure = rotateOrganizationStructureDto(organizationStructureDto);
        organizationStructure.setId(CommonUtils.getUUID());
        organizationStructure.setCreateTime(new Date());
        organizationStructure.setUpdateTime(new Date());
        organizationStructureMapper.insert(organizationStructure);

        // operation log
        OperationLogDto operationLogDto = new OperationLogDto();
        makeUpOperationLogDto(operationLogDto);
        operationLogDto.setOperationObject(organizationStructureDto.getName());
        operationLogDto.setAction(OperationAction.New.value());
        operationLogService.addOperationLog(operationLogDto);
        return ApiResultDto.success();
    }

    private void makeUpOperationLogDto(OperationLogDto operationLogDto) {
        operationLogDto.setModule(OperationModule.BasicDataOrganizationStructure.value());
        operationLogDto.setLogType(OperateLogType.OperationLogBasicData.value());
    }

    public Integer deleteOrganizationStructure(IdModel idModel) {
        logger.debug("OrganizationStructureService deleteOrganizationStructure");
        logger.debug("organization structure to delete, id: {}", JSON.toJSONString(idModel, true));

        if (idModel == null || idModel.getId() == null) {
            throw new ApplicationException(CommonConstants.JSONNULLOBJECT);
        }

        OrganizationStructure organizationStructure = organizationStructureMapper.selectByPrimaryKey(idModel.getId());

        if (organizationStructure == null) {
            throw new ApplicationException("can't find OrganizationStructure to delete, id: " + idModel.getId());
        }
        int result = organizationStructureMapper.deleteByPrimaryKey(idModel.getId());

        // operation log
        OperationLogDto operationLogDto = new OperationLogDto();
        makeUpOperationLogDto(operationLogDto);
        operationLogDto.setAction(OperationAction.Delete.value());
        operationLogService.addOperationLog(operationLogDto);

        return result;
    }

    public ApiResultDto updateOrganizationStructures(List<OrganizationStructureInputDto> organizationStructureDtoList) {
        logger.debug("OrganizationStructureService updateOrganizationStructures");
        logger.debug("organization structure to update: {}", JSON.toJSONString(organizationStructureDtoList, true));

        if (organizationStructureDtoList.isEmpty()) {
            return ApiResultDto.fail(CommonConstants.JSONNULLOBJECT);
        }

        for (OrganizationStructureInputDto organizationStructureDto : organizationStructureDtoList) {
            //isActive changed or not
            boolean isStatusChangeOperation = false;

            if (organizationStructureDto == null || !StringUtils.hasText(organizationStructureDto.getId())) {
                return ApiResultDto.fail("primary key can't be null");
            }

            // current OrganizationStructure
            OrganizationStructure organizationStructure = organizationStructureMapper
                    .selectByPrimaryKey(organizationStructureDto.getId());
            if (organizationStructure == null) {
                return ApiResultDto.fail("can't find organizationStructure, id: " + organizationStructureDto.getId());
            }
            boolean notChange = organizationStructureDto.getIsActive().equals(organizationStructure.getIsActive())&&
                    organizationStructure.getName().equals(organizationStructureDto.getName());


            OrganizationStructureExample example = new OrganizationStructureExample();
            example.createCriteria().andIdNotEqualTo(organizationStructureDto.getId()).andNameEqualTo(organizationStructureDto.getName());
            boolean nameExist = organizationStructureMapper.countByExample(example) > 0;

            if (notChange) {
                return ApiResultDto.fail(ErrorMessageCN.OrgStructureFailed);
            }
            if (nameExist) {
                return ApiResultDto.fail(ErrorMessageCN.OrgStructureNameExist);
            }

            //copy current OrganizationStructure as tmp
            OrganizationStructure originOrganizationStructure = new OrganizationStructure();
            CommonUtils.copyProperties(organizationStructure, originOrganizationStructure);

            if (!organizationStructureDto.getIsActive().equals(organizationStructure.getIsActive())  ||
                    !org.apache.commons.lang3.StringUtils.equals( organizationStructureDto.getName(),organizationStructure.getName())) {
                isStatusChangeOperation = true;
                organizationStructure.setIsActive(organizationStructureDto.getIsActive());
                organizationStructure.setName(organizationStructureDto.getName());
                // 参照atms取消层级若有机构不能停用的限制
                /*if (!organizationStructureDto.getIsActive()&&
                        organizationService.isOrganizationStructureExists(organizationStructureDto.getId())) {
//                    continue;
//                    return false;
                    throw new ApplicationException("the organization must not contain sub-organization!");
                }*/
            }
            organizationStructure.setUpdateTime(new Date());
            organizationStructureMapper.updateByPrimaryKey(organizationStructure);

            //add operation log
            if (isStatusChangeOperation) {
                UpdateLogParams updateLogParams = new UpdateLogParams();
                updateLogParams.setOperationObject(originOrganizationStructure.getName());
                updateLogParams.setOriginalState(originOrganizationStructure);
                updateLogParams.setUpdateState(organizationStructure);
                updateLogParams.setOperationUser(authUserHelper.getCurrentAuditor().get());
                updateLogParams.setOperationModule(OperationModule.BasicDataOrganizationStructure.value());
                updateLogParams.setOperationAction(OperationAction.Update.value());
                updateLogParams.setOperateLogType(OperateLogType.OperationLogBasicData.value());
                //check how many properties being updated and the log number should be same
                operationLogService.updateDataAddLog(updateLogParams);
            }
        }

        return ApiResultDto.success();
    }


    private OrganizationStructure rotateOrganizationStructureDto(OrganizationStructureInputDto organizationStructureDto) {
        OrganizationStructure organizationStructure = new OrganizationStructure();
        organizationStructure.setId(organizationStructureDto.getId());
        organizationStructure.setName(organizationStructureDto.getName());
        organizationStructure.setIsActive(organizationStructureDto.getIsActive());
        return organizationStructure;
    }

    private List<OrganizationStructureDto> rotateOrganizationStructureList(
            List<OrganizationStructure> organizationStructureList) {
        List<OrganizationStructureDto> organizationStructureDtoList = new ArrayList<>();
        if (organizationStructureList != null && !organizationStructureList.isEmpty()) {
            for (OrganizationStructure organizationStructure : organizationStructureList) {
                organizationStructureDtoList.add(rotateOrganizationStructure(organizationStructure));
            }
        }
        return organizationStructureDtoList;
    }

    private OrganizationStructureDto rotateOrganizationStructure(OrganizationStructure organizationStructure) {
        OrganizationStructureDto organizationStructureDto = new OrganizationStructureDto();
        organizationStructureDto.setId(organizationStructure.getId());
        organizationStructureDto.setIsActive(organizationStructure.getIsActive());
        organizationStructureDto.setName(organizationStructure.getName());
        return organizationStructureDto;
    }
}