package com.ningdatech.kqapi.admin.manage;

import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.model.IdVo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.ValidUtil;
import com.ningdatech.kqapi.admin.mapper.ZoneMapper;
import com.ningdatech.kqapi.admin.model.dto.MatterImportDTO;
import com.ningdatech.kqapi.admin.model.entity.Window;
import com.ningdatech.kqapi.admin.model.entity.Zone;
import com.ningdatech.kqapi.admin.model.enumerization.HandleChannelEnum;
import com.ningdatech.kqapi.admin.model.req.MatterListReq;
import com.ningdatech.kqapi.admin.model.vo.MatterDetailDTO;
import com.ningdatech.kqapi.admin.model.vo.MatterListVO;
import com.ningdatech.kqapi.admin.service.IWindowService;
import com.ningdatech.kqapi.common.enumeration.IEnum;
import com.ningdatech.kqapi.common.exception.BizException;
import com.ningdatech.kqapi.zzsfw.enumeration.ItemTypeEnum;
import com.ningdatech.kqapi.zzsfw.model.entity.KqZzsfwMenu;
import com.ningdatech.kqapi.zzsfw.service.IKqZzsfwMenuService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * <p>
 * MatterManage
 * </p>
 *
 * @author WendyYang
 * @since 15:41 2024/4/20
 */
@Component
@RequiredArgsConstructor
public class AdminMatterManage {

    private static final Logger log = LoggerFactory.getLogger(AdminMatterManage.class);
    private final IKqZzsfwMenuService zzsfwMenuService;
    private final ZoneMapper zoneMapper;
    private final IWindowService windowService;

    public PageVo<MatterListVO> pageMatter(MatterListReq req) {
        Wrapper<KqZzsfwMenu> query = Wrappers.lambdaQuery(KqZzsfwMenu.class)
                .like(StrUtil.isNotBlank(req.getMatterName()), KqZzsfwMenu::getItemName, req.getMatterName())
                .eq(req.getZoneId() != null, KqZzsfwMenu::getZoneId, req.getZoneId())
                .eq(req.getWindowId() != null, KqZzsfwMenu::getWindowId, req.getWindowId())
                .eq(req.getMatterType() != null, KqZzsfwMenu::getType, req.getMatterType())
                .eq(req.getDepartment() != null, KqZzsfwMenu::getDepartment, req.getDepartment())
                .orderByDesc(KqZzsfwMenu::getCreateOn);
        Page<KqZzsfwMenu> page = zzsfwMenuService.page(req.page(), query);
        if (page.getTotal() == 0) {
            return PageVo.empty();
        }
        List<MatterListVO> data = CollUtils.convert(page.getRecords(),
                w -> MatterListVO.builder()
                        .id(w.getId())
                        .matterName(w.getItemName())
                        .matterType(w.getType())
                        .department(w.getDepartment())
                        .zoneName(w.getZoneName())
                        .windowName(w.getWindow())
                        .build());
        return PageVo.of(data, page.getTotal());
    }

    public void del(IdVo<Long> id) {
        zzsfwMenuService.removeById(id.getId());
    }

    public MatterDetailDTO detail(Long id) {
        KqZzsfwMenu matter = zzsfwMenuService.getById(id);
        if (matter == null) {
            return null;
        }
        return MatterDetailDTO.builder()
                .id(matter.getId())
                .free(matter.getFree())
                .onlinePay(matter.getOnlinePay())
                .hasUrl(matter.getHasUrl())
                .matterType(matter.getType())
                .matterName(matter.getItemName())
                .department(matter.getDepartment())
                .handleAddress(matter.getHandleAddress())
                .handleChannel(matter.getHandleChannel())
                .handleTime(matter.getHandleTime())
                .implSubjectNature(matter.getImplSubjectNature())
                .publicityMaterials(matter.getPublicityMaterials())
                .sceneDesc(matter.getSceneDesc())
                .serviceContent(matter.getServiceContent())
                .serviceObject(matter.getServiceObject())
                .exerciseLevel(matter.getExerciseLevel())
                .serviceProcess(matter.getServiceProcess())
                .sort(matter.getSort())
                .telephone(matter.getTelephone())
                .windowName(matter.getWindow())
                .windowId(matter.getWindowId())
                .zoneId(matter.getZoneId())
                .zoneName(matter.getZoneName())
                .webApplyUrl(matter.getWebapplyurl())
                .build();
    }

    public void saveOrUpdate(MatterDetailDTO req) {
        KqZzsfwMenu matter = new KqZzsfwMenu();
        matter.setId(req.getId());
        matter.setFree(req.getFree());
        matter.setOnlinePay(req.getOnlinePay());
        matter.setHasUrl(req.getHasUrl());
        matter.setType(req.getMatterType());
        matter.setItemName(req.getMatterName());
        matter.setDepartment(req.getDepartment());
        matter.setHandleAddress(req.getHandleAddress());
        matter.setHandleChannel(req.getHandleChannel());
        matter.setHandleTime(req.getHandleTime());
        matter.setImplSubjectNature(req.getImplSubjectNature());
        matter.setExerciseLevel(req.getExerciseLevel());
        matter.setPublicityMaterials(req.getPublicityMaterials());
        matter.setSceneDesc(req.getSceneDesc());
        matter.setServiceContent(req.getServiceContent());
        matter.setServiceObject(req.getServiceObject());
        matter.setServiceProcess(req.getServiceProcess());
        matter.setSort(req.getSort());
        matter.setTelephone(req.getTelephone());
        matter.setWindow(req.getWindowName());
        matter.setWindowId(req.getWindowId());
        matter.setZoneId(req.getZoneId());
        matter.setZoneName(req.getZoneName());
        matter.setWebapplyurl(req.getWebApplyUrl());
        zzsfwMenuService.saveOrUpdate(matter);
    }

    public void importMatter(MultipartFile file, Integer matterType) {
        ItemTypeEnum type = IEnum.getNoNull(ItemTypeEnum.class, matterType);
        Assert.isTrue(!type.equals(ItemTypeEnum.SPECIAL), "不支持的导入类型");
        try (InputStream is = file.getInputStream()) {
            List<MatterImportDTO> imports = EasyExcel.read(is, buildImportEventListener(type))
                    .headRowNumber(1)
                    .head(MatterImportDTO.class)
                    .excelType(ExcelTypeEnum.XLSX)
                    .ignoreEmptyRow(true)
                    .autoCloseStream(true)
                    .doReadAllSync();
            Map<String, List<MatterImportDTO>> mattersByZone = CollUtils.group(imports, MatterImportDTO::getZoneName);
            Wrapper<Zone> zoneQuery = Wrappers.lambdaQuery(Zone.class)
                    .in(Zone::getZoneName, mattersByZone.keySet());
            List<Zone> zones = zoneMapper.selectList(zoneQuery);
            if (zones.size() != mattersByZone.size()) {
                throw BizException.wrap("导入失败：服务专区不存在");
            }
            Map<String, Zone> zoneMap = CollUtils.listToMap(zones, Zone::getZoneName);
            List<KqZzsfwMenu> newMatters = new ArrayList<>();
            mattersByZone.forEach((zoneName, matters) -> {
                Zone zone = zoneMap.get(zoneName);
                Assert.notNull(zone, "服务专区不存在");
                Map<String, List<MatterImportDTO>> mattersByWin = CollUtils.group(matters, MatterImportDTO::getWindowName);
                Wrapper<Window> windowQuery = Wrappers.lambdaQuery(Window.class)
                        .eq(Window::getZoneId, zone.getId())
                        .in(Window::getWindowName, mattersByWin.keySet());
                List<Window> windows = windowService.list(windowQuery);
                if (windows.size() != mattersByWin.size()) {
                    throw BizException.wrap("导入失败：服务窗口不存在");
                }
                Map<String, Window> windowMap = CollUtils.listToMap(windows, Window::getWindowName);
                for (MatterImportDTO matter : matters) {
                    Window window = windowMap.get(matter.getWindowName());
                    Assert.notNull(window, "服务窗口不存在");
                    KqZzsfwMenu menu = new KqZzsfwMenu();
                    menu.setWindowId(window.getId());
                    menu.setZoneId(zone.getId());
                    menu.setItemName(matter.getMatterName());
                    menu.setWebapplyurl(matter.getWebApplyUrl());
                    menu.setWindow(window.getWindowName());
                    menu.setZoneName(zone.getZoneName());
                    menu.setDepartment(matter.getDepartment());
                    if (matter.getHandleChannel() != null) {
                        menu.setHandleChannel(matter.getHandleChannelEnum().getCode());
                    }
                    menu.setServiceContent(matter.getServiceContent());
                    menu.setServiceProcess(matter.getServiceProcess());
                    menu.setTelephone(matter.getTelephone());
                    menu.setSort(99);
                    menu.setType(type.getCode());
                    newMatters.add(menu);
                }
            });
            zzsfwMenuService.saveBatch(newMatters);
        } catch (IOException e) {
            log.error("导入失败：{}", e.getMessage());
            throw BizException.wrap("政务服务事项导入失败");
        }
    }

    private static AnalysisEventListener<MatterImportDTO> buildImportEventListener(ItemTypeEnum type) {
        return new AnalysisEventListener<MatterImportDTO>() {
            @Override
            public void invoke(MatterImportDTO data, AnalysisContext context) {
                String errMsg = ValidUtil.validFast(data, type.getParamValidGroup());
                if (errMsg != null) {
                    throw BizException.wrap(errMsg);
                }
                if (StrUtil.isNotBlank(data.getHandleChannel())) {
                    Optional<HandleChannelEnum> channel = IEnum.getByDesc(HandleChannelEnum.class, data.getHandleChannel());
                    if (channel.isPresent()) {
                        data.setHandleChannelEnum(channel.get());
                    } else {
                        throw BizException.wrap("不支持的办理渠道");
                    }
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                // do nothing
            }
        };
    }

    public void downloadMatterImportTemplate(HttpServletResponse response, Integer matterType) {
        ItemTypeEnum matterTypeEnum = ItemTypeEnum.match(matterType);
        Assert.notNull(matterTypeEnum, "参数缺失");
        String templatePath = matterTypeEnum.getImportTemplatePath();
        Assert.notEmpty(templatePath, "不支持的事项模版类型");
        ClassPathResource resource = new ClassPathResource(templatePath);
        try (ServletOutputStream os = response.getOutputStream()) {
            response.setContentType(ExcelUtil.XLSX_CONTENT_TYPE);
            String fileName = URLEncoder.encode(resource.getName(), "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            os.write(resource.readBytes());
            os.flush();
        } catch (IOException e) {
            log.error("下载失败：{}", e.getMessage());
            throw BizException.wrap("事项配置模板下载失败");
        }
    }

}
