package com.blueland.common.core.service.impl;


import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.blueland.common.core.domain.International;
import com.blueland.common.core.domain.export.InternationalExport;
import com.blueland.common.core.handler.ExcelDicHandler;
import com.blueland.common.core.utils.RedisUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.blueland.common.core.utils.TranslateUtil;
import com.blueland.common.core.utils.UserUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import com.blueland.common.core.service.InternationalService;
import com.blueland.common.core.mapper.InternationalMapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.blueland.common.core.domain.table.InternationalTableDef.INTERNATIONAL;

/**
 * 国际化 服务层实现。
 *
 * @author mybatis-flex-helper automatic generation
 * @since 1.0
 */
@Service
public class InternationalServiceImpl extends ServiceImpl<InternationalMapper, International> implements InternationalService {


    /**
     * 插入
     *
     * @param international 国际
     * @return boolean
     */
    @Override
    public boolean insert(International international) {
        TranslateUtil.translate(new ArrayList<>(), "", "");
        String code = international.getCode();
        String lang = international.getLang();
        Assert.isFalse(exist(code, lang), StrUtil.format("编码【{}】，语言【{}】已经存在", code, lang));
        international.setTenantId(UserUtils.getTenantId());
        boolean flag = save(international);
        if (flag) {
            RedisUtil.hashSet(international);
        }
        return flag;
    }

    /**
     * 是否存在
     *
     * @param code 编码
     * @param lang 语言
     * @return boolean
     */
    @Override
    public boolean exist(String code, String lang) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(INTERNATIONAL.CODE.eq(code))
                .and(INTERNATIONAL.LANG.eq(lang))
                .and(INTERNATIONAL.TENANT_ID.eq(UserUtils.getTenantId()));
        return exists(queryWrapper);
    }

    @Override
    public List<International> listAll(International international) {
        QueryWrapper wrapper = QueryWrapper.create()
                .where(INTERNATIONAL.TENANT_ID.eq(UserUtils.getTenantId()))
                .and(INTERNATIONAL.CODE.like(international.getCode(), StrUtil::isNotBlank))
                .and(INTERNATIONAL.NAME.like(international.getName(), StrUtil::isNotBlank))
                .and(INTERNATIONAL.LANG.like(UserUtils.getLang()))
                .and(INTERNATIONAL.TYPE.like(international.getType(), StrUtil::isNotBlank))
                .orderBy(INTERNATIONAL.CREATE_TIME.desc());
        return list(wrapper);
    }

    /**
     * 根据语言查询
     *
     * @param lang     语言
     * @param tenantId
     * @return {@link List}<{@link International}>
     */
    @Override
    public List<International> listByLang(String lang, String tenantId, String type) {
        Assert.notBlank(lang, "语言不能为空");
        QueryWrapper wrapper = QueryWrapper.create()
                .where(INTERNATIONAL.TENANT_ID.eq(tenantId, StrUtil::isNotBlank))
                .and(INTERNATIONAL.TYPE.eq(type, StrUtil::isNotBlank))
                .and(INTERNATIONAL.LANG.eq(lang));
        return list(wrapper);
    }

    @Override
    public Page<List<International>> pageList(International international) {
        QueryWrapper wrapper = QueryWrapper.create()
                .where(INTERNATIONAL.TENANT_ID.eq(UserUtils.getTenantIdDefault(), StrUtil::isNotBlank))
                .and(INTERNATIONAL.CODE.like(international.getCode(), StrUtil::isNotBlank))
                .and(INTERNATIONAL.NAME.like(international.getName(), StrUtil::isNotBlank))
                .and(INTERNATIONAL.LANG.like(international.getLang(), StrUtil::isNotBlank))
                .and(INTERNATIONAL.TYPE.like(international.getType(), StrUtil::isNotBlank))
                .orderBy(INTERNATIONAL.CREATE_TIME.desc());
        return page(international.getPage(), wrapper);
    }

    /**
     * 一键复制
     *
     * @param targetLang 目标语言
     * @param sourceLang 源语言
     * @param tenantId
     * @return boolean
     */
    @Override
    public boolean copy(String targetLang, String sourceLang, String tenantId) {
        List<International> sourceLangList = listByLang(sourceLang, tenantId, "");

        List<String> nameList = sourceLangList.stream()
                .map(International::getName)
                .collect(Collectors.toList());

        Map<String, String> tranfer = new ConcurrentHashMap<>();
        ArrayList<CompletableFuture> futures = new ArrayList<>();
        for (List<String> list : CollUtil.split(nameList, 400)) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                tranfer.putAll(TranslateUtil.translate(list, sourceLang, targetLang));
            });
            futures.add(future);
        }
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        List<International> targetLangList = sourceLangList.stream()
                .map(el -> {
                    International international = new International();
                    international.setCode(el.getCode());
                    international.setName(tranfer.get(el.getName()));
                    international.setLang(targetLang);
                    international.setType(el.getType());
                    international.setTenantId(UserUtils.getTenantId());
                    return international;
                })
                .collect(Collectors.toList());

        boolean flag = saveBatch(targetLangList);
        if (flag) {
            RedisUtil.hashMultiSet(targetLang, targetLangList);
        }
        return flag;
    }

    @Override
    public boolean updateInternational(International international) {
        boolean flag = updateById(international);
        if (flag) {
            RedisUtil.hashSet(international);
        }
        return flag;
    }

    @Override
    public boolean deleteById(String id) {
        International international = getById(id);
        boolean flag = removeById(id);
        if (flag) {
            RedisUtil.hashRemvoe(international);
        }
        return flag;
    }

    @Override
    public void dowloadTemplate() {
        try {
            ExportParams exportParams = new ExportParams("国际化导入导出模板", "国际化");
            exportParams.setDictHandler(new ExcelDicHandler());
            exportParams.setCreateHeadRows(true);
            List<InternationalExport> list = new ArrayList<>();
            Workbook workbook = ExcelExportUtil.exportExcel(exportParams, InternationalExport.class, list);
            HttpServletResponse response = UserUtils.getResponse();
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(("template.xlsx").getBytes(), "ISO-8859-1"));
            ServletOutputStream sos = response.getOutputStream();
            workbook.write(sos);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Boolean importList(MultipartFile file) {
        ImportParams params = new ImportParams();
        params.setTitleRows(1);
        params.setDictHandler(new ExcelDicHandler());
        List<InternationalExport> importList;
        try {
            importList = ExcelImportUtil.importExcel(file.getInputStream(), InternationalExport.class, params);

            // 校验数据完整性
            for (InternationalExport export : importList) {
                Assert.notBlank(export.getCode(), "编码不能为空");
                Assert.notBlank(export.getName(), "名称不能为空");
                Assert.notBlank(export.getLang(), "语言不能为空");
                Assert.notBlank(export.getType(), "类型不能为空");
            }

            // 校验数据是否已经存在
            Map<String, International> map = list(importList);
            if (CollUtil.isNotEmpty(map)) {
                for (InternationalExport export : importList) {
                    Assert.isFalse(map.containsKey(export.getCode() + export.getLang() + export.getType()),
                            StrUtil.format("编码【{}】语言【{}】类型【{}】已存在", export.getCode(), export.getLang(), export.getType()));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("导入失败");
        }
        List<International> list = BeanUtil.copyToList(importList, International.class);
        boolean flag = saveBatch(list);
        if (flag) {
            list.stream()
                    .collect(Collectors.groupingBy(International::getLang))
                    .forEach((k, v) -> {
                        RedisUtil.hashMultiSet(k, v);
                    });
        }
        return flag;
    }

    @Override
    public Map<String, Map<String, String>> findLangPackage(String type) {
        List<International> list = listByLang(UserUtils.getLang(), UserUtils.getTenantId(), type);
        return list.stream()
                .collect(Collectors.groupingBy(el -> StrUtil.subBefore(el.getCode(),
                        ".",
                        false), Collectors.toMap(el -> StrUtil.subAfter(el.getCode(),
                        ".",
                        false), International::getName)));
    }

    public static void main(String[] args) {
        International international = new International();
        international.setCode("client.msg");
        international.setName("客户端");
        List<International> list = Arrays.asList(international);
        Map<String, Map<String, String>> collect = list.stream()
                .collect(Collectors.groupingBy(el -> StrUtil.subBefore(el.getCode(),
                        ".",
                        false), Collectors.toMap(el -> StrUtil.subAfter(el.getCode(),
                        ".",
                        false), International::getName)));
        System.out.println(collect);
    }

    @Override
    public Boolean tenantIdCopy(String sourceLang, String tenantId) {
        List<International> list = listByLang(sourceLang, tenantId, "").stream()
                .map(el -> {
                    el.setTenantId(UserUtils.getTenantId());
                    el.setId(null);
                    el.setCreateBy(null);
                    el.setCreateTime(null);
                    el.setUpdateBy(null);
                    el.setUpdateTime(null);
                    return el;
                })
                .collect(Collectors.toList());
        boolean flag = saveBatch(list);
        if (flag) {
            RedisUtil.hashMultiSet(sourceLang, list);
        }
        return flag;
    }


    public Map<String, International> list(List<InternationalExport> list) {
        QueryWrapper wrapper = QueryWrapper.create();
        for (InternationalExport international : list) {
            international.setTenantId(UserUtils.getTenantId());
            wrapper.or(INTERNATIONAL.CODE.eq(international.getCode())
                    .and(INTERNATIONAL.NAME.eq(international.getName()))
                    .and(INTERNATIONAL.TYPE.eq(international.getType()))
                    .and(INTERNATIONAL.LANG.eq(international.getLang()))
                    .and(INTERNATIONAL.TENANT_ID.eq(UserUtils.getTenantId()))
            );
        }
        List<International> dbList = list(wrapper);
        if (CollUtil.isEmpty(dbList)) {
            return new HashMap<>();
        }
        return dbList
                .stream()
                .collect(Collectors.toMap(el -> el.getCode() + el.getLang() + el.getType(), Function.identity()));
    }

}