package com.openhis.web.document.appservice.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.utils.AgeCalculatorUtil;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.bean.BeanUtils;
import com.openhis.common.enums.AdministrativeGender;
import com.openhis.common.enums.DocStatusEnum;
import com.openhis.common.enums.DocTypeEnum;
import com.openhis.common.enums.EncounterClass;
import com.openhis.common.utils.HisPageUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.document.domain.DocRecord;
import com.openhis.document.mapper.DocRecordMapper;
import com.openhis.document.service.IDocRecordService;
import com.openhis.web.document.appservice.IDocDefinitionAppService;
import com.openhis.web.document.appservice.IDocRecordAppService;
import com.openhis.web.document.appservice.IDocStatisticsAppService;
import com.openhis.web.document.appservice.IDocStatisticsDefinitionAppService;
import com.openhis.web.document.dto.*;
import com.openhis.web.document.mapper.DocRecordAppMapper;
import com.openhis.web.document.util.ConvertToDocStatistics;
import com.openhis.web.document.util.PermissionProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
@Service
public class DocRecordAppServiceImpl implements IDocRecordAppService {

    @Resource
    private IDocRecordService docRecordService;
    @Resource
    private DocRecordMapper docRecordMapper;
    @Resource
    private IDocDefinitionAppService docDefinitionAppService;
    @Resource
    private DocRecordAppMapper docRecordAppMapper;
    @Resource
    private IDocStatisticsDefinitionAppService docStatisticsDefinitionAppService;
    @Resource
    private IDocStatisticsAppService docStatisticsAppService;
    @Resource
    ConvertToDocStatistics convertToDocStatistics;
    @Autowired
    private JdbcTemplate jdbcTemplate; // 使用 JdbcTemplate 执行 SQL


    /**
     * 插入单条文档操作日志
     *
     * @param docRecord 原始文书记录
     * @return 是否插入成功
     */
    private Boolean insertLog(DocRecord docRecord) {
        if (docRecord == null) {
            log.error("插入文档日志失败：原始文书记录不能为空");
            return false;
        }

        try {
            String sql = "INSERT INTO doc_record_log(" +
                    "record_id, definition_id, definition_bus_no, content_json, " +
                    "status_enum, organization_id, encounter_id, patient_id, record_time, " +
                    "create_by, create_time, update_by, update_time, delete_flag, tenant_id" +
                    ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

            Object[] params = {
                    docRecord.getId(),
                    docRecord.getDefinitionId(),
                    docRecord.getDefinitionBusNo(),
                    docRecord.getContentJson(),
                    docRecord.getStatusEnum(),
                    docRecord.getOrganizationId(),
                    docRecord.getEncounterId(),
                    docRecord.getPatientId(),
                    new Timestamp(docRecord.getRecordTime().getTime()),
                    SecurityUtils.getUsername(),
                    new Timestamp(new Date().getTime()),
                    null, // update_by
                    null, // update_time
                    0,    // delete_flag（未删除）
                    docRecord.getTenantId()
            };

            int rowsAffected = jdbcTemplate.update(sql, params);
            return rowsAffected > 0;
        } catch (Exception e) {
            log.error("插入文档操作日志失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 批量插入文档操作日志
     *
     * @param docRecords 原始文书记录列表
     * @return 是否插入成功
     */
    private Boolean insertBatchLog(List<DocRecord> docRecords) {
        if (docRecords == null || docRecords.isEmpty()) {
            log.error("批量插入文档日志失败：文书记录列表不能为空");
            return false;
        }

        try {
            String sql = "INSERT INTO doc_record_log(" +
                    "record_id, definition_id, definition_bus_no, content_json, " +
                    "status_enum, organization_id, encounter_id, patient_id, record_time, " +
                    "create_by, create_time, update_by, update_time, delete_flag, tenant_id" +
                    ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

            int[] results = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
                @Override
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    DocRecord docRecord = docRecords.get(i);
                    ps.setLong(1, docRecord.getId());
                    ps.setLong(2, docRecord.getDefinitionId());
                    ps.setString(3, docRecord.getDefinitionBusNo());
                    ps.setString(4, docRecord.getContentJson());
                    ps.setInt(5, docRecord.getStatusEnum());
                    ps.setLong(6, docRecord.getOrganizationId());
                    ps.setLong(7, docRecord.getEncounterId());
                    ps.setLong(8, docRecord.getPatientId());
                    ps.setTimestamp(9, new Timestamp(docRecord.getRecordTime().getTime()));
                    ps.setString(10, SecurityUtils.getUsername());
                    ps.setTimestamp(11, new Timestamp(new Date().getTime()));
                    ps.setObject(12, null); // update_by
                    ps.setObject(13, null); // update_time
                    ps.setInt(14, 0); // delete_flag
                    ps.setLong(15, docRecord.getTenantId());
                }

                @Override
                public int getBatchSize() {
                    return docRecords.size();
                }
            });

            // 校验所有日志是否插入成功
            for (int result : results) {
                if (result <= 0) {
                    log.error("批量插入文档日志失败：部分记录插入无效");
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            log.error("批量插入文档操作日志失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 初始化文书记录参数校验
     *
     * @param docRecordDto 文书记录DTO
     * @return 校验失败信息（空字符串表示校验通过）
     */
    private String initRecordCheck(DocRecordDto docRecordDto) {
        if (docRecordDto == null) {
            return "文书记录不能为空";
        }
        if (docRecordDto.getDefinitionId() == null) {
            return "文书记录：文书类型ID不能为空";
        }
        if (docRecordDto.getDefinitionBusNo() == null) {
            return "文书记录：文书编号不能为空";
        }
        if (docRecordDto.getContentJson() == null) {
            return "文书记录：记录内容不能为空";
        }
        if (docRecordDto.getEncounterId() == null) {
            return "文书记录：就诊ID不能为空";
        }
        if (docRecordDto.getPatientId() == null) {
            return "文书记录：患者ID不能为空";
        }
        // 2. 校验文书定义是否存在
        DocDefinitionDto docDefinitionDto = docDefinitionAppService.getDefinitionById(docRecordDto.getDefinitionId());
        if (docDefinitionDto == null) {
            return "患者文书记录：对应文书不存在";
        }
        // 3. 权限校验
        boolean hasPermission = PermissionProcessor.hasPermission(docDefinitionDto);
        if (!hasPermission) {
            return "患者文书记录：当前登录用户无权限新增/编辑该文书";
        }
        return "";
    }


    /**
     * 文书统计 - 新增、编辑记录
     *
     * @param docRecordDto
     * @return
     */
    private R<?> createOrUpdateStatistics(DocRecordDto docRecordDto) {
        if (docRecordDto == null) {
            return R.fail("插入文书统计数据失败：原始文书记录不能为空");
        }
        if (docRecordDto.getContentJson() == null) {
            return R.fail("插入文书统计数据失败：原始文书记录内容不能为空");
        }
        List<DocStatisticsDto> list = convertToDocStatistics.convertToStatisticsDtoList(docRecordDto);
        if (list == null || list.isEmpty()) {
            return R.fail("插入文书统计数据失败：统计数据为空");
        }
        return docStatisticsAppService.createOrUpdte(list);
    }

    /**
     * 新增记录
     *
     * @param docRecordDto 文书记录信息
     * @return 返回结果 R对象
     */
    public R<?> addRecord(DocRecordDto docRecordDto) {
        String checkMsg = initRecordCheck(docRecordDto);
        if (!checkMsg.isEmpty()) {
            return R.fail(checkMsg);
        }
        DocRecord docRecord = new DocRecord();
        docRecord.setDefinitionId(docRecordDto.getDefinitionId());
        docRecord.setDefinitionBusNo(docRecordDto.getDefinitionBusNo());
        docRecord.setContentJson(docRecordDto.getContentJson());
        docRecord.setStatusEnum(docRecordDto.getStatusEnum() == null ? DocStatusEnum.DRAFT.getValue() : docRecordDto.getStatusEnum());
        docRecord.setOrganizationId(SecurityUtils.getLoginUser().getOrgId());
        docRecord.setEncounterId(docRecordDto.getEncounterId());
        docRecord.setPatientId(docRecordDto.getPatientId());
        docRecord.setRecordTime(docRecordDto.getRecordTime());
        docRecord.setIsEdit(0);
        boolean saveSuccess = docRecordService.save(docRecord);
        if (saveSuccess) {
            docRecordDto.setId(docRecord.getId());
            createOrUpdateStatistics(docRecordDto);
            return R.ok("新增患者文书记录成功");
        } else {
            return R.fail("新增患者文书记录失败");
        }
    }

    /**
     * 修改记录（含日志留痕）
     *
     * @param docRecordDto 新文书记录DTO
     * @return 返回结果 R对象
     */
    public R<?> updateRecord(DocRecordDto docRecordDto) {
        String checkMsg = initRecordCheck(docRecordDto);
        if (!checkMsg.isEmpty()) {
            return R.fail(checkMsg);
        }
        // 1. 查询原文书记录
        DocRecord docRecord = docRecordService.getById(docRecordDto.getId());
        if (docRecord == null) {
            return R.fail("修改患者文书记录失败：记录不存在");
        }
        // 2. 记录日志
        // 深拷贝原数据用于日志
        DocRecord logDocRecord = new DocRecord();
        BeanUtils.copyProperties(docRecord, logDocRecord);

        // 赋值新数据
        docRecord.setDefinitionId(docRecordDto.getDefinitionId());
        docRecord.setDefinitionBusNo(docRecordDto.getDefinitionBusNo());
        docRecord.setContentJson(docRecordDto.getContentJson());
        docRecord.setStatusEnum(docRecordDto.getStatusEnum());
        docRecord.setOrganizationId(SecurityUtils.getLoginUser().getOrgId());
        docRecord.setEncounterId(docRecordDto.getEncounterId());
        docRecord.setPatientId(docRecordDto.getPatientId());
        docRecord.setRecordTime(docRecordDto.getRecordTime());
        docRecord.setIsEdit(0);
        // 执行修改
        boolean updateSuccess = docRecordService.updateById(docRecord);
        if (updateSuccess) {
            // 插入操作日志
            boolean logSuccess = insertLog(logDocRecord);
            if (!logSuccess) {
                return R.fail("修改患者文书记录成功，但插入文档操作日志失败");
            }
            createOrUpdateStatistics(docRecordDto);
            return R.ok("修改患者文书记录成功");
        } else {
            return R.fail("修改患者文书记录失败");
        }
    }

    /**
     * 根据患者ID/就诊ID获取文书记录列表（支持分页）
     */
    @Override
    public R<?> getRecordByEncounterIdList(DocRecordQueryParam docRecordQueryParam, Integer isPage, Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request) {
        // 1. 参数校验
        if (docRecordQueryParam == null) {
            return R.fail("患者文书记录：查询参数不能为空");
        }
        if (docRecordQueryParam.getPatientId() == null && docRecordQueryParam.getEncounterId() == null) {
            return R.fail("患者文书记录：患者ID和就诊ID不能同时为空");
        }
        if (docRecordQueryParam.getDefinitionId() == null) {
            return R.fail("患者文书记录：文书类型（定义）ID不能为空");
        }

        // 2. 分页查询逻辑
        if (isPage != null && isPage == 1) {
            QueryWrapper<DocRecordDto> queryWrapper = HisQueryUtils.buildQueryWrapper(
                    docRecordQueryParam, null, null, request);
            IPage<DocRecordDto> pageResult = docRecordAppMapper.getRecordPageListByEncounterId(
                    new Page<>(pageNo, pageSize), queryWrapper);
            return R.ok(pageResult, "获取患者文书记录列表成功");
        }
        // 3. 不分页查询逻辑
        else {
            LambdaQueryWrapper<DocRecord> queryWrapper = new LambdaQueryWrapper<>();
            // 患者ID查询条件
            if (docRecordQueryParam.getPatientId() != null) {
                queryWrapper.eq(DocRecord::getPatientId, docRecordQueryParam.getPatientId());
            }
            // 文书类型ID（必传）
            queryWrapper.eq(DocRecord::getDefinitionId, docRecordQueryParam.getDefinitionId());
            // 就诊ID查询条件（修复原逻辑：原代码字段匹配错误，用EncounterId匹配DefinitionBusNo）
            if (docRecordQueryParam.getEncounterId() != null) {
                queryWrapper.eq(DocRecord::getEncounterId, docRecordQueryParam.getEncounterId());
            }
            // 科室ID查询条件（修复原逻辑：原代码字段匹配错误，用OrganizationId匹配EncounterId）
            if (docRecordQueryParam.getOrganizationId() != null) {
                queryWrapper.eq(DocRecord::getOrganizationId, docRecordQueryParam.getOrganizationId());
            }

   DocDefinitionDto docDefinitionDto = docDefinitionAppService.getDefinitionById(docRecordQueryParam.getDefinitionId());
            // 实体转DTO
            List<DocRecordDto> dtoList = new ArrayList<>();
            List<DocRecord> recordList = docRecordService.list(queryWrapper);
            for (DocRecord record : recordList) {
                DocRecordDto dto = new DocRecordDto();
                BeanUtils.copyProperties(record, dto);
                dto.setName(docDefinitionDto!=null?docDefinitionDto.getName():"");
                dto.setVersion(docDefinitionDto!=null?docDefinitionDto.getVersion():"");
                dtoList.add(dto);
            }
            return R.ok(dtoList, "获取患者文书记录列表成功");
        }
    }

    /**
     * 查询患者文书记录列表（携带患者信息，支持分页）
     */
    @Override
    public R<?> getRecordPageList(DocRecordPatientQueryParam docRecordPatientQueryParam, List<Integer> primaryMenuEnumList, Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request) {
        if (docRecordPatientQueryParam == null) {
            return R.fail("文书记录：查询参数不能为空");
        }

        // 构建查询条件（支持患者姓名搜索）
        QueryWrapper<DocRecordPatientDto> queryWrapper = HisQueryUtils.buildQueryWrapper(
                docRecordPatientQueryParam, searchKey, new HashSet<>(Arrays.asList("patient_name", "bus_no")), request);

        // 分页查询
        IPage<DocRecordPatientDto> pageResult = docRecordAppMapper.getRecordPageList(
                new Page<>(pageNo, pageSize), primaryMenuEnumList, queryWrapper);

        // 处理返回结果：枚举值转中文描述、计算患者年龄
        if (pageResult != null && !pageResult.getRecords().isEmpty()) {
            for (DocRecordPatientDto dto : pageResult.getRecords()) {
                // 就诊类型枚举转描述（修复原逻辑：原代码用EncounterClass匹配所有枚举）
                EncounterClass encounterClass = EncounterClass.getByValue(dto.getClassEnum());
                dto.setClassEnumValue(encounterClass != null ? encounterClass.getInfo() : "");

                // 性别枚举转描述
                AdministrativeGender gender = AdministrativeGender.getByValue(dto.getGender());
                dto.setGenderValue(gender != null ? gender.getInfo() : "");

                // 文书类型枚举转描述
                DocTypeEnum docType = DocTypeEnum.getByValue(dto.getPrimaryMenuEnum());
                dto.setPrimaryMenuEnumValue(docType != null ? docType.getInfo() : "");

                // 计算患者年龄
                dto.setAge(AgeCalculatorUtil.getAge(dto.getBirthDate()));
            }
        }

        return R.ok(pageResult, "获取患者文书记录列表成功");
    }

    /**
     * 根据记录ID获取文书详情
     */
    @Override
    public R<?> getRecordById(Long recordId) {
        if (recordId == null) {
            return R.fail("患者文书记录：文书记录ID不能为空");
        }

        DocRecord record = docRecordService.getById(recordId);
        if (record == null) {
            return R.fail("患者文书记录：对应记录不存在");
        }

        DocRecordDto dto = new DocRecordDto();
        BeanUtils.copyProperties(record, dto);
        return R.ok(dto, "获取患者文书记录成功");
    }

    @Override
    public R<?> setIsEdit(Long recordId) {
        if (recordId == null) {
            return R.fail("患者文书记录：文书记录ID不能为空");
        }
        DocRecord record = docRecordService.getById(recordId);
        if (record == null) {
            return R.fail("患者文书记录：对应记录不存在");
        }
        if (record.getIsEdit() != null && record.getIsEdit() == 1) {
            return R.ok("患者文书记录：记录已处于编辑中状态,请勿重复 editing");
        }
        record.setIsEdit(1);
        boolean updateSuccess = docRecordService.updateById(record);
        if (updateSuccess) {
            return R.ok("设置患者文书记录为编辑中成功");
        } else {
            return R.fail("设置患者文书记录为编辑中失败");
        }
    }

    /**
     * 批量删除文书记录（含日志留痕）
     */
    @Override
    public R<?> deleteRecord(List<Long> ids) {
        // 1. 参数校验
        if (ids == null || ids.isEmpty()) {
            return R.fail("患者文书记录：删除的记录ID列表不能为空");
        }

        // 2. 校验记录是否存在
        List<DocRecord> recordList = docRecordService.listByIds(ids);
        if (recordList == null || recordList.isEmpty()) {
            return R.fail("患者文书记录：待删除的记录不存在");
        }

        // 3. 权限校验：仅允许删除自己创建的记录
        String currentUsername = SecurityUtils.getLoginUser().getUsername();
        List<DocRecord> logRecordList = new ArrayList<>();
        for (DocRecord record : recordList) {
            if (!currentUsername.equals(record.getCreateBy())) {
                return R.fail("患者文书记录：无权限删除他人创建的记录（记录ID：" + record.getId() + "）");
            }
            // 拷贝原记录用于日志
            DocRecord logRecord = new DocRecord();
            BeanUtils.copyProperties(record, logRecord);
            logRecordList.add(logRecord);
        }

        // 4. 执行删除
        boolean deleteSuccess = docRecordService.removeByIds(ids);
        if (deleteSuccess) {
            // 5. 批量插入删除日志
            boolean logSuccess = insertBatchLog(logRecordList);
            if (!logSuccess) {
                return R.fail("删除患者文书记录成功，但插入文档操作日志失败");
            }
            return R.ok("删除患者文书记录成功");
        } else {
            return R.fail("删除患者文书记录失败");
        }
    }
}