package pwc.taxtech.atms.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

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 com.alibaba.fastjson.JSON;

import pwc.taxtech.atms.exception.ApplicationException;
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.entitiy.OrganizationStructure;
import pwc.taxtech.atms.entitiy.OrganizationStructureExample;
import pwc.taxtech.atms.service.OperationLogService;
import pwc.taxtech.atms.service.OrganizationService;
import pwc.taxtech.atms.service.OrganizationStructureService;

/** @see PwC.Tax.Tech.Atms.Admin.Application.Services.Impl.OrganizationStructureService.cs */
@Service
public class OrganizationStructureServiceImpl implements OrganizationStructureService {

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

    @Autowired
    private OrganizationStructureMapper organizationStructureMapper;

    @Autowired
    private OperationLogService operationLogService;

    @Autowired
    private OrganizationService organizationService;

    @Autowired
    private AuthUserHelper authUserHelper;

    @Override
    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);
    }

    @Override
    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());
    }

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

    @Override
    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() != organizationStructure.getIsActive() || organizationStructureDto.getName() != organizationStructure.getName()) {
                isStatusChangeOperation = true;
                organizationStructure.setIsActive(organizationStructureDto.getIsActive());
                organizationStructure.setName(organizationStructureDto.getName());
                if (!organizationStructureDto.getIsActive()
                        && organizationService.isOrganizationStructureExists(organizationStructureDto.getID())) {
                    continue;
                }
            }
            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());
                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;
    }
}