package cn.reghao.autodop.dmaster.notification.service;

import cn.reghao.autodop.common.http.DefaultWebRequest;
import cn.reghao.autodop.common.http.WebRequest;
import cn.reghao.autodop.dmaster.notification.model.po.*;
import cn.reghao.autodop.common.util.thread.ThreadPoolWrapper;
import cn.reghao.autodop.dmaster.notification.db.repository.DingAccountRepository;
import cn.reghao.autodop.dmaster.notification.db.repository.EmailAccountRepository;
import cn.reghao.autodop.dmaster.notification.db.repository.NotifyGroupRepository;
import cn.reghao.autodop.dmaster.notification.db.repository.SmsAccountRepository;
import cn.reghao.autodop.dmaster.notification.service.notifier.Notify;
import cn.reghao.autodop.dmaster.notification.service.notifier.ding.DingMsg;
import cn.reghao.autodop.dmaster.notification.service.notifier.ding.DingNotify;
import cn.reghao.autodop.dmaster.notification.service.notifier.email.EmailMsg;
import cn.reghao.autodop.dmaster.notification.service.notifier.email.EmailNotify;
import cn.reghao.autodop.dmaster.notification.service.notifier.sms.SmsNotify;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * 通知服务
 *
 * @author reghao
 * @date 2021-02-25 00:10:17
 */
@Slf4j
@Service
public class NotifyService {
    private ExecutorService threadPool = ThreadPoolWrapper.threadPool("notify-service");
    private Map<NotifyGroup, Notify> notifierMap = new HashMap<>();
    private WebRequest webRequest;
    private NotifyGroupRepository groupRepository;
    private DingAccountRepository dingRepository;
    private EmailAccountRepository emailRepository;
    private SmsAccountRepository smsRepository;

    public NotifyService(NotifyGroupRepository groupRepository,
                         DingAccountRepository dingRepository,
                         EmailAccountRepository emailRepository,
                         SmsAccountRepository smsRepository) {
        this.webRequest = new DefaultWebRequest();
        this.groupRepository = groupRepository;
        this.dingRepository = dingRepository;
        this.emailRepository = emailRepository;
        this.smsRepository = smsRepository;
    }

    /**
     * TODO 系统启动时初始化所有的通知组，这应该推迟到通知组第一次使用时才初始化
     *
     * @param
     * @return
     * @date 2021-06-23 下午9:47
     */
    @PostConstruct
    private void initNotifier() throws UnsupportedEncodingException {
        for (NotifyGroup group : groupRepository.findAll()) {
            addNotifier(group);
        }
    }

    public void addNotifier(NotifyGroup notifyGroup) throws UnsupportedEncodingException {
        Notify notify = notifierMap.get(notifyGroup);
        if (notify != null) {
            return;
        }

        String notifyType = notifyGroup.getNotifyType();
        String notifyAccountId = notifyGroup.getNotifyAccountId();
        switch (NotifyType.valueOf(notifyType)) {
            case ding:
                DingAccount dingAccount = dingRepository.findDingAccountByNotifyAccountId(notifyAccountId);
                notifierMap.put(notifyGroup, new DingNotify(webRequest, dingAccount));
                break;
            case email:
                EmailAccount emailAccount = emailRepository.findEmailAccountByNotifyAccountId(notifyAccountId);
                if (emailAccount != null) {
                    notifierMap.put(notifyGroup, new EmailNotify(emailAccount));
                }
                break;
            case sms:
                SmsAccount smsAccount = smsRepository.findSmsAccountByNotifyAccountId(notifyAccountId);
                if (smsAccount != null) {
                    notifierMap.put(notifyGroup, new SmsNotify(webRequest, smsAccount));
                }
                break;
            default:
                log.error("通知类型不存在");
        }
    }

    public <T> void notify(NotifyGroup notifyGroup, T msg) {
        Notify notify = notifierMap.get(notifyGroup);
        if (notify == null) {
            log.error("类型为 {}, 账户为 {} 的通知发送器不存在",
                    notifyGroup.getNotifyType(), notifyGroup.getNotifyAccountId());
            return;
        }

        String notifyType = notifyGroup.getNotifyType();
        switch (NotifyType.valueOf(notifyType)) {
            case ding:
                if (!(msg instanceof DingMsg)) {
                    log.error("{} 消息格式不正确, 不是 DingMsg", msg);
                }
                notifyGroup.getReceivers()
                        .forEach(receiver -> threadPool.execute(new NotifyTask(notify, receiver, msg)));
                break;
            case email:
                if (!(msg instanceof EmailMsg)) {
                    log.error("{} 消息格式不正确, 不是 EmailMsg", msg);
                }
                notifyGroup.getReceivers()
                        .forEach(receiver -> threadPool.execute(new NotifyTask(notify, receiver, msg)));
                break;
            case sms:
                if (!(msg instanceof String)) {
                    log.error("{} 消息格式不正确, 不是 SMS 消息", msg);
                }
                notifyGroup.getReceivers()
                        .forEach(receiver -> threadPool.execute(new NotifyTask(notify, receiver, msg)));
                break;
            default:
                log.error("通知类型不存在");
        }
    }

    /**
     * TODO 添加通知日志
     *
     * @param
     * @return
     * @date 2021-06-23 上午9:29
     */
    static class NotifyTask<T> implements Runnable {
        private Notify<T> notify;
        private String receiver;
        private T msg;

        public NotifyTask(Notify<T> notify, String receiver, T msg) {
            this.notify = notify;
            this.receiver = receiver;
            this.msg = msg;
        }

        @Override
        public void run() {
            try {
                notify.send(receiver, msg);
            } catch (Exception e) {
                log.error("发送给 {} 的通知失败 -> {}", receiver, e.getMessage());
            }
        }
    }
}
