package com.ningdatech.carapi.sys.service.impl;

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

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.basic.exception.BaseUncheckedException;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.cache.repository.CachePlusOps;
import com.ningdatech.carapi.archives.enums.AuditStateEnum;
import com.ningdatech.carapi.car.datascreen.model.dto.DataScreenParam;
import com.ningdatech.carapi.car.rpt.model.dto.RptOtherParamDTO;
import com.ningdatech.carapi.car.rpt.model.po.RptOtherOnlineVehiclesPO;
import com.ningdatech.carapi.car.vehicle.model.NdVehicleBaseInfo;
import com.ningdatech.carapi.car.vehicle.service.IVehicleBaseInfoService;
import com.ningdatech.carapi.common.aop.BuildChildrenRegionEntity;
import com.ningdatech.carapi.common.aop.BuildChildrenRegionWrapper;
import com.ningdatech.carapi.common.constant.UserRoleTypeEnum;
import com.ningdatech.carapi.common.util.CodeUtil;
import com.ningdatech.carapi.company.entity.po.ResCompanyTotalPO;
import com.ningdatech.carapi.datascope.aop.LambdaDataScope;
import com.ningdatech.carapi.datascope.aop.XmlDataScope;
import com.ningdatech.carapi.datascope.contants.LambdaDataScopeTypeEnum;
import com.ningdatech.carapi.driver.constant.UserCanLoginEnum;
import com.ningdatech.carapi.safe.model.dto.SecurityHonestyPageQuery;
import com.ningdatech.carapi.sys.contants.UserLoginConstant;
import com.ningdatech.carapi.sys.entity.Company;
import com.ningdatech.carapi.sys.entity.CompanyType;
import com.ningdatech.carapi.sys.entity.CompanyTypeRelated;
import com.ningdatech.carapi.sys.entity.UserRole;
import com.ningdatech.carapi.sys.entity.dto.CompanySaveDTO;
import com.ningdatech.carapi.sys.entity.dto.CompanyUpdateDTO;
import com.ningdatech.carapi.sys.helper.CompaniesCacheHelper;
import com.ningdatech.carapi.sys.helper.RegionsCacheHelper;
import com.ningdatech.carapi.sys.helper.UserCacheHelper;
import com.ningdatech.carapi.sys.mapper.CompanyMapper;
import com.ningdatech.carapi.sys.service.CompanyService;
import com.ningdatech.carapi.sys.service.CompanyTypeRelatedService;
import com.ningdatech.carapi.sys.service.CompanyTypeService;
import com.ningdatech.carapi.sys.service.UserRoleService;
import com.ningdatech.carapi.sys.utils.AuthCacheKeyUtils;
import com.ningdatech.carapi.user.entity.NdUserAuth;
import com.ningdatech.carapi.user.entity.NdUserInfo;
import com.ningdatech.carapi.user.mapper.NdUserInfoMapper;
import com.ningdatech.carapi.user.security.auth.constant.AuthTypeEnum;
import com.ningdatech.carapi.user.service.INdUserAuthService;
import com.ningdatech.carapi.user.service.INdUserInfoService;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 业务实现类
 * 企业
 * </p>
 *
 * @author PoffyZhang
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> implements CompanyService {

    private final CompanyMapper companyMapper;
    private final NdUserInfoMapper userInfoMapper;

    private final CompanyTypeService companyTypeService;
    private final CompanyTypeRelatedService companyTypeRelatedService;
    private final RegionsCacheHelper regionsCacheHelper;
    private final CompaniesCacheHelper companiesCacheHelper;
    private final INdUserInfoService userInfoService;
    private final UserCacheHelper userCacheHelper;
    private final PasswordEncoder passwordEncoder;
    private final INdUserAuthService ndUserAuthService;
    private final UserRoleService userRoleService;
    private final IVehicleBaseInfoService vehicleBaseInfoService;
    private final CachePlusOps cachePlusOps;

    /**
     *
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByIdWithCache(List<Long> ids) {
        if (ids.isEmpty()) {
            return true;
        }

        // 获取企业名称
        List<String> companyNameList = companyMapper.selectList(Wrappers.lambdaQuery(Company.class).in(Company::getId, ids)).stream()
                .map(Company::getName).collect(Collectors.toList());
        // 删除企业
        boolean removeFlag = removeByIds(ids);
        if(removeFlag){
            companiesCacheHelper.refresh(ids);
            // 删除该企业用户及用户关联的角色、账号密码等信息
            List<NdUserInfo> userInfos = userInfoService.list(Wrappers.lambdaQuery(NdUserInfo.class)
                    .in(NdUserInfo::getUsername, companyNameList));
            for (NdUserInfo userInfo : userInfos) {
                Long id = userInfo.getId();
                UserRole userRole = userRoleService.getOne(Wrappers.lambdaQuery(UserRole.class).eq(UserRole::getUserId, id));
                if (Objects.nonNull(userRole)) {
                    Long roleId = userRole.getRoleId();
                    if (UserRoleTypeEnum.COMPANY_INFO_OFFICER_ROLE_ID.getId().equals(roleId)) {
                        // 删除企业用户
                        userInfoService.removeById(userInfo);
                        //删除关联表和缓存
                        ndUserAuthService.remove(Wrappers.lambdaQuery(NdUserAuth.class).eq(NdUserAuth::getUserId, id));
                        userRoleService.remove(Wrappers.lambdaQuery(UserRole.class).eq(UserRole::getUserId, id));
                        cachePlusOps.del(AuthCacheKeyUtils.userResourceCacheKey(id));
                        userCacheHelper.refresh(id);
                    }
                }
            }

            // 车辆关联的这些企业与区域信息置为空
            List<NdVehicleBaseInfo> vehicleBaseInfoList = vehicleBaseInfoService.list(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                    .in(NdVehicleBaseInfo::getCompanyId, ids));

            for (NdVehicleBaseInfo vehicleBaseInfo : vehicleBaseInfoList) {
                LambdaUpdateWrapper<NdVehicleBaseInfo> updateWrapper = Wrappers.lambdaUpdate(NdVehicleBaseInfo.class);
                updateWrapper.set(NdVehicleBaseInfo::getCompanyId,null)
                        .set(NdVehicleBaseInfo::getUseCompanyId,null)
                        .set(NdVehicleBaseInfo::getUseCompanyName,null)
                        .set(NdVehicleBaseInfo::getRegionId,null)
                        .set(NdVehicleBaseInfo::getRegionName,null)
                        .eq(NdVehicleBaseInfo::getCarPlate,vehicleBaseInfo.getCarPlate());
                vehicleBaseInfoService.update(updateWrapper);
            }
        }

        //删除前要判断是否企业下还有用户
        //ids.forEach(entId -> {
        //    if (userInfoMapper.selectCount(Wrappers.lambdaQuery(NdUserInfo.class).eq(NdUserInfo::getCompanyId, entId)) > 0) {
        //        throw new BaseUncheckedException(HttpStatus.HTTP_OK, "删除失败!企业下还有用户存在 {}",
        //                entId);
        //    }
        //});

        // 企业
        return removeFlag;
    }


    /**
     * 保存用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Company save(CompanySaveDTO data, Long userId) {
        if (StrUtil.isNotBlank(data.getName())
                && check(data.getName())) {
            throw new BaseUncheckedException(HttpStatus.HTTP_OK, "企业名{} 已经存在",
                    data.getName());
        }
        Company company = BeanUtil.toBean(data, Company.class);

        //创建人 最后修改人
        company.setCreateBy(userId);
        company.setUpdateBy(userId);
        company.setRegionName(regionsCacheHelper.getDisplayName(data.getRegionId()));
        // 默认新增的企业为待审核
        company.setRegionAuditStatus(AuditStateEnum.AUDIT.getCode());
        company.setCityAuditStatus(AuditStateEnum.AUDIT.getCode());
        if(save(company)){
            companiesCacheHelper.refresh(company.getId());
        }
        // 公司类型
        if (CollUtil.isNotEmpty(data.getEnterpriseTypes())) {
            // 先删除原来关联的信息，再保存
            companyTypeRelatedService.remove(Wrappers.lambdaQuery(CompanyTypeRelated.class)
                    .eq(CompanyTypeRelated::getCompanyId,company.getId()));
            for (Long enterpriseType : data.getEnterpriseTypes()) {
                CompanyTypeRelated companyTypeRelated = new CompanyTypeRelated();
                companyTypeRelated.setCompanyId(company.getId());
                companyTypeRelated.setCompanyTypeId(enterpriseType);
                companyTypeRelatedService.save(companyTypeRelated);
            }
        }

        // 新增企业后，直接创建企业信息员账号
        NdUserInfo user = new NdUserInfo();
        user.setUsername(company.getName());
        user.setCompanyId(company.getId());
        // 登录用户手机号使用企业法人电话
        user.setMobile(company.getLegalPersonPhone());
        user.setNickName(company.getName());
        user.setCanLogin(UserCanLoginEnum.CAN_LOGIN.getCode());

        //创建人 最后修改人
        user.setCreateBy(userId);
        user.setUpdateBy(userId);
        if (checkMobile(company)){
            throw new BizException("该法人手机号绑定的企业用户已存在,请更换法人手机号!");
        }
        boolean save = userInfoService.save(user);
        if(save){
            userCacheHelper.refresh(user.getId());
        }

        NdUserAuth userAuth = new NdUserAuth();
        userAuth.setUserId(user.getId());
        // 登录账号使用企业全称
        userAuth.setIdentifier(company.getName());
        // 初始密码使用123456Aa
        userAuth.setCredential(passwordEncoder.encode(UserLoginConstant.LOGIN_PASSWORD));
        userAuth.setAuthType(AuthTypeEnum.ACCOUNT_PASSWORD.getKey());
        ndUserAuthService.save(userAuth);

        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        // 用户角色设置为企业信息员
        userRole.setRoleId(UserRoleTypeEnum.COMPANY_INFO_OFFICER_ROLE_ID.getId());
        userRoleService.save(userRole);
        return company;
    }

    private Boolean checkMobile(Company company) {
        return userInfoService.count(Wrappers.lambdaQuery(NdUserInfo.class)
                .eq(NdUserInfo::getMobile, company.getLegalPersonPhone())) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Company update(CompanyUpdateDTO data, Long userId) {
        Company company = BeanUtil.toBean(data, Company.class);
        //最后修改人
        company.setUpdateBy(userId);
        company.setRegionName(regionsCacheHelper.getDisplayName(data.getRegionId()));
        if(updateById(company)){
            companiesCacheHelper.refresh(company.getId());
        }
        //公司类型
        if (CollUtil.isNotEmpty(data.getEnterpriseTypes())) {
            companyTypeRelatedService.remove(Wrappers.lambdaQuery(CompanyTypeRelated.class).eq(CompanyTypeRelated::getCompanyId, company.getId()));
            for (Long enterpriseType : data.getEnterpriseTypes()) {
                CompanyType companyType = companyTypeService.getById(enterpriseType);
                if (Objects.nonNull(companyType)) {
                    companyTypeRelatedService.save(new CompanyTypeRelated(null, company.getId(), companyType.getId()));
                }
            }
        }
        return company;
    }

    @Override
    public Boolean check(String name) {
        return super.count(Wrappers.lambdaQuery(Company.class).eq(Company::getName, name)) > 0;
    }

    @Override
    public Boolean check(String name, Long id) {
        return super.count(Wrappers.lambdaQuery(Company.class).ne(Company::getId, id)
                .eq(Company::getName, name)) > 0;
    }

    @Override
    public Map<Long, Company> listCompanyByIds(Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyMap();
        }
        List<Company> companyList = list(Wrappers.lambdaQuery(Company.class)
                .in(Company::getId, ids));
        return CollUtils.listToMap(companyList, Company::getId);
    }

    @Override
    public List<Company> listCompanyByCompanyNames(Collection<String> names) {
        if (CollectionUtils.isEmpty(names)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<Company> query = Wrappers.lambdaQuery(Company.class);
        query.in(Company::getName, names);
        return list(query);
    }

    @Override
    public Company getCompanyByCompanyNames(String companyName) {
        LambdaQueryWrapper<Company> query = Wrappers.lambdaQuery(Company.class);
        query.eq(Company::getName, companyName);
        return getOne(query);
    }

    @Override
    public List<String> findCompanyIdsByRegionId(DataScreenParam param) {
        LambdaQueryWrapper<Company> wrapper = Wrappers.lambdaQuery(Company.class);
        CompanyServiceImpl context = (CompanyServiceImpl)AopContext.currentProxy();
        List<Company> list =  context.searchCompanyIdsByRegionId(wrapper,param);
        return list.stream().map(v -> String.valueOf(v.getId())).collect(Collectors.toList());
    }

    @Override
    @XmlDataScope(entityIndex = 1)
    @BuildChildrenRegionEntity(entityIndex = 1)
    public Page<RptOtherOnlineVehiclesPO> pageVehicleNum(Page<RptOtherOnlineVehiclesPO> page, RptOtherParamDTO param) {
        CodeUtil.buildRegionLevel(param);
        return companyMapper.pageVehicleNum(page,param);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<ResCompanyTotalPO> countByRegion(DataScreenParam param) {
        CodeUtil.buildRegionLevel(param);
        return companyMapper.countByRegion(param);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<ResCompanyTotalPO> countEnterpriseType(DataScreenParam param) {
        CodeUtil.buildRegionLevel(param);
        return companyMapper.countEnterpriseType(param);
    }

    @Override
    @LambdaDataScope(wrapperIndex = 1)
    @BuildChildrenRegionWrapper(wrapperIndex = 1,entityIndex = 2)
    public Page<Company> pageDataScope(Page<Company> page, LambdaQueryWrapper<Company> wrapper, SecurityHonestyPageQuery param) {
        return page(page,wrapper);
    }

    @LambdaDataScope(type = LambdaDataScopeTypeEnum.COMPANY_TABLE)
    @BuildChildrenRegionWrapper
    public List<Company> searchCompanyIdsByRegionId(LambdaQueryWrapper<Company> wrapper, DataScreenParam param) {
        log.info(param.toString());
        return list(wrapper);
    }

}
