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.dao.OrganizationStructureMapper;
import pwc.taxtech.atms.dto.IdModel;
import pwc.taxtech.atms.dto.OperationLogDto;
import pwc.taxtech.atms.dto.OrganizationStructureDto;
import pwc.taxtech.atms.dto.OrganizationStructureInputDto;
import pwc.taxtech.atms.dto.UpdateLogParams;
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.Date;
import java.util.List;

@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 void addOrganizationStructures(List<OrganizationStructureInputDto> organizationStructureDtoList) {
        logger.debug("OrganizationStructureService addOrganizationStructure");
        logger.debug("organization structure to add: {}", JSON.toJSONString(organizationStructureDtoList, true));
        if (organizationStructureDtoList.isEmpty()) {
            throw new ApplicationException(CommonConstants.JSONNULLOBJECT);
        }

        for (OrganizationStructureInputDto organizationStructureDto : organizationStructureDtoList) {
            addOrganizationStructure(organizationStructureDto);
        }

    }

    public void addOrganizationStructure(OrganizationStructureInputDto organizationStructureDto) {
        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);
    }

    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 Boolean updateOrganizationStructures(List<OrganizationStructureInputDto> organizationStructureDtoList) {
        logger.debug("OrganizationStructureService updateOrganizationStructures");
        logger.debug("organization structure to update: {}", JSON.toJSONString(organizationStructureDtoList, true));

        if (organizationStructureDtoList.isEmpty()) {
            throw new ApplicationException(CommonConstants.JSONNULLOBJECT);
        }

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

            if (organizationStructureDto == null || !StringUtils.hasText(organizationStructureDto.getId())) {
                throw new ApplicationException("primary key can't be null");
            }

            // current OrganizationStructure
            OrganizationStructure organizationStructure = organizationStructureMapper
                    .selectByPrimaryKey(organizationStructureDto.getId());
            if (organizationStructure == null) {
                throw new ApplicationException("can't find organizationStructure, id: " + organizationStructureDto.getId());
            }

            //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());
                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 true;
    }


    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;
    }
}