package com.eitc.patient.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.eitc.common.annotation.Log;
import com.eitc.common.core.controller.BaseController;
import com.eitc.common.core.domain.AjaxResult;
import com.eitc.common.core.page.TableDataInfo;
import com.eitc.common.enums.BusinessType;
import com.eitc.common.utils.PatientBaseUtil;
import com.eitc.common.utils.PdfUtil;
import com.eitc.common.utils.bean.BeanUtils;
import com.eitc.patient.domain.DiagnosisType;
import com.eitc.patient.domain.MedicalRecord;
import com.eitc.patient.domain.MedicalRecordDetails;
import com.eitc.patient.domain.Patient;
import com.eitc.patient.domain.ProtocolTemplate;
import com.eitc.patient.dto.MedicalRecordDto;
import com.eitc.patient.dto.MedicalRecordListDto;
import com.eitc.patient.dto.PatientTeethInfoDto;
import com.eitc.patient.service.IDiagnosisTypeService;
import com.eitc.patient.service.IMedicalRecordDetailsService;
import com.eitc.patient.service.IMedicalRecordService;
import com.eitc.patient.service.IPatientService;
import com.eitc.patient.service.IProtocolTemplateService;
import com.eitc.patient.utils.PatientUtil;
import com.eitc.patient.vo.MedicalRecordDetailsVO;
import com.eitc.patient.vo.MedicalRecordParamVO;
import com.eitc.patient.vo.MedicalRecordTemplateVO;
import com.eitc.system.utils.UserCacheUtil;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * MedicalRecordController
 *
 * @author mtx
 * @date: 2024/05/17 15:52
 */
@Api(tags = "病例")
@RestController
@RequestMapping("/patient/medical-record")
@Slf4j
public class MedicalRecordController extends BaseController {

    @Resource
    private IMedicalRecordService medicalRecordService;
    @Resource
    private IMedicalRecordDetailsService medicalRecordDetailsService;
    @Resource
    private IPatientService patientService;
    @Resource
    private IDiagnosisTypeService diagnosisTypeService;
    @Resource
    private IProtocolTemplateService protocolTemplateService;

    @Resource
    private FreeMarkerConfigurer freeMarkerConfigurer;

    /**
     * 将HTML格式的病历模板转换为PDF格式。
     * 此方法接收一个病历模板的视图对象，经过处理后，使用Freemarker模板引擎将HTML内容渲染，
     * 最后通过响应对象将渲染后的PDF内容发送给客户端。
     *
     * @param vo       包含病历详细信息和模板数据的视图对象。
     * @param response HTTP响应对象，用于向客户端发送PDF内容。
     * @throws IOException       如果处理过程中发生IO异常。
     * @throws TemplateException 如果Freemarker模板处理过程中发生异常。
     */
    @PostMapping(value = "/html2Pdf")
    @ApiOperation("打印病历")
    public void html2Pdf(@RequestBody MedicalRecordTemplateVO vo, HttpServletResponse response) throws IOException, TemplateException {
        // 过滤出标记为需要包含在PDF中的病历细节。
        Map<String, List<MedicalRecordDetailsVO>> listMap = vo.getListMap();
        for (String s : listMap.keySet()) {
            List<MedicalRecordDetailsVO> vos = listMap.get(s);
            if (CollectionUtils.isEmpty(vos)) {
                continue;
            }
            List<MedicalRecordDetailsVO> collect = vos.stream().filter(MedicalRecordDetailsVO::isFlagCheck).collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(collect)){
                collect.forEach(li -> li.setToothPosition(initToothPosition(li.getToothPosition())));
            }
            listMap.put(s, collect);
        }

        // 准备Freemarker模板渲染所需的参数。
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("companyName", UserCacheUtil.getClinicName() + "口腔科病历");
        paramMap.put("patient", vo);

        // 使用Freemarker模板引擎渲染HTML模板。
        Writer out = new StringWriter();
        //获取模板地址
        Template template = freeMarkerConfigurer.getConfiguration().getTemplate("medical-record.html");
        template.process(paramMap, out);
        out.flush();
        out.close();
        String templateContent = out.toString();

        // 设置响应头，以便客户端可以接收PDF内容。
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/pdf");
        String fileName = System.currentTimeMillis() + ".pdf";
        response.setHeader("Content-Disposition", "filename=" + new String(fileName.getBytes(), StandardCharsets.UTF_8));

        // 调用PDF工具类，将渲染后的HTML内容转换为PDF，并通过响应对象发送给客户端。
        PdfUtil.html2Pdf(templateContent, response);
    }


    /**
     * 根据病历ID查询病历信息。
     *
     * @param medicalRecordId 病历ID。
     * @return 返回病历详情的Ajax结果。
     * @throws ParseException 如果解析日期出错。
     */
    @GetMapping("/findMedicalInfo")
    @ApiOperation("打印病历详情")
    public AjaxResult findMedicalInfo(String medicalRecordId, String protocolTemplateId) throws ParseException {
        MedicalRecordTemplateVO mrt = new MedicalRecordTemplateVO();
        // 根据病历ID查询病历信息
        MedicalRecord medicalRecord = medicalRecordService.getById(medicalRecordId);
        // 检查病历是否存在
        if (Objects.nonNull(medicalRecord)) {
            // 获取患者ID
            String patientId = medicalRecord.getPatientId();
            // 根据患者ID查询患者信息
            Patient patient = patientService.getById(patientId);
            // 检查患者信息是否存在
            if (Objects.nonNull(patient)) {
                // 将患者信息复制到病历模板VO中
                BeanUtils.copyProperties(patient, mrt);
                // 根据身份证号计算患者年龄
                mrt.setAge(PatientBaseUtil.findAge(patient.getIdentificationCard()));
            }
            // 根据病历ID查询病历详情列表
            List<MedicalRecordDetailsVO> list = medicalRecordDetailsService.findMedicalRecordDetailsVOByMedicalRecordId(medicalRecordId);
            // 根据模板名称分组聚合病历详情
            Map<String, List<MedicalRecordDetailsVO>> collect = list.stream().collect(Collectors.groupingBy(MedicalRecordDetailsVO::getTemplateName));
            // 使用LinkedHashMap保持插入顺序
            Map<String, List<MedicalRecordDetailsVO>> linkedMap = new LinkedHashMap<>();
            // 遍历诊断类型模板键列表
            for (String s : PatientUtil.diagnosisTypeTemplateKeyList()) {
                // 对特定的模板类型进行处理
                if ("主诉".equals(s) || "现病史".equals(s) || "既往史".equals(s) || "家族史".equals(s) || "过敏史".equals(s)) {
                    List<MedicalRecordDetailsVO> vos = collect.get(s);
                    // 检查是否存在对应的病历详情
                    if (CollectionUtils.isNotEmpty(vos)) {
                        MedicalRecordDetailsVO vo = vos.get(0);
                        // 对既往史、家族史、过敏史进行格式化处理
                        if ("既往史".equals(s) || "家族史".equals(s) || "过敏史".equals(s)) {
                            if (Objects.nonNull(vo.getFormattedPartsList())) {
                                vo.setContentInfo(String.join(",", vo.getFormattedPartsList()));
                            } else {
                                vo.setContentInfo("");
                            }
                        }
                        // 将处理后的病历详情添加到linkedMap中
                        linkedMap.put(s, Collections.singletonList(vo));
                    } else {
                        // 如果不存在对应的病历详情，则添加空列表
                        linkedMap.put(s, new ArrayList<>());
                    }
                } else {
                    // 对其他模板类型，直接添加到linkedMap中
                    linkedMap.put(s, collect.get(s));
                }
            }
            // 设置处理后的病历详情列表到病历模板VO中
            mrt.setListMap(linkedMap);
        }
        mrt.setCompanyName(UserCacheUtil.getClinicName() + "口腔科病历");

        ProtocolTemplate protocolTemplate = protocolTemplateService.getById(protocolTemplateId);
        if (Objects.nonNull(protocolTemplate)) {
            mrt.setContent(protocolTemplate.getProtocolTemplateContent());
        }
        // 返回处理结果
        return success(mrt);
    }

    /**
     * 查询病历
     */
    @GetMapping("/list")
    @ApiOperation("查询病历")
    public AjaxResult list(MedicalRecordDto medicalRecord) {
        LambdaQueryWrapper<MedicalRecord> wrapper = Wrappers.<MedicalRecord>lambdaQuery()
                .eq(MedicalRecord::getPatientId, medicalRecord.getPatientId())
                .orderByDesc(MedicalRecord::getCreateDate);

        Long queryScope = medicalRecord.getQueryScope();
        if (Objects.nonNull(queryScope)) {
            LocalDateTime dateTime = LocalDateTime.now().minusMonths(queryScope);
            wrapper.ge(MedicalRecord::getCreateDate, dateTime);
        }
        List<MedicalRecordParamVO> voList = new ArrayList<>();
        List<MedicalRecord> list = medicalRecordService.list(wrapper);
        if (Objects.nonNull(list)) {
            for (MedicalRecord record : list) {
                voList.add(medicalRecordService.getMedicalRecordVO(record));
            }
        }
        return success(voList);
    }

    /**
     * 获取病历详细信息
     */
    @GetMapping(value = "/detail")
    @ApiOperation("获取病历详细信息")
    public AjaxResult detail(String id) {
        MedicalRecord medicalRecord = medicalRecordService.getById(id);
        return success(medicalRecordService.getMedicalRecordVO(medicalRecord));
    }

    /**
     * 新增病历
     */
    @Log(title = "新增病历", businessType = BusinessType.INSERT)
    @PostMapping
    @ApiOperation("新增病历")
    public AjaxResult add(@Valid @RequestBody MedicalRecordParamVO vo) {
        if (CollectionUtils.isEmpty(vo.getDataList())) {
            return error("无数据");
        }
        medicalRecordService.addOrEdit(vo);
        return success();
    }

    /**
     * 修改病历
     */
    @Log(title = "修改病历", businessType = BusinessType.UPDATE)
    @PutMapping
    @ApiOperation("修改病历")
    public AjaxResult edit(@Valid @RequestBody MedicalRecordParamVO vo) {
        if (StringUtils.isEmpty(vo.getId())) {
            return error();
        }
        if (CollectionUtils.isEmpty(vo.getDataList())) {
            return error("无数据");
        }
        medicalRecordService.addOrEdit(vo);
        return success();
    }

    /**
     * 删除病历
     */
    @Log(title = "删除病历", businessType = BusinessType.DELETE)
    @DeleteMapping("/remove")
    @ApiOperation("删除病历")
    public AjaxResult remove(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));
        return toAjax(medicalRecordService.removeBatchByIds(idList));
    }

    /**
     * 根据患者ID查询其牙齿治疗状态
     * 该接口通过患者ID从医疗记录中检索患者的牙齿治疗信息，并根据治疗状态进行过滤。
     * 如果指定了治疗状态类型（type参数），则只返回匹配该治疗状态的牙齿信息。
     *
     * @param patientId 患者ID，用于查询患者的医疗记录。
     * @param type      治疗状态类型，可选参数，用于过滤牙齿治疗信息。
     * @return 返回一个包含患者牙齿治疗状态的列表。如果患者没有相关记录，则返回空列表。
     */
    @GetMapping(value = "/teethListByPatientId")
    @ApiOperation("根据患者id查询牙齿治疗状态")
    public AjaxResult teethListByPatientId(@RequestParam(name = "patientId") String patientId,
                                           @RequestParam(name = "type", required = false) Integer type) {
        // 根据患者ID查询所有的医疗记录，并按创建日期降序排序
        List<MedicalRecord> list = medicalRecordService.list(Wrappers.<MedicalRecord>lambdaQuery()
                .eq(MedicalRecord::getPatientId, patientId)
                .orderByDesc(MedicalRecord::getCreateDate));

        // 如果没有查询到医疗记录，直接返回空列表
        if (CollectionUtils.isEmpty(list)) {
            return success(new ArrayList<>());
        }

        List<PatientTeethInfoDto> dtoList = new ArrayList<>();
        for (MedicalRecord medicalRecord : list) {
            PatientTeethInfoDto dto = new PatientTeethInfoDto();

            // 根据治疗状态类型查询对应的医疗记录详情
            List<MedicalRecordDetails> recordDetails = medicalRecordDetailsService.list(Wrappers.<MedicalRecordDetails>lambdaQuery()
                    .eq(Objects.nonNull(type), MedicalRecordDetails::getToothStatus, type)
                    .eq(MedicalRecordDetails::getMedicalRecordId, medicalRecord.getId()));

            // 如果没有找到匹配的医疗记录详情，跳过当前循环
            if (CollectionUtils.isEmpty(recordDetails)) {
                continue;
            }

            // 将医疗记录详情按牙齿位置分组，以便后续处理
            Map<String, List<MedicalRecordDetails>> collect = recordDetails.stream().collect(Collectors.groupingBy(MedicalRecordDetails::getToothPosition));

            for (String s : collect.keySet()) {
                List<MedicalRecordDetails> details = collect.get(s);
                // 从每个分组中选择第一个记录（因为它们按创建日期排序，这意味这是最新地记录）
                Optional<MedicalRecordDetails> first = details.stream().findFirst();

                // 如果找到了最新的记录
                if (first.isPresent()) {
                    MedicalRecordDetails medicalRecordDetails = first.get();

                    // 设置DTO的属性，包括患者ID、牙齿状态、牙齿位置、医疗记录ID等
                    dto.setPatientId(patientId);
                    dto.setToothPositionStatus(medicalRecordDetails.getToothStatus());
                    dto.setToothPosition(medicalRecordDetails.getToothPosition());
                    dto.setMedicalRecordId(medicalRecord.getId());
                    dto.setMedicalRecordDetailsId(medicalRecordDetails.getId());

                    // 根据医疗记录详情中的诊断类型ID，查询并设置诊断类型名称
                    DiagnosisType diagnosisType = diagnosisTypeService.getById(medicalRecordDetails.getDiagnosisTypeId());
                    if (Objects.nonNull(diagnosisType)) {
                        dto.setDiagnosisTypeName(diagnosisType.getDiagnosisName());
                    }

                    // 设置医疗记录的创建日期
                    dto.setCreateDate(medicalRecord.getCreateDate());

                    // 将处理好的DTO添加到结果列表中
                    dtoList.add(dto);
                }
            }
        }

        return success(dtoList);
    }


    @GetMapping("/medical-record-list")
    @ApiOperation("查询病历列表")
    public TableDataInfo medicalRecordList(@RequestParam(name = "searchParam", required = false) String searchParam) {
        startPage();
        List<MedicalRecordListDto> list = medicalRecordService.medicalRecordList(searchParam);
        return getDataTable(list);
    }

    private String initToothPosition(String toothPosition) {
        switch (toothPosition) {
            case "upLeft1":
                return "上左牙位1";
            case "upLeft2":
                return "上左牙位2";
            case "upLeft3":
                return "上左牙位3";
            case "upLeft4":
                return "上左牙位4";
            case "upLeft5":
                return "上左牙位5";
            case "upLeft6":
                return "上左牙位6";
            case "upLeft7":
                return "上左牙位7";
            case "upLeft8":
                return "上左牙位8";
            case "upRight1":
                return "上右牙位1";
            case "upRight2":
                return "上右牙位2";
            case "upRight3":
                return "上右牙位3";
            case "upRight4":
                return "上右牙位4";
            case "upRight5":
                return "上右牙位5";
            case "upRight6":
                return "上右牙位6";
            case "upRight7":
                return "上右牙位7";
            case "upRight8":
                return "上右牙位8";
            case "downLeft1":
                return "下左牙位1";
            case "downLeft2":
                return "下左牙位2";
            case "downLeft3":
                return "下左牙位3";
            case "downLeft4":
                return "下左牙位4";
            case "downLeft5":
                return "下左牙位5";
            case "downLeft6":
                return "下左牙位6";
            case "downLeft7":
                return "下左牙位7";
            case "downLeft8":
                return "下左牙位8";
            case "downRight1":
                return "下左牙位1";
            case "downRight2":
                return "下左牙位2";
            case "downRight3":
                return "下左牙位3";
            case "downRight4":
                return "下左牙位4";
            case "downRight5":
                return "下左牙位5";
            case "downRight6":
                return "下左牙位6";
            case "downRight7":
                return "下左牙位7";
            case "downRight8":
                return "下左牙位8";
            default:
                return "-";
        }
    }

}
