package pwc.taxtech.atms.service.impl;

import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import pwc.taxtech.atms.common.CommonUtils;
import pwc.taxtech.atms.common.OperateLogType;
import pwc.taxtech.atms.dao.*;
import pwc.taxtech.atms.dpo.ProjectDisplayDto;
import pwc.taxtech.atms.dto.FieldsMapper;
import pwc.taxtech.atms.dto.ProjectClientDto;
import pwc.taxtech.atms.dto.ServiceTypeDto;
import pwc.taxtech.atms.dto.TemplateGroupDto;
import pwc.taxtech.atms.dto.taxadmin.AddProjectResult;
import pwc.taxtech.atms.dto.taxadmin.ProjectYearParam;
import pwc.taxtech.atms.entity.*;
import pwc.taxtech.atms.entity.ProjectClientExample.Criteria;

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

@Service
public class ProjectServiceImpl {
    /**
     */
    private static final int FIRST_OR_DEFAULT = 0;
    private static Logger LOGGER = LoggerFactory.getLogger(ProjectServiceImpl.class);

    @Autowired
    private ServiceTypeMapper serviceTypeMapper;

    @Autowired
    private TemplateGroupMapper templateGroupMapper;

    @Autowired
    private ProjectClientMapper projectClientMapper;

    @Autowired
    private ProjectMapper projectMapper;

    @Autowired
    private OrganizationServiceTemplateGroupMapper organizationServiceTemplateGroupMapper;

    @Autowired
    private ProjectServiceTypeMapper projectServiceTypeMapper;


    @Value("${jdbc_admin_db}")
    private String adminDbName;

    private final OperateLogType LOG_TYPE = OperateLogType.OperationLogProject;
    private static final Logger logger = LoggerFactory.getLogger(ProjectServiceImpl.class);

    public List<ServiceTypeDto> getServiceList() {

        ServiceTypeExample serviceTypeExample = new ServiceTypeExample();
        serviceTypeExample.createCriteria().andIsActiveEqualTo(true);

        logger.debug("获取ServiceType Start");
        List<ServiceType> serviceTypeList = serviceTypeMapper.selectByExample(serviceTypeExample);

        logger.debug("获取TemplateGroup Start");
        List<TemplateGroup> templateGroupList = templateGroupMapper.selectByExample(new TemplateGroupExample());

        List<ServiceTypeDto> serviceTypeDtoList = serviceTypeList.stream().map(this::rotateServiceType)
                .collect(Collectors.toList());
        List<TemplateGroupDto> templateGroupDtoList = templateGroupList.stream().map(this::rotateTemplateGroup)
                .collect(Collectors.toList());

        for (ServiceTypeDto serviceTypeDto : serviceTypeDtoList) {
            // serviceType 和 templateGroup是上下层级关系,遍历ServiceTypeDto,
            // 并将每个ServiceType下级关联的templateGroup填充进去
            serviceTypeDto.setTemplateGroupList(templateGroupDtoList.stream()
                    .filter(templateGroupDto -> (serviceTypeDto.getId().equals(templateGroupDto.getServiceTypeId())))
                    .collect(Collectors.toList()));
        }
        return serviceTypeDtoList;
    }

    public List<ProjectClientDto> getProjectClientList() {
        logger.debug("获取ProjectClient Start");
        ProjectClientExample example = new ProjectClientExample();
        example.setOrderByClause("CREATE_TIME desc");
        Criteria criteria = example.createCriteria();
        criteria.andIsActiveEqualTo(true);
        List<ProjectClient> projectClientList = projectClientMapper.selectByExample(example);
        return projectClientList.stream().map(this::rotateProjectClient).collect(Collectors.toList());
    }

    /**
     * @TODO: this dmeo only for running,should change to query data from db (neo)
     */
    public ProjectDisplayDto getProjectById(String projectId) {
        Project project = projectMapper.selectByPrimaryKey(projectId);
        ProjectDisplayDto dto = new ProjectDisplayDto();
        dto.extractFromProject(project);

        List<ProjectStatusManage> manageStatus = projectMapper.getStatusesByProjectId(projectId);
        Map<Integer, Integer> dic = new HashMap<>();
        manageStatus.forEach(m -> {
            dic.put(m.getPeriodId(), m.getStatus());
        });

        dto.setProjectStatusList(dic);
        return dto;
    }

    public List<ProjectDisplayDto> getAllProjectList(String orgId, String serviceId, Integer projectYear) {
        List<ProjectDisplayDto> pList = new ArrayList<>();
        List<ProjectDisplayDto> data = projectMapper.getProjectList(orgId, serviceId, projectYear);
        //获取每个项目的项目状态
        data.forEach(p -> {
            List<ProjectStatusManage> manageStatus = projectMapper.getStatusesByProjectId(p.getId());
            Map<Integer, Integer> dic = new HashMap<>();
            manageStatus.forEach(m -> {
                dic.put(m.getPeriodId(), m.getStatus());
            });

            p.setProjectStatusList(dic);
        });
        //添加账套中需要创建的项目
        List<ProjectDisplayDto> orgList = getProjectFromEnterpriseAccountSetOrg(data, orgId, serviceId, projectYear);
        //修改账套时间变化引起的已建项目的月份更改
        List<ProjectDisplayDto> removeList = new ArrayList<>();

        orgList.forEach(p -> {
            List<ProjectDisplayDto> fixList = data.stream()
                    .filter(x -> x.getOrganizationId().equals(p.getOrganizationId())
                            && x.getEnterpriseAccountSetId().equals(p.getEnterpriseAccountSetId())
                            && x.getServiceTypeId().equals(p.getServiceTypeId())
                            && x.getYear().equals(p.getYear())
                            && (!x.getStartPeriod().equals(p.getStartPeriod()) || x.getEndPeriod().equals(p.getEndPeriod())))
                    .collect(Collectors.toList());

            if (fixList != null && !fixList.isEmpty()) {
                ProjectDisplayDto pddFirst = fixList.get(FIRST_OR_DEFAULT);

                //修改数据库对应项目起始月
                Project project = new Project();
                project.setId(pddFirst.getId());
                project.setStartPeriod(p.getStartPeriod());
                project.setEndPeriod(p.getEndPeriod());

                projectMapper.updateByPrimaryKeySelective(project);

                //修改已取出数据中项目起始月
                pddFirst.setStartPeriod(p.getStartPeriod());
                pddFirst.setEndPeriod(p.getEndPeriod());
                //删除该条为创建项目记录
                removeList.add(p);
            }
        });

        removeList.forEach(r -> {
            orgList.remove(r);
        });

        pList.addAll(data);
        pList.addAll(orgList);
        return pList;
    }

    public AddProjectResult addProject(ProjectDisplayDto projectDto, String userName) {
        projectDto.setActive(true);
        projectDto.setRuleType(1);

        Project project = new Project();
        try {
            FieldsMapper.map(projectDto, project);
        } catch (Exception e) {
            LOGGER.warn("map project dto to project may be some error");
            e.printStackTrace();
        }

        try {
            project.setId(UUID.randomUUID().toString());

            OrganizationServiceTemplateGroupExample example = new OrganizationServiceTemplateGroupExample();
            example.createCriteria().andOrganizationIdEqualTo(projectDto.getOrganizationId());
            organizationServiceTemplateGroupMapper.selectByExample(example).forEach(ostg -> {
                ProjectServiceType pst = new ProjectServiceType();
                pst.setId(UUID.randomUUID().toString());
                pst.setProjectId(project.getId());
                pst.setServiceTypeId(ostg.getServiceTypeId());
                pst.setTemplateGroupId(ostg.getTemplateGroupId());

                projectServiceTypeMapper.insert(pst);
//                operationLogService.addDataAddLog(pst, OperationModule.ProjectServiceType, userName,//TODO add peration log
//                        Message.Project.AddProjectServiceType, pst.getId(), projectDto.getName(), LOG_TYPE);
            });

            project.setUpdateTime(new Date());
            projectMapper.insert(project);
//            operationLogService.addDataAddLog(project, OperationModule.Project, userName, Message.Project.AddProject,
//                    project.getId(), projectDto.getName(), LOG_TYPE);

            AddProjectResult addProjectResult = new AddProjectResult();
            addProjectResult.setResult(true);
            addProjectResult.setDbName(project.getDbName());
            addProjectResult.setProjectId(project.getId());
            return addProjectResult;
        } catch (Exception e) {
//            operationLogService.addDataAddLog(null, OperationModule.Project, userName,
//                    Message.Project.AddProjectFail, e.getMessage(), projectDto.getName(), LOG_TYPE);

            AddProjectResult addProjectResult = new AddProjectResult();
            addProjectResult.setResult(false);
            addProjectResult.setResultMsg(e.getMessage());
            return addProjectResult;
        }

    }

    public Map<Integer, Integer> getProjectAllStatus(String projectId) {
        List<ProjectStatusManage> psmList = projectMapper.selectProjectAllStatus(projectId);
        Map<Integer, Integer> result = new HashMap<>(psmList.size());
        psmList.forEach(m -> {
            result.put(m.getPeriodId(), m.getStatus());
        });

        return result;
    }


    private List<ProjectDisplayDto> getProjectFromEnterpriseAccountSetOrg(List<ProjectDisplayDto> pList, String orgId,
                                                                          String serviceId, Integer projectYear) {
        List<ProjectDisplayDto> list = new ArrayList<>();
        List<ProjectDisplayDto> data = projectMapper.getProjectFromEnterpriseAccountSetOrg(orgId, serviceId);
        Map<String, ProjectDisplayDto> groupMap = new HashMap<>();

        if (data != null && data.size() > 0) {
            //去除project表已经存在的项目 g
            data.forEach(dto -> {
                getOrgProjectYears(dto.getEffectiveDate(), dto.getExpiredDate()).forEach(yDto -> {
                    ProjectDisplayDto orgDto = new ProjectDisplayDto();
                    orgDto.setCreateTime(dto.getCreateTime());
                    orgDto.setActive(dto.getActive());
                    orgDto.setOrganizationId(dto.getOrganizationId());
                    orgDto.setCode(dto.getCode());
                    orgDto.setId(dto.getId());
                    orgDto.setIndustryId(dto.getIndustryId());
                    orgDto.setRegionId(dto.getRegionId());
                    orgDto.setRuleType(dto.getRuleType());
                    orgDto.setServiceTypeId(dto.getServiceTypeId());
                    orgDto.setUpDate(dto.getUpDate());
                    orgDto.setServiceTypeName(dto.getServiceTypeName());
                    orgDto.setOrganizationName(dto.getOrganizationName());
                    orgDto.setIndustryName(dto.getIndustryName());
                    orgDto.setTemplateGroupId(dto.getTemplateGroupId());
                    orgDto.setTemplateGroupName(dto.getTemplateGroupName());
                    orgDto.setClientCode(dto.getClientCode());
                    orgDto.setDbName(dto.getDbName());
                    orgDto.setHaveCreateProject(dto.getHaveCreateProject());
                    orgDto.setEnterpriseAccountSetId(dto.getEnterpriseAccountSetId());
                    orgDto.setEffectiveDate(dto.getEffectiveDate());
                    orgDto.setExpiredDate(dto.getExpiredDate());
                    orgDto.setRegionName(dto.getRegionName());
                    orgDto.setYear(yDto.year);
                    orgDto.setStartPeriod(yDto.startMonth);
                    orgDto.setEndPeriod(yDto.endMonth);
                    orgDto.setName(orgDto.getOrganizationName() + "(" + orgDto.getYear() + ")");

                    //判断是否已经创建过该项目
                    //对于VAT,只创建今年及之后年份的项目(2018.1.2经Michelle确定,已放开vat限制)
                    //if ((projectList != null && projectList.Count > 0) || (int.Parse(orgDto.ServiceTypeId) == 2 && orgDto.Year < DateTime.Now.Year))
                    if (pList.stream().noneMatch(t -> t.getYear().equals(orgDto.getYear())
                            && t.getServiceTypeId().equals(orgDto.getServiceTypeId())
                            && t.getOrganizationId().equals(orgDto.getOrganizationId())
                            && t.getEnterpriseAccountSetId().equals(orgDto.getEnterpriseAccountSetId())
                            && t.getStartPeriod().equals(orgDto.getStartPeriod())
                            && t.getEndPeriod().equals(orgDto.getEndPeriod()))) {
                        list.add(orgDto);
                    }
                });
            });

            if (!list.isEmpty()) {
                list.forEach(m -> {
                    StringBuilder groupKeyBulder = new StringBuilder(m.getOrganizationId()).append("_")
                            .append(m.getEnterpriseAccountSetId()).append("_")
                            .append(m.getYear()).append("_")
                            .append(m.getServiceTypeId()).append("_")
                            .append(m.getStartPeriod()).append("_")
                            .append(m.getEndPeriod());

                    groupMap.putIfAbsent(groupKeyBulder.toString(), m);
                });
            }
        }

        return Lists.newArrayList(groupMap.values());

    }

    private List<ProjectYearParam> getOrgProjectYears(Date startDate, Date endDate) {
        List<ProjectYearParam> pList = new ArrayList<>();

        ProjectYearParam pYear = new ProjectYearParam();
        Calendar nowCal = Calendar.getInstance();
        Date nowDate = new Date();
        nowCal.setTime(nowDate);

        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);

        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate);
        if (startDate == null && endDate == null) {
            pYear.year = nowCal.get(Calendar.YEAR);
            pYear.startMonth = 1;
            pYear.endMonth = 12;

            pList.add(pYear);
        } else if (startDate == null && endDate != null && endCal.after(nowCal)) {
            pList.addAll(getProjectYears(nowDate, endDate));
        } else if (startDate != null && endDate == null && startCal.before(nowCal)) {
            pList.addAll(getProjectYears(startDate, nowDate));
        } else if (startDate != null && endDate != null && startCal.get(Calendar.YEAR) <= endCal.get(Calendar.YEAR)) {
            pList.addAll(getProjectYears(startDate, endDate));
        }
        return pList;
    }

    private List<ProjectYearParam> getProjectYears(Date startDate, Date endDate) {
        List<ProjectYearParam> pList = new ArrayList<>();

        Calendar start = Calendar.getInstance();
        start.setTime(startDate);
        int startYear = start.get(Calendar.YEAR);

        Calendar end = Calendar.getInstance();
        end.setTime(endDate);
        int endYear = end.get(Calendar.YEAR);

        if (startDate != null && endDate != null && startYear <= endYear) {
            for (int i = startYear; i <= endYear; i++) {
                ProjectYearParam pYear = new ProjectYearParam();
                pYear.year = i;
                if (startYear == endYear) {
                    pYear.startMonth = start.get(Calendar.MONTH)+1;
                    pYear.endMonth = end.get(Calendar.MONTH)+1;
                } else if (i == startYear && i < endYear) {
                    pYear.startMonth = start.get(Calendar.MONTH)+1;
                    pYear.endMonth = 12;
                } else if (i > startYear && i < endYear) {
                    pYear.startMonth = 1;
                    pYear.endMonth = 12;
                } else if (i > startYear && i == endYear) {
                    pYear.startMonth = 1;
                    pYear.endMonth = end.get(Calendar.MONTH)+1;
                }
                pList.add(pYear);
            }
        }
        return pList;
    }

    /**
     * @TODO: change the return part after copyProperties function updated
     */
    private ServiceTypeDto rotateServiceType(ServiceType serviceType) {
        ServiceTypeDto serviceTypeDto = new ServiceTypeDto();
        return CommonUtils.copyProperties(serviceType, serviceTypeDto);
    }

    /**
     * @TODO: change the return part after copyProperties function updated
     */
    private TemplateGroupDto rotateTemplateGroup(TemplateGroup templateGroup) {
        TemplateGroupDto templateGroupDto = new TemplateGroupDto();
        return CommonUtils.copyProperties(templateGroup, templateGroupDto);
    }

    /**
     * @TODO: change the return part after copyProperties function updated
     */
    private ProjectClientDto rotateProjectClient(ProjectClient projectClient) {
        ProjectClientDto projectClientDto = new ProjectClientDto();
        return CommonUtils.copyProperties(projectClient, projectClientDto);
    }

}