package thyyxxk.webserver.service.highreport;

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import thyyxxk.webserver.config.exception.ExceptionEnum;
import thyyxxk.webserver.dao.his.highreport.HighReportDao;
import thyyxxk.webserver.entity.HeadInfo;
import thyyxxk.webserver.entity.ResultVo;
import thyyxxk.webserver.entity.highreport.HighReportDto;
import thyyxxk.webserver.entity.highreport.ReportBaseInfo;
import thyyxxk.webserver.entity.highreport.ReportType;
import thyyxxk.webserver.entity.highreport.TableDisplayInfo;
import thyyxxk.webserver.entity.highreport.TableGroupInfo;
import thyyxxk.webserver.entity.highreport.XYBean;
import thyyxxk.webserver.utils.ExcelUtil;
import thyyxxk.webserver.utils.ReportUtil;
import thyyxxk.webserver.utils.ResultVoUtil;
import thyyxxk.webserver.utils.StringUtil;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description: 报表统一门户入口
 * @Author: hsh
 * @CreateTime: 2022-10-31  17:04
 * @Version: 1.0
 */
@Service
@Slf4j
public class HighReportService {

    private final HighReportDao dao;

    @Autowired
    public HighReportService(HighReportDao dao) {
        this.dao = dao;
    }

    public ResultVo<Map<String, Object>> selectReportPortal(HighReportDto dto) {
        Map<String, Object> map = new HashMap<>();
        String reportType = dto.getReportType();
        String menuId = dto.getMenuId();
        String title = ReportType.find(reportType);
        List<ReportBaseInfo> reportList = dao.selectReportPortal(reportType, menuId);

        // 计算指标或者报表的总览结果
        for(ReportBaseInfo info : reportList){
            // 没有办法需要一条一条执行sql
            String sql = ReportUtil.callSqlFormat(dto, info.getBaseSql(), info.getGatherSql(), null, null, null);
            // 查询总览数据结果
            String overview = dao.selectReportPortalOverview(info.getReportId(), info.getMenuId(), info.getReportType(), sql);
            info.setOverview(overview);
        }

        map.put("title", title);
        map.put("data", reportList);
        return ResultVoUtil.success(map);
    }

    /**
     * @description: 单个报表查询页面数据展示以及数据结果信息
     * @author: hsh
     * @date: 2022/11/4 9:10
     * @param: [dto]
     * @return: ResultVo<ReportBaseInfo>
     **/
    public ResultVo<ReportBaseInfo> selectReportPortalMenu(HighReportDto dto) {
        String reportId = dto.getReportId();
        String menuId = dto.getMenuId();
        String reportType = dto.getReportType();
        String type = dto.getType();

        if(null == reportId){
            return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST);
        }

        ReportBaseInfo report = dao.selectReportPortalMenu(reportId, menuId, reportType);
        if(null == report){
            log.info("没有配置报表参数");
            return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST);
        }

        List<TableDisplayInfo> tableInfoList = dao.selectTableDisplayInfoByReportId(reportId);
        // 没有配置病人详情展示数据的话默认查询公共展示数据信息
        String level = report.getLevel();
        if(tableInfoList.isEmpty()){
            if(ReportType.GROUP_KS.getCode().equals(level)){
                // 固定分组类型是科室
                tableInfoList = dao.selectTableDisplayInfoByReportId("comm_report");
            } else if(ReportType.GROUP_ZB.getCode().equals(level)){
                // 固定分组类型是指标
                tableInfoList = dao.selectTableDisplayInfoByReportId("zb_report");
            }

        }
        report.setTableDisplays(tableInfoList);

        // 根据配置的sql查询数据结果
        // 组装查询sql
        if(ReportType.ONE.getCode().equals(type)){
            String baseSql = report.getBaseSql();
            String gatherSql = report.getGatherSql();
            String sortSql = report.getReportSort();
            String sql = ReportUtil.callSqlFormat(dto, baseSql, gatherSql, null, null, sortSql);
            log.info("查询sql: {}", sql);
            // 查询结果
            List<Map<String, Object>> dataList = dao.selectReportPortalData(reportId, menuId, reportType, sql);
            report.setDataList(dataList);
        } else {
            // 固定分组类型是科室
            if(ReportType.GROUP_KS.getCode().equals(level)){
                reportId = ReportType.GROUP_KS.getCode();
            } else if(ReportType.GROUP_ZB.getCode().equals(level)){
                // 固定分组类型是指标
                reportId = ReportType.GROUP_ZB.getCode();
            }
            TableGroupInfo info = dao.selectTableGroupInfoByLevel(reportId, level);
            String sql = ReportUtil.callSqlFormatByGroup(dto, report, info, true);
            log.info("查询sql: {}", sql);
            // 查询结果
            List<Map<String, Object>> dataList = dao.selectReportPortalData(report.getReportId(), menuId, reportType, sql);
            report.setDataList(dataList);
        }
        return ResultVoUtil.success(report);
    }

    /**
     * @description: 报表数据导出
     * @author: hsh
     * @date: 2022/11/7 10:26
     * @param: [response, dto]
     * @return: ResultVo<String>
     **/
    public ResultVo<Map<String, Object>> exportReportPortalData(HttpServletResponse response, HighReportDto dto){
        Map<String, Object> map = new HashMap<>();
        ResultVo<ReportBaseInfo> resultVo = selectReportPortalMenu(dto);
        ReportBaseInfo report = resultVo.getData();
        if(null == report || report.getDataList().isEmpty()){
            map.put("code", 1);
            map.put("massage", "查询数据为空，导出失败！");
            return ResultVoUtil.success(map);
        }

        // 数据
        List<Map<String, Object>> list = report.getDataList();
        // 合计
        if(null != report.getTotal() && !report.getTotal().isEmpty()){
            Map<String, Object> total = report.getTotal();
            list.add(total);
        }

        // 导出标题
        String exportName = dto.getExportName();

        // 组装表头
        List<HeadInfo> headInfoList = calHeadList(report.getTableDisplays());
        List<HeadInfo> sortHeadInfo = headInfoList.stream().sorted(Comparator.comparing(HeadInfo::getSort)).collect(Collectors.toList());

        // 导出
        ExcelUtil.exportExcelReport(response, list, sortHeadInfo, exportName);

        map.put("code", 0);
        map.put("massage", "导出成功！");
        return ResultVoUtil.success(map);
    }

    /**
     * @description: 组装导出的表头
     * @author: hsh
     * @date: 2022/11/7 11:17
     * @param: [tableDisplays]
     * @return: List<HeadInfo>
     **/
    private @NotNull List<HeadInfo> calHeadList(@NotNull List<TableDisplayInfo> tableDisplays) {
        List<HeadInfo> headList = new ArrayList<>();
        Map<String, List<TableDisplayInfo>> map = tableDisplays.stream().collect(
                Collectors.groupingBy(TableDisplayInfo::getPid, Collectors.toList()));

        for(Map.Entry<String, List<TableDisplayInfo>> entry : map.entrySet()){
            String pid = entry.getKey();
            List<TableDisplayInfo> list = entry.getValue();
            List<HeadInfo> headInfoList = new ArrayList<>();
            List<HeadInfo> headInfoChildrenList = new ArrayList<>();
            for(TableDisplayInfo table : list){
                String id = table.getId();
                /*
                * 重要：一定要维护父id！！！一定要维护父id！！！一定要维护父id！！！
                * 表格行父id是独一无二的一个，证明是单独一个表格个列，没有多表头，放在同一个表头对象中
                * 如果表格行id与表格行父id是一样的话，表示这条记录是此表格行的父级行，其余的都是表格的子级行
                *（例如二级表头pid与id父子关系：1 1，1 2，1 3，1 4 其中1 1是父级表头，1 2、1 3、1 4都是它的子级表头）
                */
                if(StringUtil.notBlank(pid) && !id.equals(pid)){
                    HeadInfo headInfoChild = new HeadInfo();
                    headInfoChild.setDisplay(table.getLabel());
                    headInfoChild.setName(table.getProp());
                    headInfoChild.setSort(table.getSort());
                    headInfoChildrenList.add(headInfoChild);
                } else {
                    HeadInfo headInfo = new HeadInfo();
                    headInfo.setDisplay(table.getLabel());
                    headInfo.setName(table.getProp());
                    headInfo.setSort(table.getSort());
                    headInfoList.add(headInfo);
                }
            }
            if(headInfoChildrenList.size() > 0){
                // 存在子节点的父节点只允许有一个
                headInfoList.get(0).setColumns(headInfoChildrenList);
            }
            headList.addAll(headInfoList);
        }
        return headList;
    }

    public ResultVo<Map<String, Object>> selectReportGroup(HighReportDto dto){
        Map<String, Object> map = new HashMap<>();
        String reportId = dto.getReportId();
        String reportType = dto.getReportType();
        String menuId = dto.getMenuId();

        ReportBaseInfo report = dao.selectReportPortalMenu(reportId, menuId, reportType);
        String level = report.getLevel();
        // 固定分组类型是科室
        if(ReportType.GROUP_KS.getCode().equals(level)){
            reportId = ReportType.GROUP_KS.getCode();
        }
        TableGroupInfo info = dao.selectTableGroupInfoByLevel(reportId, level);

        String title = ReportType.find(report.getReportType());
        // 这里目前应该分两种模式： 一种是以图形展示为主的报表; 一种是以表格展示为主的报表.
        // 第一种：
        String sql = ReportUtil.callSqlFormatByGroup(dto, report, info, false);
        log.info("查询sql: {}", sql);
        List<XYBean> beanList = dao.selectReportPortalXYBean(report.getReportId(), menuId, reportType, sql);

        map.put("title", title);
        map.put("data", beanList);
        map.put("tableInfo", info);
        return ResultVoUtil.success(map);
    }

    /**
     * @Description 查询报表信息
     * @Author hsh
     * @param dto 报表id
     * @return ReportBaseInfo
     * @Date 2024/4/19 15:07
     */
    public ResultVo<ReportBaseInfo> selectReportInfoData(HighReportDto dto){
        return ResultVoUtil.success(dao.selectReportPortalMenu(dto.getReportId(), null, null));
    }

}
