package com.yida.data.school.news.service.impl;

import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
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.yida.data.common.core.entity.constant.AppConstant;
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
import com.yida.data.common.core.entity.job.Job;
import com.yida.data.common.core.entity.notice.qywx.MpNewsSchoolNotice;
import com.yida.data.common.core.entity.notice.qywx.inside.Article;
import com.yida.data.common.core.entity.notice.qywx.inside.News;
import com.yida.data.common.core.entity.school.*;
import com.yida.data.common.core.entity.system.Dept;
import com.yida.data.common.core.entity.system.EduApp;
import com.yida.data.common.core.entity.system.EduYidaApp;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.AppUtil;
import com.yida.data.common.core.utils.Asserts;
import com.yida.data.common.core.utils.WxPublicUtil;
import com.yida.data.common.core.utils.WxUtil;
import com.yida.data.common.service.CommonService;
import com.yida.data.job.feign.RemoteJobService;
import com.yida.data.school.index.mapper.EduSchoolHomeAnnouncementMapper;
import com.yida.data.school.news.mapper.EduSchoolManuscriptAuthorMapper;
import com.yida.data.school.news.mapper.EduSchoolManuscriptMapper;
import com.yida.data.school.news.service.EduSchoolManuscriptLabelService;
import com.yida.data.school.news.service.EduSchoolManuscriptService;
import com.yida.data.school.news.service.EduSchoolMatterService;
import com.yida.data.school.vo.index.ManuscriptPageInfoVO;
import com.yida.data.school.vo.news.AddManuscriptProgress;
import com.yida.data.system.feign.RemoteCommonService;
import javax.sound.sampled.Line.Info;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Service实现
 *
 * @author zhanghaijun
 * @date 2021-07-19 17:21:58
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class EduSchoolManuscriptServiceImpl extends ServiceImpl
    <EduSchoolManuscriptMapper, EduSchoolManuscript> implements EduSchoolManuscriptService {

    private final static String JOB_BEAN_NAME = "schoolManuscriptTask";
    private final static String JOB_METHOD_NAME = "publishManuscript";
    private static final Pattern BASE64_IMG_HEAD_PATTERN = Pattern
        .compile("\\s*data:([a-z]+\\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;" +
            "base64)?,([A-Za-z0-9+/]{4}){2,}(?:[A-Za-z0-9+=]{4})");
    private static final Pattern BASE64_IMG_PATTERN = Pattern.compile("([A-Za-z0-9+/]{4}){2,}(?:[A-Za-z0-9+=]{4})");
    private static final Pattern URL_IMG_PATTERN = Pattern.compile("http:\\/\\/((?!http:\\/\\/).)+?(\\.jpg|\\.png)");
    //    private static final Pattern URL_IMG_PATTERN_MINE = Pattern
//        .compile("http:\\/\\/((?!https:\\/\\/).)+?(\\/jpg|\\/png|\\/GIF|\\/gif|\\/PNG|\\/JPG)");
    private static final String URL_IMG_PATTERN_MINE = "((http|https|ftp):\\/\\/)?([\\w\\-]+\\.)+[\\w\\-]+(:[0-9]+)?(/[\\w \\-.~!*:@&=+$,/?#%]*)";
    //    private static final String LOCAL_IMG_URL_HRAD = "http://zbz.yd-data.com";
    private static final ExecutorService PUBLISH_THREAD_POOL =
        new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
    private final RemoteJobService remoteJobService;
    private final EduSchoolHomeAnnouncementMapper eduSchoolHomeAnnouncementMapper;
    private final EduSchoolManuscriptAuthorMapper eduSchoolManuscriptAuthorMapper;
    private final EduSchoolMatterService eduSchoolMatterService;
    private final WxUtil wxUtil;
    private final WxPublicUtil wxPublicUtil;
    private final CommonService commonService;
    private final RemoteCommonService remoteCommonService;
    private final EduSchoolManuscriptLabelService labelService;
    private final RedisService redisService;
    @Value("${febs.uploadUrl}")
    private String uploadUrl;

    @Override
    public IPage<ManuscriptPageInfoVO> listManuscriptPage(Page page, Long schoolId, String keyword,
        String author, String publishDate, Integer path,
        Long notShowId, Integer type, Integer status, Integer label) {
        IPage<ManuscriptPageInfoVO> ipage = baseMapper.listManuscriptPage(page, schoolId, keyword, author,
            publishDate, path, notShowId, type, status, label);
        for (ManuscriptPageInfoVO record : ipage.getRecords()) {
            String htmlStr = HtmlUtil.cleanHtmlTag(record.getContent());
            if (StrUtil.isBlank(htmlStr) || htmlStr.length() < 50) {
                record.setContent(htmlStr);
            } else {
                record.setContent(htmlStr.substring(0, 50));
            }
        }
        return ipage;
    }

    @Override
    public IPage<ManuscriptPageInfoVO> listpublicManuscriptPage(Page page, Long schoolId, Long notShowId, Integer type) {
        IPage<ManuscriptPageInfoVO> ipage = baseMapper.listPublicManuscriptPage(page, schoolId, notShowId, type);
        for (ManuscriptPageInfoVO record : ipage.getRecords()) {
            String htmlStr = HtmlUtil.cleanHtmlTag(record.getContent());
            if (StrUtil.isBlank(htmlStr) || htmlStr.length() < 50) {
                record.setContent(htmlStr);
            } else {
                record.setContent(htmlStr.substring(0, 50));
            }
        }
        return ipage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveManuscript(EduSchoolManuscript manuscript, String key) {
        redisService
            .hset(CachePrefixConstant.ADD_MANUSCRIPT_PROGRESS, key, AddManuscriptProgress.builder().key(key).finish(0).build());

        AddManuscriptProgress addManuscriptProgress = new AddManuscriptProgress();
        addManuscriptProgress.setKey(key);

        Dept school = commonService.getDept(manuscript.getSchoolId());
        if (StrUtil.isBlank(school.getWxPublicAppId()) && manuscript.getPublishPath()
            .contains(EduSchoolManuscript.WX_PUBLIC_PATH)) {
            addManuscriptProgress.setFinish(1);
            addManuscriptProgress.setErrorMsg("该学校未绑定微信公众号");
            redisService
                .hset(CachePrefixConstant.ADD_MANUSCRIPT_PROGRESS, key, addManuscriptProgress);

            throw new FebsException("该学校未绑定微信公众号");
//            Asserts.isFalse(StrUtil.isBlank(school.getWxPublicAppId()) && manuscript.getPublishPath()
//                .contains(EduSchoolManuscript.WX_PUBLIC_PATH), "该学校未绑定微信公众号");
        }

        if (school.getSchoolType().contains(Dept.TYPE_YIDA_APP)) {
            EduSchoolManuscriptAuthor author = eduSchoolManuscriptAuthorMapper.selectById(manuscript.getAuthorId());
            if (StrUtil.isBlank(author.getYidaAppUserId())) {
                addManuscriptProgress.setFinish(1);
                addManuscriptProgress.setErrorMsg("该作者未在易达APP中创建，请选择其他作者");
                redisService
                    .hset(CachePrefixConstant.ADD_MANUSCRIPT_PROGRESS, key, addManuscriptProgress);
                throw new FebsException("该作者未在易达APP中创建，请选择其他作者");
            }
//            Asserts.isTrue(StrUtil.isNotBlank(author.getYidaAppUserId()), "该作者未在易达APP中创建，请选择其他作者");
        }
        // 正则匹配base64为url
        Matcher matcher1 = BASE64_IMG_HEAD_PATTERN.matcher(manuscript.getContent());
        Matcher matcher2 = BASE64_IMG_PATTERN.matcher(manuscript.getContent());
        List<String> saveMatter = new ArrayList<>();
        if (matcher1.find()) {
            matcher2.find();
            for (int i = 0; i < matcher1.groupCount(); i++) {
                String base64 = matcher1.group(i);
                if (base64 == null || base64.length() < 22 || matcher2.group(i) == null || StrUtil
                    .isBlankIfStr(matcher2.group(i))) {
                    continue;
                }
                String base64Code = matcher2.group(i);
                String url = remoteCommonService.uploadImg(base64Code).getData();
                manuscript.setContent(manuscript.getContent().replace(base64, url));
            }
        }
        //正则匹配非本地媒资系统URL
        List<String> urlList = ReUtil.findAll(URL_IMG_PATTERN_MINE, manuscript.getContent(), 0, new ArrayList<>());
        log.info("匹配出的图片：【{}】", urlList);
        for (String url : urlList) {
            if (url == null || url.length() < 12 || url.contains(uploadUrl)) {
                continue;
            }
            String path = com.yida.data.common.core.utils.FileUtil.downloadToLocal(url);

            log.info("缓存本地图片地址：【{}】", path);
            //上传本地媒资系统
            String newUrl = com.yida.data.common.core.utils.FileUtil.uploadFileToMediaServer(uploadUrl, path);
            FileUtil.del(path);
            manuscript.setContent(manuscript.getContent().replace(url, newUrl));
        }
        // 上传图片到素材库
        if (CollUtil.isNotEmpty(saveMatter)) {
            List<EduSchoolMatter> list = eduSchoolMatterService.list(Wrappers.lambdaQuery(new EduSchoolMatter())
                .eq(EduSchoolMatter::getSchoolId, manuscript.getSchoolId())
                .in(EduSchoolMatter::getUrl, saveMatter)
                .select(EduSchoolMatter::getUrl));
            if (CollUtil.isNotEmpty(list)) {
                // 已存在的素材库
                List<String> collect = list.stream().map(x -> x.getUrl()).collect(Collectors.toList());
                saveMatter = saveMatter.stream().filter(x -> !collect.contains(x)).collect(Collectors.toList());
            }
            if (CollUtil.isNotEmpty(saveMatter)) {
                eduSchoolMatterService.saveMatterBatch(saveMatter, manuscript.getSchoolId(), 0);
            }
        }
        saveOrUpdate(manuscript);
        if (CollUtil.isNotEmpty(manuscript.getTags())) {
            List<Long> labels = manuscript.getTags();
            List<EduSchoolManuscriptLabel> list = new ArrayList<>();
            for (Long label : labels) {
                EduSchoolManuscriptLabel eduSchoolManuscriptLabel = new EduSchoolManuscriptLabel();
                eduSchoolManuscriptLabel.setLabel(label);
                eduSchoolManuscriptLabel.setManuscriptId(manuscript.getId());
                list.add(eduSchoolManuscriptLabel);
            }
            labelService.saveBatch(list);
        }
        if (Objects.isNull(manuscript.getContentType())) {
            // 如果没有则默认为文章类型
            manuscript.setContentType(1);
        }
        if (manuscript.getStatus() != 0 && manuscript.getContentType() != 2) {
            if (manuscript.getIsTiming() == 0) {
                // 定时发送
                StringBuilder cron = new StringBuilder();
                // cron表达式
                cron.append("0")
                    .append(" ")
                    .append(manuscript.getPublishDate().getMinute())
                    .append(" ")
                    .append(manuscript.getPublishDate().getHour())
                    .append(" ")
                    .append(manuscript.getPublishDate().getDayOfMonth())
                    .append(" ")
                    .append(manuscript.getPublishDate().getMonthValue())
                    .append(" ")
                    .append("?")
                    .append(" ")
                    .append(manuscript.getPublishDate().getYear());
                Job job = new Job();
                job.setBeanName(JOB_BEAN_NAME);
                job.setMethodName(JOB_METHOD_NAME);
                job.setParams(manuscript.getId().toString());
                job.setCronExpression(cron.toString());
                job.setRemark(String.format("学校稿件定时发送,name:[%s],id:[%d]", manuscript.getTitle(),
                    manuscript.getId()));
                job.setStatus(Job.ScheduleStatus.NORMAL.getValue());
                remoteJobService.saveJob(job);
            } else {
                // 立即发送
                manuscript.setPublishDate(LocalDateTime.now());
                updateById(manuscript);
                publishManuscript(manuscript);
            }
        }
        addManuscriptProgress.setFinish(1);
        redisService
            .hset(CachePrefixConstant.ADD_MANUSCRIPT_PROGRESS, key, addManuscriptProgress);
    }

    @Override
    @Transactional
    public void delManuscript(List<Long> ids) {
        Collection<EduSchoolManuscript> manuscriptList = listByIds(ids);
        for (EduSchoolManuscript manuscript : manuscriptList) {
            if (manuscript.getStatus() == 1 && manuscript.getIsTiming() == 0) {
                remoteJobService.delJobByMethod(JOB_BEAN_NAME, JOB_METHOD_NAME, manuscript.getId().toString());
            }
        }

        removeByIds(ids);
        List<String> manuscriptYidaIds = manuscriptList.stream().map(EduSchoolManuscript::getYidaAppMunascriptId)
            .filter(StrUtil::isNotBlank).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(manuscriptYidaIds)) {
            EduYidaApp yidaApp = commonService.getYidaApp(manuscriptList.toArray(new EduSchoolManuscript[0])[0].getSchoolId());
            AppUtil.manuscriptDel(manuscriptYidaIds, yidaApp);
        }
    }

    @Override
    public void cancelManuscriptBatch(Long id) {
        EduSchoolManuscript manuscript = getById(id);
        // 判断是否调用易达app撤回稿件接口
        if (Objects.nonNull(manuscript.getYidaAppMunascriptId())) {
            EduYidaApp yidaApp = commonService.getYidaApp(manuscript.getSchoolId());
            AppUtil.manuscriptDel(Collections.singletonList(manuscript.getYidaAppMunascriptId()), yidaApp);
            manuscript.setYidaAppMunascriptId("");
        }
        // 修改为撤回(待发送)状态
        manuscript.setStatus(1);
        updateById(manuscript);
    }

    @Override
    public void publishManuscript(Long id) {
        publishManuscript(getById(id));
    }

    @Override
    public List<EduSchoolManuscript> listManuscript(Long schoolId, String name, Integer path, Integer type, Integer status) {
        return baseMapper.listManuscript(schoolId, name, path, type, status);
    }

    @Override
    public EduSchoolManuscript getInfo(Long id) {
        return baseMapper.getInfo(id);
    }

    /**
     * 保存稿件到app
     */
    @Override
    public void saveManuscriptToYidaApp(EduSchoolManuscript manuscript, Dept school, EduYidaApp yidaApp) {
        if (yidaApp == null) {
            yidaApp = commonService.getYidaApp(manuscript.getSchoolId());
        }
        // 稿件发布到app中
        EduSchoolManuscriptAuthor author;
        if (manuscript.getAuthorId() == null) {
            author = eduSchoolManuscriptAuthorMapper
                .selectOne(Wrappers.<EduSchoolManuscriptAuthor>lambdaQuery()
                    .eq(EduSchoolManuscriptAuthor::getSchoolId, school.getDeptId())
                    .isNotNull(EduSchoolManuscriptAuthor::getYidaAppUserId)
                    .orderByAsc(EduSchoolManuscriptAuthor::getCreateDate)
                    .last("limit 1"));
        } else {
            author = eduSchoolManuscriptAuthorMapper.selectById(manuscript.getAuthorId());
        }
        Asserts.isNotNull(author.getYidaAppUserId(), "作者未在APP中创建");
        String manuscriptId = AppUtil.publishManuscript(manuscript, yidaApp, school.getYidaAppColId(), author.getYidaAppUserId());
        manuscript.setYidaAppMunascriptId(manuscriptId);
        saveOrUpdate(manuscript);
        // 公告同步的稿件放入公告区
        if (manuscript.getNoticeSchoolId() != null) {
            EduSchoolHomeAnnouncement announcement = eduSchoolHomeAnnouncementMapper
                .selectOne(Wrappers.lambdaQuery(new EduSchoolHomeAnnouncement())
                    .eq(EduSchoolHomeAnnouncement::getSchoolId, school.getDeptId()));
            if (StrUtil.isNotBlank(announcement.getYidaAppRegionId())) {
                AppUtil.regionSaveContent(announcement.getYidaAppRegionId(), manuscriptId, null,
                    school.getYidaAppColId(), yidaApp);
            }
        }
    }

    @Override
    public IPage<EduSchoolManuscript> findManuscriptByLabel(Page<EduSchoolManuscript> page, String label) {
        List<EduSchoolManuscriptLabel> labelList = labelService.list(Wrappers.query(new EduSchoolManuscriptLabel())
            .lambda().eq(EduSchoolManuscriptLabel::getLabel, label));
        List<Long> manuscriptId = new ArrayList<>();
        for (EduSchoolManuscriptLabel item : labelList) {
            manuscriptId.add(item.getManuscriptId());
        }
        if (CollectionUtil.isEmpty(manuscriptId)) {
            return new Page<>();
        }

        return page(page, Wrappers.query(new EduSchoolManuscript()).lambda().eq(EduSchoolManuscript::getType, 2)
            .in(EduSchoolManuscript::getId, manuscriptId));
    }

    /**
     * 发布稿件
     *
     * @param manuscript
     */
    private void publishManuscript(EduSchoolManuscript manuscript) {
        PUBLISH_THREAD_POOL.execute(() -> {
            Dept school = commonService.getDept(manuscript.getSchoolId());
            EduSchoolManuscriptAuthor author = eduSchoolManuscriptAuthorMapper.selectById(manuscript.getAuthorId());
            String publishPath = manuscript.getPublishPath();
            EduYidaApp yidaApp = commonService.getYidaApp(manuscript.getSchoolId());
            if (author != null) {
                manuscript.setAuthorName(author.getName());
            }
            // 安全教育稿件不发送
            if (manuscript.getType() == null || manuscript.getType() == 0 || manuscript.getType() == 2) {
                // 发布微信公众号文章
                if (publishPath.contains(EduSchoolManuscript.WX_PUBLIC_PATH)) {
                    wxPublishPublic(manuscript, school);
                }
                // 发布公告到易达app
                if (school.getSchoolType().contains(Dept.TYPE_YIDA_APP)) {
                    saveManuscriptToYidaApp(manuscript, school, yidaApp);
                }
                // 稿件不是从学校通知同步且学校绑定企业微信才发送微信通知
                if (manuscript.getStatus() != 3 && school.getSchoolType().contains(Dept.TYPE_QYWX)) {
                    // 发送学校通知
                    wxPublishNotice(manuscript);
                }
            }
            // 更新发送状态
            if (manuscript.getStatus() != 3) {
                manuscript.setStatus(2);
                updateById(manuscript);
            }
        });
    }

    private void wxPublishNotice(EduSchoolManuscript manuscript) {
        EduApp eduApp = commonService.getAppByCodeAndSchool(AppConstant.INDEX_PARENT, null, manuscript.getSchoolId());
        String accessToken = wxUtil.getAccessToken(eduApp.getWxCorpId(), eduApp.getWxSecret());
        String contentText = HtmlUtil.cleanHtmlTag(manuscript.getContent());
        MpNewsSchoolNotice notice = new MpNewsSchoolNotice();
        Article article = new Article();
        article.setTitle(manuscript.getTitle());
        // 封面图
        if (manuscript.getCoverType() != 0) {
            try {
                String path =
                    com.yida.data.common.core.utils.FileUtil
                        .downloadCompressBase64Img(manuscript.getCoverUrl().split(",")[0], 2048L);
                File file = new File(path);
                article.setThumb_media_id(wxUtil.uploadTempFile(accessToken, "image", file));
                FileUtil.del(path);
            } catch (IOException e) {
                e.printStackTrace();
                throw new FebsException("缩略图压缩失败");
            }
        }
        article.setAuthor(manuscript.getAuthorName());
        article.setContent(manuscript.getContent());
        if (contentText.length() > 54) {
            article.setDigest(contentText.substring(0, 54));
        } else {
            article.setDigest(contentText);
        }
        News news = new News(Arrays.asList(article));
        notice.setMpnews(news);
        // 发送给所有人
        notice.setToall(1);
        notice.setAgentid(eduApp.getWxAgentId());
        wxUtil.pushSchoolNotice(accessToken, notice);
    }

    private void wxPublishPublic(EduSchoolManuscript manuscript, Dept school) {
        String token = wxPublicUtil.getAccessToken(school.getWxPublicAppId(), school.getWxPublicSecret());
        EduSchoolManuscriptAuthor author = eduSchoolManuscriptAuthorMapper.selectById(manuscript.getAuthorId());
        Article article = new Article();
        article.setTitle(manuscript.getTitle());
        if (manuscript.getCoverType() != 0) {
            try {
                String path = com.yida.data.common.core.utils.FileUtil
                    .downloadCompressBase64Img(manuscript.getCoverUrl().split(",")[0], 64L);
                File file = new File(path);
                article.setThumb_media_id(wxPublicUtil.uploadTempMaterial("thumb", file, token));
                FileUtil.del(path);
            } catch (IOException e) {
                e.printStackTrace();
                throw new FebsException("缩略图压缩失败");
            }
        }
        article.setAuthor(author.getName());
        String content = manuscript.getContent();
        // 更换上传正文图片到微信公众号
        Matcher matcher1 = URL_IMG_PATTERN.matcher(content);
        if (matcher1.find()) {
            for (int i = 0; i < matcher1.groupCount(); i++) {
                String url = matcher1.group(i);
                if (url == null || url.length() < 12) {
                    continue;
                }
                String path = com.yida.data.common.core.utils.FileUtil.downloadToLocal(url);
                String newUrl = wxPublicUtil.uploadNewsImg(new File(path), token);
                FileUtil.del(path);
                content.replace(url, newUrl);
            }
        }
        article.setContent(content);
        News news = new News(Arrays.asList(article));
        String mediaId = wxPublicUtil.uploadNews(news, token);
        wxPublicUtil.sendNews(mediaId, token);
    }

    private Integer judgeType(String suffix) {
        Integer type = null;
        switch (suffix.toLowerCase(Locale.ROOT)) {
            case "jpg":
                type = 0;
                break;
            case "png":
                type = 0;
                break;
            case "gif":
                type = 0;
                break;
            case "jpeg":
                type = 0;
                break;
            case "mp3":
                type = 1;
                break;
            case "wma":
                type = 1;
                break;
            case "wav":
                type = 1;
                break;
            case "amr":
                type = 1;
                break;
            case "mp4":
                type = 2;
                break;
        }
        return type;
    }
}
