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

import java.math.BigDecimal;
import java.util.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Service;

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.AssignSeqUtil;
import com.core.common.utils.MessageUtils;
import com.core.common.utils.SecurityUtils;
import com.openhis.administration.domain.Account;
import com.openhis.administration.service.IAccountService;
import com.openhis.common.constant.CommonConstants;
import com.openhis.common.constant.PromptMsgConstant;
import com.openhis.common.enums.*;
import com.openhis.common.enums.ybenums.YbPayment;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.financial.domain.PaymentRecDetail;
import com.openhis.financial.domain.PaymentReconciliation;
import com.openhis.financial.service.IPaymentRecDetailService;
import com.openhis.financial.service.IPaymentReconciliationService;
import com.openhis.web.inhospitalcharge.appservice.IAdvancePaymentManageAppService;
import com.openhis.web.inhospitalcharge.dto.AdvancePaymentFlowRecordDto;
import com.openhis.web.inhospitalcharge.dto.AdvancePaymentInAndOutDto;
import com.openhis.web.inhospitalcharge.dto.AdvancePaymentInfoDto;
import com.openhis.web.inhospitalcharge.mapper.AdvancePaymentManageAppMapper;

import lombok.extern.slf4j.Slf4j;

/**
 * 预交金管理 应用实现类
 */
@Slf4j
@Service
public class AdvancePaymentManageAppServiceImpl implements IAdvancePaymentManageAppService {

    @Resource
    AssignSeqUtil assignSeqUtil;

    @Resource
    AdvancePaymentManageAppMapper advancePaymentManageAppMapper;

    @Resource
    IPaymentReconciliationService iPaymentReconciliationService;

    @Resource
    IPaymentRecDetailService iPaymentRecDetailService;

    @Resource
    IAccountService iAccountService;

    /**
     * 查询患者预交金基本信息
     *
     * @param advancePaymentInfoDto 患者预交金基本信息dto
     * @param searchKey 模糊查询关键字
     * @param pageNo 当前页
     * @param pageSize 每页多少条
     * @param request 请求
     * @return 患者预交金基本信息
     */
    @Override
    public IPage<AdvancePaymentInfoDto> getAdvancePaymentInfo(AdvancePaymentInfoDto advancePaymentInfoDto,
        String searchKey, Integer pageNo, Integer pageSize, HttpServletRequest request) {
        // 构建查询条件
        QueryWrapper<AdvancePaymentInfoDto> queryWrapper = HisQueryUtils.buildQueryWrapper(advancePaymentInfoDto,
            searchKey, new HashSet<>(Arrays.asList("bus_no", "patient_name", "inHospital_org_name")), request);

        IPage<AdvancePaymentInfoDto> advancePaymentInfo = advancePaymentManageAppMapper.getAdvancePaymentInfo(
            new Page<>(pageNo, pageSize), PaymentKind.HOSPITAL_DEPOSIT.getValue(), LocationForm.WARD.getValue(),
            LocationForm.HOUSE.getValue(), LocationForm.BED.getValue(), EncounterActivityStatus.ACTIVE.getValue(),
            ChargeItemStatus.BILLABLE.getValue(), ChargeItemStatus.BILLED.getValue(),
            ChargeItemStatus.REFUNDED.getValue(), EncounterClass.IMP.getValue(),
            AccountType.PERSONAL_CASH_ACCOUNT.getCode(), queryWrapper);
        advancePaymentInfo.getRecords().forEach(e -> {
            // 性别
            e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
            // 计算年龄
            e.setAge(e.getBirthDate() != null ? AgeCalculatorUtil.getAge(e.getBirthDate()) : "");
        });

        return advancePaymentInfo;
    }

    /**
     * 查询预交金支付流水记录
     *
     * @param encounterId 住院就诊Id
     * @return 预交金支付流水记录
     */
    @Override
    public List<AdvancePaymentFlowRecordDto> getAdvancePaymentFlowRecord(Long encounterId) {
        List<AdvancePaymentFlowRecordDto> advancePaymentFlowRecordList =
            advancePaymentManageAppMapper.getAdvancePaymentFlowRecordList(encounterId);
        advancePaymentFlowRecordList.forEach(e ->
        // 付款类别
        e.setPaymentEnum_enumText(EnumUtils.getInfoByValue(PaymentType.class, e.getPaymentEnum())));
        return advancePaymentFlowRecordList;
    }

    /**
     * 预交金交款
     *
     * @param advancePaymentInAndOutDto 交款dto
     * @return 结果
     */
    @Override
    public R<?> inAdvancePayment(AdvancePaymentInAndOutDto advancePaymentInAndOutDto) {
        this.handleAdvancePayment(advancePaymentInAndOutDto, PaymentType.PAY.getValue());
        return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"预交金交款"}));
    }

    /**
     * 预交金退款
     *
     * @param advancePaymentInAndOutDto 退款dto
     * @return 结果
     */
    @Override
    public R<?> outAdvancePayment(AdvancePaymentInAndOutDto advancePaymentInAndOutDto) {
        this.handleAdvancePayment(advancePaymentInAndOutDto, PaymentType.UN_PAY.getValue());
        return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"预交金退款"}));
    }

    /**
     * 处理 预交金交费/退费
     *
     * @param advancePaymentInAndOutDto 预交金dto
     * @param paymentEnum 类别
     */
    private void handleAdvancePayment(AdvancePaymentInAndOutDto advancePaymentInAndOutDto, Integer paymentEnum) {
        BigDecimal ammount;
        // 交费
        if (PaymentType.PAY.getValue().equals(paymentEnum)) {
            ammount = advancePaymentInAndOutDto.getAmount();
        }
        // 退费
        else {
            ammount = advancePaymentInAndOutDto.getAmount().multiply(new BigDecimal("-1"));
        }
        // 保存支付信息
        String paymentNo = assignSeqUtil.getSeqByDay(AssignSeqEnum.PAYMENT_NO.getPrefix(), 20); // 生成流水号
        Date setlTime = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(setlTime);
        calendar.add(Calendar.HOUR_OF_DAY, 24);
        Date futureTime = calendar.getTime();

        // 支付管理主表
        PaymentReconciliation payment = new PaymentReconciliation();
        payment.setStatusEnum(PaymentStatus.SUCCESS.getValue()); // 状态
        payment.setPaymentNo(paymentNo); // 支付的业务标识符
        payment.setPaymentEnum(paymentEnum); // 付款类别
        payment.setPaymentReconciliationId(advancePaymentInAndOutDto.getPatientId()); // 付款实体ID
        payment.setKindEnum(PaymentKind.HOSPITAL_DEPOSIT.getValue()); // 发起支付的工作流程类别-住院存款
        payment.setEntererId(SecurityUtils.getLoginUser().getPractitionerId()); // 收款员
        payment.setPatientId(advancePaymentInAndOutDto.getPatientId()); // 患者ID
        payment.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId()); // 请求支付责任人ID
        payment.setOutcomeEnum(PaymentOutcome.PARTIAL.getCode()); // 付款结果
        payment.setLocationId(-99L); // 支付位置
        payment.setExpirationDate(futureTime); // 到期时间
        payment.setTenderedAmount(ammount); // 应收金额
        payment.setDisplayAmount(ammount); // 付款总额
        payment.setPrintCount(0); // 打印标识
        payment.setEncounterId(advancePaymentInAndOutDto.getEncounterId()); // 住院就诊id
        payment.setBillDate(setlTime); // 结算时间
        iPaymentReconciliationService.save(payment);

        // 支付管理子表
        PaymentRecDetail paymentDetail = new PaymentRecDetail();
        paymentDetail.setAccountId(advancePaymentInAndOutDto.getAccountId()); // 账户
        paymentDetail.setPayEnum(YbPayment.SELF_CASH_PAY.getValue()); // 支付类型
        paymentDetail.setPayLevelEnum(YbPayment.SELF_CASH_PAY.getLevel()); // 支付类型等级
        paymentDetail.setAmount(ammount); // 金额
        paymentDetail.setPayTransNo(CommonConstants.BusinessName.DEFAULT_CONTRACT_NO);
        paymentDetail.setReconciliationId(payment.getId()); // 付款id

        iPaymentRecDetailService.save(paymentDetail);

        // 更新个人现金账户的预交金
        Account account =
            iAccountService.lambdaQuery().eq(Account::getTypeCode, AccountType.PERSONAL_CASH_ACCOUNT.getCode())
                .eq(Account::getEncounterId, advancePaymentInAndOutDto.getEncounterId())
                .select(Account::getId, Account::getBalanceAmount).one();
        if (account == null) {
            throw new RuntimeException("未找到对应的个人现金账户信息");
        }
        BigDecimal currentBalance = account.getBalanceAmount() != null ? account.getBalanceAmount() : BigDecimal.ZERO;
        BigDecimal newBalance = currentBalance.add(ammount);
        iAccountService.lambdaUpdate().set(Account::getBalanceAmount, newBalance).eq(Account::getId, account.getId())
            .update();
    }

}
