package com.usoftchina.uas.office.dingtalk.service;

import com.alibaba.fastjson.JSON;
import com.dingtalk.api.request.OapiUserCreateRequest;
import com.dingtalk.api.request.OapiUserUpdateRequest;
import com.dingtalk.api.response.OapiUserListbypageResponse;
import com.usoftchina.dingtalk.sdk.AddrBookSdk;
import com.usoftchina.uas.office.dingtalk.entity.Employee;
import com.usoftchina.uas.office.dingtalk.entity.HrOrg;
import com.usoftchina.uas.office.service.AbstractService;
import com.usoftchina.uas.office.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

/**
 * @author yingp
 * @date 2020/2/16
 */
@Service
public class UasEmployeeService extends AbstractService {

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

    @Autowired
    private AddrBookSdk addrBookSdk;

    public Employee getById(Integer id) {
        return queryForBean("select * from employee where em_id=?", Employee.class, id);
    }

    public Employee getRemovedById(Integer id) {
        Employee employee = queryFlashbackBean("employee", "where em_id=?", Employee.class, id);
        if (null == employee) {
            employee = queryRecycleBean("Employeemanager", Employee.class, id);
        }
        return employee;
    }

    public Employee getByDingUserId(String dingUserId) {
        return queryForBean("select * from employee where em_ding=? and em_class<>'离职'", Employee.class, dingUserId);
    }

    public Employee getByMobile(String mobile) {
        return queryForBean("select * from employee where em_mobile=? and em_class<>'离职'", Employee.class, mobile);
    }

    public Employee getByEmail(String email) {
        return queryForBean("select * from employee where em_email=? and em_class<>'离职'", Employee.class, email);
    }

    public Employee getByCode(String code) {
        return queryForBean("select * from employee where em_code=? and em_class<>'离职'", Employee.class, code);
    }

    public void setDingUserId(Employee employee) {
        jdbcTemplate.update("update employee set em_ding=? where em_id=?", employee.getEm_ding(), employee.getEm_id());
    }

    public List<Employee> getAll() {
        return queryForBeanList("select * from employee where em_class<>'离职' and em_ding is not null", Employee.class);
    }

    /**
     * 是否组织内成员
     *
     * @param org
     * @param employee
     * @return
     */
    public boolean isMemberOfOrg(HrOrg org, Employee employee) {
        if (org.getOr_id().equals(employee.getEm_defaultorid())) {
            return true;
        }
        return queryForObject("select count(1) from empsjobs where org_id=? and emp_id=?",
                Integer.class, org.getOr_id(), employee.getEm_id()) > 0;
    }

    /**
     * 同步全部 uas人员资料 -> 钉钉通讯录成员资料
     */
    public void sync() {
        // 先对非离职人员建立绑定关系，并同步到通讯录；再对已离职人员，如果无冲突匹配，则删除到通讯录
        List<Employee> employeeList = queryForBeanList("select * from employee where em_statuscode='AUDITED' and em_mobile is not null " +
                "and exists (select 1 from hrorg where or_id=em_defaultorid and or_ding is not null) order by (case em_class when '离职' then 1 else 0 end)", Employee.class);
        if (CollectionUtils.isEmpty(employeeList)) {
            return;
        }
        // 人员资料预处理
        Iterator<Employee> iterator = employeeList.iterator();
        while (iterator.hasNext()) {
            Employee employee = iterator.next();
            employee.setEm_mobile(StringUtils.getValidMobile(employee.getEm_mobile()));
            employee.setEm_email(StringUtils.getValidEmail(employee.getEm_email()));
            employee.setEm_tel(StringUtils.getValidTelephone(employee.getEm_tel()));
            if (StringUtils.isEmpty(employee.getEm_mobile()) && StringUtils.isEmpty(employee.getEm_email())
                    && StringUtils.isEmpty(employee.getEm_ding())) {
                iterator.remove();
            }
        }
        DingTalkUserFactory userFactory = new DingTalkUserFactory(getRootOrg().getOr_ding());
        for (Employee employee : employeeList) {
            sync(employee, userFactory);
        }
    }

    /**
     * 同步单个 uas人员资料 -> 钉钉通讯录成员资料
     */
    public void sync(Employee employee) {
        employee.setEm_mobile(StringUtils.getValidMobile(employee.getEm_mobile()));
        employee.setEm_email(StringUtils.getValidEmail(employee.getEm_email()));
        employee.setEm_tel(StringUtils.getValidTelephone(employee.getEm_tel()));
        if (StringUtils.isEmpty(employee.getEm_mobile())) {
            if (null != employee.getEm_ding()) {
                // 手机号和邮箱均为空，不符合微信要求，账号先封禁
                logger.debug("mobile must not null " + JSON.toJSONString(employee));
            }
            return;
        }

        DingTalkUserFactory userFactory = new DingTalkUserFactory(getRootOrg().getOr_ding());
        sync(employee, userFactory);
    }

    private void sync(Employee employee, DingTalkUserFactory userFactory) {
        List<HrOrg> orgList = getOrgListByEmployee(employee);
        OapiUserListbypageResponse.Userlist user = userFactory.getSimilarUser(employee);
        if (null != user) {
            if ("离职".equals(employee.getEm_class())) {
                addrBookSdk.deleteUser("Uas", user.getUserid());
                logger.debug("delete dingtalk user " + JSON.toJSONString(user) + ", because of " + JSON.toJSONString(employee));
                userFactory.remove(user);
                employee.setEm_ding(null);
                setDingUserId(employee);
            } else {
                checkUserChange(employee, user, orgList, userFactory);
                if (!user.getUserid().equals(employee.getEm_ding())) {
                    employee.setEm_ding(user.getUserid());
                    setDingUserId(employee);
                }
            }
        } else {
            if (!"离职".equals(employee.getEm_class())) {
                createUser(employee, orgList);
                employee.setEm_ding(employee.getEm_code());
                setDingUserId(employee);
            }
        }
    }

    private void checkUserChange(Employee employee, OapiUserListbypageResponse.Userlist user, List<HrOrg> orgList, DingTalkUserFactory userFactory) {
        boolean changed = false;
        OapiUserUpdateRequest req = new OapiUserUpdateRequest();
        req.setUserid(user.getUserid());
        if (!StringUtils.equals(employee.getEm_name(), user.getName())) {
            changed = true;
            req.setName(employee.getEm_name());
        }
        if (!StringUtils.equals(employee.getEm_mobile(), user.getMobile())) {
            if (!StringUtils.isEmpty(employee.getEm_mobile())) {
                changed = true;
                req.setMobile(employee.getEm_mobile());
            }
        }
        if (!StringUtils.equals(employee.getEm_email(), user.getEmail())) {
            if (!StringUtils.isEmpty(employee.getEm_email())) {
                userFactory.checkNewEmail(employee);
                changed = true;
                req.setEmail(employee.getEm_email());
            }
        }
        if (!StringUtils.equals(employee.getEm_tel(), user.getTel())) {
            if (!StringUtils.isEmpty(employee.getEm_tel())) {
                changed = true;
                req.setTel(employee.getEm_tel());
            }
        }
        if (!StringUtils.equals(employee.getEm_csshortname(), user.getWorkPlace())) {
            changed = true;
            req.setWorkPlace(employee.getEm_csshortname());
        }
        if (!StringUtils.equals(employee.getEm_position(), user.getPosition())) {
            changed = true;
            req.setPosition(employee.getEm_position());
        }
        if (!StringUtils.equals(employee.getEm_code(), user.getJobnumber())) {
            changed = true;
            req.setJobnumber(employee.getEm_code());
        }

        List<Long> oldDepList = JSON.parseArray(user.getDepartment(), Long.class);
        oldDepList.sort((o1, o2) -> o1.compareTo(o2));
        List<Long> newDepList = orgList.stream().map(HrOrg::getOr_ding).sorted().collect(Collectors.toList());
        if (!Objects.equals(oldDepList, newDepList)) {
            changed = true;
            req.setDepartment(newDepList);
        }

        if (changed) {
            addrBookSdk.updateUser("Uas", req);
        }
    }

    private HrOrg getRootOrg() {
        return queryForBean("select * from hrorg where or_subof=0 and or_statuscode<>'DISABLE' and or_ding is not null",
                HrOrg.class);
    }

    public List<HrOrg> getOrgListByEmployee(Employee employee) {
        // 只需考虑所属组织已同步的情况
        return queryForBeanList("select * from hrorg where or_statuscode<>'DISABLE' and or_ding is not null and (or_id=? or exists (select 1 from empsjobs where org_id=or_id and emp_id=?))",
                HrOrg.class, employee.getEm_defaultorid(), employee.getEm_id());
    }

    private void createUser(Employee employee, List<HrOrg> orgList) {
        List<Long> departmentList = orgList.stream().map(HrOrg::getOr_ding).collect(Collectors.toList());
        OapiUserCreateRequest req = new OapiUserCreateRequest();
        req.setUserid(employee.getEm_code());
        req.setJobnumber(employee.getEm_code());
        req.setWorkPlace(employee.getEm_csshortname());
        req.setEmail(employee.getEm_email());
        req.setMobile(employee.getEm_mobile());
        req.setTel(employee.getEm_tel());
        req.setPosition(employee.getEm_position());
        req.setName(employee.getEm_name());
        req.setDepartment(JSON.toJSONString(departmentList));
        logger.debug("create dingtalk user, for " + JSON.toJSONString(employee));
        addrBookSdk.createUser("Uas", req);
    }

    class DingTalkUserFactory {
        private Map<String, List<OapiUserListbypageResponse.Userlist>> mobileGroup;
        private Map<String, List<OapiUserListbypageResponse.Userlist>> emailGroup;
        private Map<String, List<OapiUserListbypageResponse.Userlist>> nameGroup;
        private Map<String, OapiUserListbypageResponse.Userlist> idMap;

        public DingTalkUserFactory(Long departmentId) {
            List<OapiUserListbypageResponse.Userlist> userList = addrBookSdk.getUserList("Uas", departmentId, true);
            mobileGroup = new HashMap<>();
            emailGroup = new HashMap<>();
            nameGroup = new HashMap<>();
            idMap = new HashMap<>(userList.size());
            for (OapiUserListbypageResponse.Userlist user : userList) {
                if (StringUtils.hasText(user.getMobile())) {
                    List<OapiUserListbypageResponse.Userlist> users = mobileGroup.get(user.getMobile());
                    if (null == users) {
                        users = new ArrayList<>(1);
                        mobileGroup.put(user.getMobile(), users);
                    }
                    users.add(user);
                }
                if (StringUtils.hasText(user.getEmail())) {
                    List<OapiUserListbypageResponse.Userlist> users = emailGroup.get(user.getEmail());
                    if (null == users) {
                        users = new ArrayList<>(1);
                        emailGroup.put(user.getEmail(), users);
                    }
                    users.add(user);
                }
                List<OapiUserListbypageResponse.Userlist> users = nameGroup.get(user.getName());
                if (null == users) {
                    users = new ArrayList<>(1);
                    nameGroup.put(user.getName(), users);
                }
                users.add(user);
                idMap.put(user.getUserid(), user);
            }
        }

        /**
         * 用于人员资料更新邮箱
         *
         * @param employee
         */
        public void checkNewEmail(Employee employee) {
            List<OapiUserListbypageResponse.Userlist> users = emailGroup.get(employee.getEm_email());
            if (!CollectionUtils.isEmpty(users)) {
                for (OapiUserListbypageResponse.Userlist user : users) {
                    if (!user.getUserid().equals(employee.getEm_ding())) {
                        Employee other = getByDingUserId(user.getUserid());
                        if (null == other) {
                            // 如果人员在uas不存在，删除通讯录人员
                            addrBookSdk.deleteUser("Uas", user.getUserid());
                            logger.info("delete dingtalk user: " + JSON.toJSONString(user) + ", because of " + JSON.toJSONString(employee));
                        } else {
                            throw new RuntimeException(String.format("邮箱 %s 已被 %s 使用，请修改 %s 的邮箱",
                                    employee.getEm_email(), other.getEm_name(), employee.getEm_name()));
                        }
                    }
                }
            }
        }

        public OapiUserListbypageResponse.Userlist getSimilarUser(Employee employee) {
            OapiUserListbypageResponse.Userlist user = null;
            if (StringUtils.hasText(employee.getEm_ding()) && idMap.containsKey(employee.getEm_ding())) {
                user = idMap.get(employee.getEm_ding());
            } else if (StringUtils.hasText(employee.getEm_mobile()) && mobileGroup.containsKey(employee.getEm_mobile())) {
                if (mobileGroup.get(employee.getEm_mobile()).size() > 1) {
                    throw new RuntimeException("钉钉存在多个人绑定同一手机号的情况 " + employee.getEm_mobile());
                }
                user = mobileGroup.get(employee.getEm_mobile()).get(0);
            } else if (StringUtils.hasText(employee.getEm_email()) && emailGroup.containsKey(employee.getEm_email())) {
                if (emailGroup.get(employee.getEm_email()).size() > 1) {
                    throw new RuntimeException("钉钉存在多个人绑定同一邮箱的情况 " + employee.getEm_email());
                }
                user = emailGroup.get(employee.getEm_email()).get(0);
            } else if (nameGroup.containsKey(employee.getEm_name())) {
                if (nameGroup.get(employee.getEm_name()).size() > 1) {
                    throw new RuntimeException("钉钉人员存在同名的情况 " + employee.getEm_name());
                }
                user = nameGroup.get(employee.getEm_name()).get(0);
            }
            // 如果不是通过userId绝对匹配的
            if (null != user && !user.getUserid().equals(employee.getEm_ding())) {
                Employee other = getByDingUserId(user.getUserid());
                if (null != other && !other.getEm_id().equals(employee.getEm_id())) {
                    // 另有其人
                    user = null;
                }
            }
            return user;
        }

        public void remove(OapiUserListbypageResponse.Userlist user) {
            idMap.remove(user.getUserid());
            if (mobileGroup.containsKey(user.getMobile())) {
                mobileGroup.get(user.getMobile()).remove(user);
                if (mobileGroup.get(user.getMobile()).size() == 0) {
                    mobileGroup.remove(user.getMobile());
                }
            }
            if (emailGroup.containsKey(user.getEmail())) {
                emailGroup.get(user.getEmail()).remove(user);
                if (emailGroup.get(user.getEmail()).size() == 0) {
                    emailGroup.remove(user.getEmail());
                }
            }
            if (nameGroup.containsKey(user.getName())) {
                nameGroup.get(user.getName()).remove(user);
                if (nameGroup.get(user.getName()).size() == 0) {
                    nameGroup.remove(user.getName());
                }
            }
        }
    }
}
