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

import com.alibaba.fastjson.JSON;
import com.usoftchina.qywx.sdk.AddrBookSdk;
import com.usoftchina.qywx.sdk.dto.CreateUserReq;
import com.usoftchina.qywx.sdk.dto.GetUserListResp;
import com.usoftchina.qywx.sdk.dto.UpdateUserReq;
import com.usoftchina.uas.office.qywx.entity.Employee;
import com.usoftchina.uas.office.qywx.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.*;

/**
 * @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 getByQywx(String qywxUserId) {
        return queryForBean("select * from employee where em_qywx=? and em_class<>'离职'", Employee.class, qywxUserId);
    }

    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 setQywx(Employee employee) {
        jdbcTemplate.update("update employee set em_qywx=? where em_id=?", employee.getEm_qywx(), employee.getEm_id());
    }

    public void setMobile(Employee employee) {
        jdbcTemplate.update("update employee set em_mobile=? where em_id=?", employee.getEm_mobile(), employee.getEm_id());
    }

    public void setEmail(Employee employee) {
        jdbcTemplate.update("update employee set em_email=? where em_id=?", employee.getEm_email(), employee.getEm_id());
    }

    public void setTelephone(Employee employee) {
        jdbcTemplate.update("update employee set em_tel=? where em_id=?", employee.getEm_tel(), employee.getEm_id());
    }

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

    /**
     * 同步全部 uas人员资料 -> 企业微信成员资料
     */
    public void sync() {
        // 先对非离职人员建立绑定关系，并同步到通讯录；再对已离职人员，如果无冲突匹配，则删除到通讯录
        List<Employee> employeeList = queryForBeanList("select * from employee where em_statuscode='AUDITED' and (em_mobile is not null or em_email is not null) " +
                "and exists (select 1 from hrorg where or_id=em_defaultorid and or_qywx 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_qywx())) {
                iterator.remove();
            }
        }
        QywxUserFactory qywxUserFactory = new QywxUserFactory(getRootOrg().getOr_qywx());
        for (Employee employee : employeeList) {
            sync(employee, qywxUserFactory);
        }
    }

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

    public List<HrOrg> getOrgListByEmployee(Employee employee) {
        // 只需考虑所属组织已同步的情况
        return queryForBeanList("select * from hrorg where or_statuscode<>'DISABLE' and or_qywx 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());
    }

    /**
     * 是否组织内成员
     *
     * @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;
    }

    private void sync(Employee employee, QywxUserFactory qywxUserFactory) {
        List<HrOrg> orgList = getOrgListByEmployee(employee);
        GetUserListResp.User user = qywxUserFactory.getSimilarUser(employee);
        if (null != user) {
            if ("离职".equals(employee.getEm_class())) {
                addrBookSdk.deleteUser(user.getUserid());
                logger.debug("delete qywx user " + JSON.toJSONString(user) + ", because of " + JSON.toJSONString(employee));
                qywxUserFactory.remove(user);
                employee.setEm_qywx(null);
                setQywx(employee);
            } else {
                checkUserChange(employee, user, orgList, qywxUserFactory);
                if (!user.getUserid().equals(employee.getEm_qywx())) {
                    employee.setEm_qywx(user.getUserid());
                    setQywx(employee);
                }
            }
        } else {
            if (!"离职".equals(employee.getEm_class())) {
                createUser(employee, orgList);
                employee.setEm_qywx(employee.getEm_code());
                setQywx(employee);
            }
        }
    }

    private void checkUserChange(Employee employee, GetUserListResp.User user, List<HrOrg> orgList, QywxUserFactory qywxUserFactory) {
        if (addrBookSdk.isReadonly()) {
            return;
        }
        boolean changed = false;
        UpdateUserReq req = new UpdateUserReq();
        if (!StringUtils.equals(employee.getEm_name(), user.getName())) {
            changed = true;
            req.name(employee.getEm_name());
        }
        if (!StringUtils.equals(employee.getEm_mobile(), user.getMobile())) {
            if (!StringUtils.isEmpty(employee.getEm_mobile())) {
                changed = true;
                req.mobile(employee.getEm_mobile());
            }
        }
        if (!StringUtils.equals(employee.getEm_email(), user.getEmail())) {
            if (!StringUtils.isEmpty(employee.getEm_email())) {
                qywxUserFactory.checkNewEmail(employee);
                changed = true;
                req.email(employee.getEm_email());
            }
        }
        if (!StringUtils.equals(employee.getEm_tel(), user.getTelephone())) {
            if (!StringUtils.isEmpty(employee.getEm_tel())) {
                changed = true;
                req.telephone(employee.getEm_tel());
            }
        }
        if (!StringUtils.equals(employee.getEm_csshortname(), user.getAddress())) {
            changed = true;
            req.address(employee.getEm_csshortname());
        }
        if (!(null == employee.getEm_sex() && null == user.getGender())) {
            UpdateUserReq.Gender gender = "男".equals(employee.getEm_sex()) ? UpdateUserReq.Gender.MALE : UpdateUserReq.Gender.FEMALE;
            if (!gender.getCode().equals(user.getGender())) {
                changed = true;
                req.gender(gender);
            }
        }
        if (!StringUtils.equals(employee.getEm_position(), user.getPosition())) {
            changed = true;
            req.position(employee.getEm_position());
        }

        List<UpdateUserReq.Department> departmentList = new ArrayList<>(orgList.size());
        for (HrOrg org : orgList) {
            boolean leader = employee.getEm_code().equals(org.getOr_headmancode());
            departmentList.add(new UpdateUserReq.Department(org.getOr_qywx(), null, leader));
            if (!user.getDepartment().contains(org.getOr_qywx()) ||
                    (leader ? 1 : 0) != user.getIs_leader_in_dept().get(user.getDepartment().indexOf(org.getOr_qywx()))) {
                changed = true;
            }
        }
        req.department(departmentList);

        if (changed || 0 == user.getEnable()) {
            addrBookSdk.updateUser(req.userId(user.getUserid()).enable(true));
        }
    }

    private void createUser(Employee employee, List<HrOrg> orgList) {
        if (addrBookSdk.isReadonly()) {
            return;
        }
        List<CreateUserReq.Department> departmentList = new ArrayList<>(orgList.size());
        for (HrOrg org : orgList) {
            departmentList.add(new CreateUserReq.Department(org.getOr_qywx(), null,
                    employee.getEm_code().equals(org.getOr_headmancode())));
        }
        CreateUserReq req = new CreateUserReq()
                .userId(employee.getEm_code())
                .address(employee.getEm_csshortname())
                .email(null == employee.getEm_email() ? null : employee.getEm_email())
                .mobile(null == employee.getEm_mobile() ? null : employee.getEm_mobile())
                .telephone(null == employee.getEm_tel() ? null : employee.getEm_tel())
                .position(employee.getEm_position())
                .gender("女".equals(employee.getEm_sex()) ? CreateUserReq.Gender.FEMALE : CreateUserReq.Gender.MALE)
                .enable(true)
                .name(employee.getEm_name())
                .toInvite(true)
                .department(departmentList);
        logger.debug("create qywx user, for " + JSON.toJSONString(employee));
        addrBookSdk.createUser(req);
    }

    class QywxUserFactory {
        private Map<String, List<GetUserListResp.User>> mobileGroup;
        private Map<String, List<GetUserListResp.User>> emailGroup;
        private Map<String, List<GetUserListResp.User>> nameGroup;
        private Map<String, GetUserListResp.User> idMap;

        public QywxUserFactory(Integer departmentId) {
            List<GetUserListResp.User> userList = addrBookSdk.getUserList(departmentId, true);
            mobileGroup = new HashMap<>();
            emailGroup = new HashMap<>();
            nameGroup = new HashMap<>();
            idMap = new HashMap<>(userList.size());
            for (GetUserListResp.User user : userList) {
                if (StringUtils.hasText(user.getMobile())) {
                    List<GetUserListResp.User> 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<GetUserListResp.User> users = emailGroup.get(user.getEmail());
                    if (null == users) {
                        users = new ArrayList<>(1);
                        emailGroup.put(user.getEmail(), users);
                    }
                    users.add(user);
                }
                List<GetUserListResp.User> 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<GetUserListResp.User> users = emailGroup.get(employee.getEm_email());
            if (!CollectionUtils.isEmpty(users)) {
                for (GetUserListResp.User user : users) {
                    if (!user.getUserid().equals(employee.getEm_qywx())) {
                        Employee other = getByQywx(user.getUserid());
                        if (null == other) {
                            // 如果人员在uas不存在，删除通讯录人员
                            addrBookSdk.deleteUser(user.getUserid());
                            logger.info("delete qywx 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 GetUserListResp.User getSimilarUser(Employee employee) {
            GetUserListResp.User user = null;
            if (StringUtils.hasText(employee.getEm_qywx()) && idMap.containsKey(employee.getEm_qywx())) {
                user = idMap.get(employee.getEm_qywx());
            } 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_qywx())) {
                Employee other = getByQywx(user.getUserid());
                if (null != other && !other.getEm_id().equals(employee.getEm_id())) {
                    // 另有其人
                    user = null;
                }
            }
            return user;
        }

        public void remove(GetUserListResp.User 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());
                }
            }
        }
    }

    /**
     * 同步单个 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()) && StringUtils.isEmpty(employee.getEm_email())) {
            if (null != employee.getEm_qywx() && !addrBookSdk.isReadonly()) {
                // 手机号和邮箱均为空，不符合微信要求，账号先封禁
                addrBookSdk.updateUser(new UpdateUserReq().userId(employee.getEm_qywx()).enable(false));
                logger.debug("disable qywx user " + employee.getEm_qywx() + ", because of " + JSON.toJSONString(employee));
            }
            return;
        }

        QywxUserFactory qywxUserFactory = new QywxUserFactory(getRootOrg().getOr_qywx());
        sync(employee, qywxUserFactory);
    }
}
