package com.ywt.outpatient.service.rpc;

import com.ywt.biz.common.enums.PaymentChannelEnum;
import com.ywt.biz.common.enums.TerminalEnum;
import com.ywt.biz.common.util.Checker;
import com.ywt.biz.common.util.DateUtil;
import com.ywt.gapi.im.IMService;
import com.ywt.gapi.message.WeChatMessageService;
import com.ywt.outpatient.core.BaseResponse;
import com.ywt.outpatient.core.SqlHelper;
import com.ywt.outpatient.domain.entity.center.*;
import com.ywt.outpatient.domain.models.config.ConstantTemplateIds;
import com.ywt.outpatient.core.Constants;
import com.ywt.outpatient.domain.models.enums.AccountDetailTypeEnum;
import com.ywt.outpatient.domain.models.enums.CommonBillTypeEnum;
import com.ywt.outpatient.domain.models.enums.SettlementStatusEnum;
import com.ywt.outpatient.service.rpc.common.SmsProvider;
import com.ywt.outpatient.service.rpc.common.WeChatMsgProvider;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.ParameterMode;
import javax.persistence.StoredProcedureQuery;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class OfflineConsultationService {
    private static final Logger logger = LoggerFactory.getLogger(OfflineConsultationService.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private OfflineConsultationRepository offlineConsultationRepository;

    @Autowired
    private AccountRepository accountRepository;

    @Autowired
    private EntityManager entityManager;

    @Autowired
    private SqlHelper sqlHelper;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private MedicalRecordsImageRepository medicalRecordsImageRepository;

    @Autowired
    private SmsProvider smsProvider;

    @DubboReference
    private IMService imServiceBlockingStub;

    @Autowired
    private WeChatMsgProvider weChatMsgProvider;

    @DubboReference
    private WeChatMessageService weChatMessageServiceBlockingStub;

    @Autowired
    private OrderPaymentRepository orderPaymentRepository;

    @Autowired
    private SnsUserInfoRepository snsUserInfoRepository;

    @Autowired
    private HospitalService hospitalService;

    /**
     * 医生账户写入退款信息
     */
    public BaseResponse updateDoctorAccount(int offlineId) {
        BaseResponse baseResponse = new BaseResponse();
        OfflineConsultation order = offlineConsultationRepository.findById(offlineId).orElse(null);
        if (Checker.isNone(order)) {
            logger.error("线下就诊订单ID有误{},退款已结算的线下就诊订单处理失败，", offlineId);
            return baseResponse.failed(BaseResponse.PARAMETER_ERROR, "参数有误");
        }

        Account account = null;
        AccountDetail detail = null;
        // 如果订单是已结算的状态，那么需要从医生账户里面扣掉钱再说
        if (order.getSettlementStatus() == SettlementStatusEnum.SETTLED.getValue()) {
            account = accountRepository.getByUserId(order.getDoctorId());
            if (Checker.isNone(account)) {
                return baseResponse.failed(BaseResponse.PARAMETER_ERROR, "参数错误");
            }
            int amount = Checker.getIntegerValue(order.getTotal());
            int balance = account.getBalance() - amount;
            if (balance < 0) {
                logger.error("医生账户余额小于退款金额", order.toString());
                return baseResponse.failed(BaseResponse.PARAMETER_ERROR, "医生账户余额小于退款金额");
            }

            StoredProcedureQuery storedProcedureQuery = entityManager.createStoredProcedureQuery("add_user_account_detail")
                    .registerStoredProcedureParameter("user_id", Integer.class, ParameterMode.IN)
                    .registerStoredProcedureParameter("type", Integer.class, ParameterMode.IN)
                    .registerStoredProcedureParameter("amount", Integer.class, ParameterMode.IN)
                    .registerStoredProcedureParameter("title", String.class, ParameterMode.IN)
                    .registerStoredProcedureParameter("remark", String.class, ParameterMode.IN)
                    .registerStoredProcedureParameter("object_id", String.class, ParameterMode.IN)
                    .registerStoredProcedureParameter("result_code", Integer.class, ParameterMode.OUT)
                    .registerStoredProcedureParameter("total_balance", Integer.class, ParameterMode.OUT)
                    .setParameter("user_id", order.getDoctorId())
                    .setParameter("type", AccountDetailTypeEnum.Refund.getValue())
                    .setParameter("amount", -amount)
                    .setParameter("title", AccountDetailTypeEnum.Refund.getDisplayName())
                    .setParameter("remark", "线下就诊咨询退款")
                    .setParameter("object_id", String.valueOf(order.getOrderId()));
            storedProcedureQuery.execute();
            Integer resultCode = (Integer) storedProcedureQuery.getOutputParameterValue("result_code");
            Integer totalBalance = (Integer) storedProcedureQuery.getOutputParameterValue("total_balance");
            if (resultCode == 0) {

                // 去掉已结算的状态
                BaseResponse response = updateOfflineConsultGiveupSettlementStatus(order.getId());
                if (response.getCode() == BaseResponse.SUCCEED) {
                    return baseResponse.succeed("操作成功");
                }
                logger.error("医生账户操作成功，咨询状态处理失败", order.toString());
                return baseResponse.failed(BaseResponse.APP_ERROR, "医生账户操作成功，咨询状态处理失败");
            } else {
                logger.error("医生账户操作失败", order.toString());
                return baseResponse.failed(BaseResponse.APP_ERROR, "医生账户操作失败");
            }
        }
        return baseResponse.succeed("处理成功");
    }


    /**
     * 去掉线下就诊订单已结算的状态
     *
     * @param offlineId
     */
    private BaseResponse updateOfflineConsultGiveupSettlementStatus(int offlineId) {
        BaseResponse response = new BaseResponse();
        try {
            String sql = "update offline_consultation set settlement_status = null,settlement_time = null where id = ?";
            Object[] args = new Object[]{offlineId};
            int[] types = new int[]{Types.INTEGER};
            int count = jdbcTemplate.update(sql, args, types);
            if (count > 0) {
                return response.succeed("处理成功");
            }
        } catch (Exception e) {
            logger.error("updateOfflineConsultGiveupSettlementStatus:{}", offlineId, e.getMessage(), e);
        }
        return response.failed(BaseResponse.APP_ERROR, "处理成功");
    }


    /**
     * 发送模板消息
     */
    public void sendForPaidSuccessful(OfflineConsultation offlineConsultation, int source) {
        try {
            // source: 1-太和，2-广三
            int terminal = TerminalEnum.TAI_HE_INTERNET_HOSPITAL_PATIENT_MINI_PROGRAM.getValue();
            String page = String.format(Constants.TAI_HE_INTERNET_HOSPITAL_PATIENT_MINI_PROGRAM_OFFLINE_CONSULT_CHAT_URL, offlineConsultation.getOrderId());
            if (source == 2) {
                terminal = TerminalEnum.PatientWxApp.getValue();
                page = String.format(Constants.PATIENT_WXAPP_OFFLINE_CONSULT_CHAT_URL, offlineConsultation.getOrderId());
            }
            SnsUserInfo snsUserInfo = snsUserInfoRepository.getSnsUserInfoByUserId(offlineConsultation.getUserId(), terminal);
            String openId = !Checker.isNone(snsUserInfo) ? snsUserInfo.getOpenId() : "";

            String templateId = ConstantTemplateIds.OFFLINE_CONSULT_PAY_SUCCESS;

            Map<String, String> keywordMap = new HashMap();
            keywordMap.put("name1", offlineConsultation.getRealName());
            keywordMap.put("name2", offlineConsultation.getDoctorName());
            keywordMap.put("thing3", "请您按时前往就诊");
            weChatMsgProvider.sendMPSubscribeMessage(terminal, openId, templateId, page, keywordMap);

        } catch (Exception e) {
            logger.error("sendForPaidSuccessful(): {}", e.getMessage(), e);
        }
    }

    /**
     * 线下就诊0元支付推送小程序订阅消息
     */
    public void payOrderNotify(OfflineConsultation consultation, String openId) {
        int terminal = TerminalEnum.TAI_HE_INTERNET_HOSPITAL_PATIENT_MINI_PROGRAM.getValue();
        String templateId = ConstantTemplateIds.OFFLINE_CONSULT_PAY_SUCCESS;
        String page = String.format(Constants.TAI_HE_INTERNET_HOSPITAL_PATIENT_MINI_PROGRAM_OFFLINE_CONSULT_CHAT_URL, consultation.getOrderId());
        Map<String, String> keywordMap = new HashMap();
        keywordMap.put("name1", consultation.getRealName());
        keywordMap.put("name2", consultation.getDoctorName());
        keywordMap.put("thing3", "请您按时前往就诊");
        weChatMsgProvider.sendMPSubscribeMessage(terminal, openId, templateId, page, keywordMap);
    }


    public String getOrderBill(String billDate, int hospitalId) {
        String csvData = "";
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH:mm:ss");
            Date startDate = sdf.parse(billDate + "00:00:00");
            Date endDate = sdf.parse(billDate + "23:59:59");
            String startDateStr = DateUtil.convertToString(startDate, "yyyy-MM-dd HH:mm:ss");
            String endDateStr = DateUtil.convertToString(endDate, "yyyy-MM-dd HH:mm:ss");

            List paramList = new ArrayList();
            List<Integer> argTypesList = new ArrayList();

            StringBuilder sql = new StringBuilder(" Select `id`, `order_no`,`total`,`payment_no`,`payment_channel`,`refund_status`,\n" +
                    " `refund_no`,`refund_time`, `create_time`,`pay_time`, `hospital_id`, `hospital_name`\n" +
                    "  from `offline_consultation` where `pay_time` >= ? and \n" +
                    " `pay_time` <= ? and `payment_status` = 2 and total > 0 ");
            paramList.add(startDateStr);
            argTypesList.add(Types.VARCHAR);

            paramList.add(endDateStr);
            argTypesList.add(Types.VARCHAR);

            if (hospitalId > 0) {
                sql.append(" and (`hospital_id` = ? ) ");
                paramList.add(hospitalId);
                argTypesList.add(Types.INTEGER);
            }
            sql.append(" order by `pay_time` asc;");

            int size = argTypesList.size();
            int[] argTypes = new int[size];
            for (int i = 0; i < size; i++) {
                argTypes[i] = argTypesList.get(i);
            }
            SqlRowSet sqlRowSet = jdbcTemplate.queryForRowSet(sql.toString(), paramList.toArray(), argTypes);
            String[] fileHeader = new String[]{
                    "业务编码ID", "订单号", "支付流水号", "业务类型", "金额", "支付渠道标识", "支付渠道说明", "下单时间", "支付时间", "退款金额", "退款时间", "退款流水号", "医院ID", "医院名称"
            };

            StringBuffer stringBuffer = new StringBuffer();
            CSVPrinter csvPrinter = new CSVPrinter(stringBuffer, CSVFormat.DEFAULT.withHeader(fileHeader));
            SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String bizName = CommonBillTypeEnum.OFFLINE_CONSULTATION.getDisplayName();
            while (sqlRowSet.next()) {
                // 存在 payment_channel 值为空的情况，此时默认 payment_channel = 1
                int payCh = Checker.getIntegerValue(sqlRowSet.getInt("payment_channel"), 1);
                String payChName = PaymentChannelEnum.valueOf(payCh).getDisplayName();
                int hospitalIdInt = Checker.getIntegerValue(sqlRowSet.getInt("hospital_id"));
                String hospitalName = Checker.getStringValue(sqlRowSet.getString("hospital_name"));
                csvPrinter.printRecord(sqlRowSet.getString("id"), sqlRowSet.getString("order_no"), sqlRowSet.getString("payment_no"),
                        bizName, sqlRowSet.getInt("total"), payCh, payChName, sdf2.format(sqlRowSet.getDate("create_time")),
                        sdf2.format(sqlRowSet.getDate("pay_time")), 0, "", "", hospitalIdInt, hospitalName);
            }

            //退款账单
            sql = new StringBuilder("  Select `id`,`order_no`,`total`,`payment_no`,`payment_channel`,`refund_status`,\n" +
                    " `refund_no`,`refund_time`,`create_time`,`pay_time`, `hospital_id`, `hospital_name`\n" +
                    "  from `offline_consultation` where `refund_time` >= ? and \n" +
                    " `refund_time` <= ? and `payment_status` = 2 and total > 0 ");
            if (hospitalId > 0) {
                sql.append(" and (`hospital_id` = ? ) ");
                paramList.add(hospitalId);
            }
            sql.append(" and refund_status = 1 order by `refund_time` asc;");
            sqlRowSet = jdbcTemplate.queryForRowSet(sql.toString(), paramList.toArray(), argTypes);

            while (sqlRowSet.next()) {
                int payCh = Checker.getIntegerValue(sqlRowSet.getInt("payment_channel"), 1);
                String payChName = PaymentChannelEnum.valueOf(payCh).getDisplayName();
                int hospitalIdInt = Checker.getIntegerValue(sqlRowSet.getInt("hospital_id"));
                String hospitalName = Checker.getStringValue(sqlRowSet.getString("hospital_name"));
                csvPrinter.printRecord(sqlRowSet.getString("id"), sqlRowSet.getString("order_no"), sqlRowSet.getString("payment_no"),
                        bizName, 0, payCh, payChName, sdf2.format(sqlRowSet.getDate("create_time")),
                        sdf2.format(sqlRowSet.getDate("pay_time")), sqlRowSet.getInt("total"), sdf2.format(sqlRowSet.getDate("refund_time")),
                        sqlRowSet.getString("refund_no"), hospitalIdInt, hospitalName);
            }

            csvPrinter.flush();
            csvData = stringBuffer.toString();
        } catch (Exception e) {
            logger.error("OfflineConsultationService#getOrderBill(): {}", e.getMessage(), e);
        }
        return csvData;
    }
}
