package com.xxfc.platform.universal.service;

import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.xxfc.platform.order.entity.OrderInvoice;
import com.xxfc.platform.order.feign.OrderFeign;
import com.xxfc.platform.universal.biz.InvoiceQueryErrorBiz;
import com.xxfc.platform.universal.constant.InvoiceStatusEum;
import com.xxfc.platform.universal.entity.Invoice;
import com.xxfc.platform.universal.entity.InvoiceQueryError;
import com.xxfc.platform.universal.entity.OrderData;
import com.xxfc.platform.universal.entity.vo.InvoiceVo;
import com.xxfc.platform.universal.mapper.InvoiceMapper;
import com.xxfc.platform.universal.service.invoice.InvoiceDao;
import com.xxfc.platform.universal.utils.NumberToCN;
import com.xxfc.platform.universal.utils.SnowflakeIdWorker;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 发票服务
 *
 * @author Administrator
 * @Date: 2019/12/31 10:05
 * @Description:
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @_({@Autowired}))
public class InvoiceBiz extends BaseBiz<InvoiceMapper, Invoice> {

    private final InvoiceDao invoiceDao;

    private final InvoiceQueryErrorBiz invoiceQueryErrorBiz;
    private final OrderFeign orderFeign;
    private static final SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(0, 0);


    @Value("${invoice.salerName}")
    private String salerName;
    @Value("${invoice.salerTel}")
    private String salerTel;
    @Value("${invoice.salerTaxNum}")
    private String salerTaxNum;
    @Value("${invoice.salerAddress}")
    private String salerAddress;
    @Value("${invoice.salerAccount}")
    private String salerAccount;
    /**
     * 收款人
     */
    @Value("${invoice.payee}")
    private String payee;
    /**
     * 复核人
     */
    @Value("${invoice.checker}")
    private String checker;
    /**
     * 开票人
     */
    @Value("${invoice.clerk}")
    private String clerk;

    /**
     * 推送方式:-1,不推送;0,邮箱;1,手机（默认）;2,邮箱、手机
     */
    @Value("${invoice.pushMode}")
    private Integer pushMode;
    private static final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(8);

    /**
     * 通过公司代码获取公司税号
     *
     * @param code
     * @return
     */
    public String getDutyParagraphByCode(String code) {
        return invoiceDao.getDutyParagraphByCode(code);
    }

    /**
     * 通过公司名获取公司全名和公司代码
     *
     * @param name
     * @return
     */
    public List<Map<String, String>> corporateName(String name) {
        return invoiceDao.corporateName(name);
    }

    /**
     * 开票
     *
     * @param invoiceVo
     * @return
     * @throws Exception
     */
    public String Invoicing(InvoiceVo invoiceVo) throws Exception {
        //没有填写邮箱不需要进行推送
        if (StringUtils.isBlank(invoiceVo.getPurchaserEmail())) {
            pushMode = -1;
        }

        Invoice invoice = new Invoice();
        long on = snowflakeIdWorker.nextId();
        BeanUtils.copyProperties(invoiceVo, invoice);
        invoice.setOrderNo(String.valueOf(on));
        invoice.setSalerName(salerName);
        invoice.setSalerTaxCode(salerTaxNum);
        invoice.setSalerAddress(salerAddress);
        invoice.setSalerPhone(salerTel);
        invoice.setSalerAccountBank(salerAccount);
        invoice.setPayee(payee);
        invoice.setReviewer(checker);
        invoice.setIssuer(clerk);
        invoice.setPushMode(pushMode);

        Date invoiceDate = new Date();
        invoice.setCreateTime(invoiceDate);
        invoice.setInvoiceDate(invoiceDate);
        List<OrderData> orders = invoiceVo.getOrders();


        //调用第三方开票
        String invoiceSerialNum = invoiceDao.Invoicing(invoice, orders);

        insertInvoic(invoice, invoiceSerialNum, orders);
        return invoiceSerialNum;
    }

    /**
     * 保存发票信息
     *
     * @param invoice
     * @param invoiceSerialNum
     * @param orders
     * @throws Exception
     */
    public void insertInvoic(Invoice invoice, String invoiceSerialNum, List<OrderData> orders) throws Exception {

        invoice.setInvoiceSerialNum(invoiceSerialNum);
        BigDecimal taxExcludedAmount = BigDecimal.ZERO;
        BigDecimal taxIncludedAmount = BigDecimal.ZERO;
        for (OrderData order : orders) {
            if (order.getWithTaxFlag() == 1) {
                //含价税合计
                BigDecimal taxIncluded = order.getPrice();
                //税率
                BigDecimal taxRate = order.getTaxRate();
                //不含税单价
                BigDecimal TaxFree = taxIncluded.divide(taxRate.add(new BigDecimal(1)), 2, BigDecimal.ROUND_HALF_UP);
                taxExcludedAmount = taxExcludedAmount.add(TaxFree);
                taxIncludedAmount = taxIncludedAmount.add(taxIncluded);
            } else {
                //不含税单价
                BigDecimal taxFree = order.getPrice();
                //税率
                BigDecimal taxRate = order.getTaxRate();
                //含税单价
                BigDecimal taxIncluded = taxFree.multiply(taxRate.add(new BigDecimal(1)));
                taxExcludedAmount = taxExcludedAmount.add(taxFree);
                taxIncludedAmount = taxIncludedAmount.add(taxIncluded);
            }
        }
        invoice.setInvoiceAmount(taxExcludedAmount);
        invoice.setTaxAmount(taxIncludedAmount.subtract(taxExcludedAmount));
        invoice.setInvoiceSummaryAmount(taxIncludedAmount);
        invoice.setInvoiceSummaryAmountText(NumberToCN.number2CNMontrayUnit(taxIncludedAmount));

        //保存到数据库
        mapper.insert(invoice);

        //异步查询开票结果
        schedule(invoiceSerialNum, invoice.getId(), invoice.getOrderId(), null, 0, 5, TimeUnit.SECONDS);

    }


    /**
     * 或去查询结果
     *
     * @param invoiceSerialNum 发票流水号
     * @param id               发票id
     * @param orderId          订单id
     * @param lastStatus       上一次产需到的开票状态
     * @param count            已经查询过的次数
     */
    public void queryInvoiceResult(String invoiceSerialNum, Integer id, String orderId, Integer lastStatus, Integer count) {
        if (count == 2) {
            InvoiceQueryError invoiceQueryError = new InvoiceQueryError();
            invoiceQueryError.setInvoiceId(id);
            invoiceQueryError.setInvoiceSerialNum(invoiceSerialNum);
            invoiceQueryError.setCreTime(new Date());
            invoiceQueryError.setStatus(lastStatus);
            invoiceQueryErrorBiz.insertSelectiveRe(invoiceQueryError);
            return;
        }

        List<Map<String, Object>> result = invoiceResult(invoiceSerialNum);
        if (result == null) {
            schedule(invoiceSerialNum, id, orderId, null, ++count, 2, TimeUnit.HOURS);
            return;
        }
        log.info("查询到的开票结果={}", result);
        Map<String, Object> data = result.get(0);
        Integer status = (Integer) data.get("status");
        String invoiceFileUrl = (String) data.get("invoiceFileUrl");
        String invoiceCode = (String) data.get("invoiceCode");
        String invoiceNum = (String) data.get("invoiceNum");
        String taxIncludedAmountStr = String.valueOf(data.get("taxIncludedAmount"));
        String taxExcludedAmountStr = String.valueOf(data.get("taxExcludedAmount"));

        Invoice invoice = new Invoice();
        invoice.setId(id);
        invoice.setStatus(status);
        invoice.setUpdateTime(new Date());
        if (StringUtils.isNotBlank(taxExcludedAmountStr) && StringUtils.isNotBlank(taxIncludedAmountStr)) {
            BigDecimal taxExcludedAmount = new BigDecimal(taxExcludedAmountStr);
            BigDecimal taxIncludedAmount = new BigDecimal(taxIncludedAmountStr);
            invoice.setInvoiceAmount(taxExcludedAmount);
            invoice.setTaxAmount(taxIncludedAmount.subtract(taxExcludedAmount));
            invoice.setInvoiceSummaryAmount(taxIncludedAmount);
            invoice.setInvoiceSummaryAmountText(NumberToCN.number2CNMontrayUnit(taxIncludedAmount));
        }
        if (InvoiceStatusEum.ONGOING.getCode().equals(status) || InvoiceStatusEum.UNBILLED.getCode().equals(status)) {
            schedule(invoiceSerialNum, id, orderId, status, ++count, 2, TimeUnit.HOURS);
        } else if (InvoiceStatusEum.SUCCESS.getCode().equals(status)) {
            invoice.setInvoiceCode(invoiceCode);
            invoice.setInvoiceNo(invoiceNum);
            invoice.setPictureUrl(invoiceFileUrl);

            setOrderInvoice(orderId, status, invoiceFileUrl);
        } else {
            setOrderInvoice(orderId, status, null);
        }
        mapper.updateByPrimaryKeySelective(invoice);

    }

    /**
     * 设置订单发票表
     *
     * @param orderId        订单id
     * @param status         发票查询状态
     * @param invoiceFileUrl 发票pdf地址
     */
    public void setOrderInvoice(String orderId, Integer status, String invoiceFileUrl) {
        //设置发票订单表
        OrderInvoice orderInvoice = new OrderInvoice();
        orderInvoice.setOrderIds(orderId);
        orderInvoice.setInvoiceUrl(invoiceFileUrl);
        orderInvoice.setStatus(status);
        orderFeign.updateByOrderId(orderInvoice);
    }

    /**
     * 再次查询开票结果
     *
     * @param invoiceSerialNum
     * @param id
     * @param orderId
     * @param lastStatus
     * @param count
     * @param time
     * @param timeUnit
     */
    public void schedule(String invoiceSerialNum, Integer id, String orderId, Integer lastStatus, int count, int time, TimeUnit timeUnit) {
        executorService.schedule(() -> {
            queryInvoiceResult(invoiceSerialNum, id, orderId, lastStatus, count);
        }, time, timeUnit);
    }

    /**
     * 获取开票结果
     *
     * @param invoiceSerialNum
     * @return
     */
    public List<Map<String, Object>> invoiceResult(String invoiceSerialNum) {
        return invoiceDao.invoiceResult(invoiceSerialNum);
    }
}
