package com.fdkankan.ucenter.service.impl;

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.extension.plugins.pagination.Page;
import com.fdkankan.common.constant.AppConstant;
import com.fdkankan.common.constant.ErrorCode;
import com.fdkankan.common.constant.ServerCode;
import com.fdkankan.common.constant.TbStatus;
import com.fdkankan.common.exception.BusinessException;
import com.fdkankan.ucenter.common.PageInfo;
import com.fdkankan.ucenter.entity.*;
import com.fdkankan.ucenter.mapper.IInvoiceMapper;
import com.fdkankan.ucenter.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fdkankan.ucenter.vo.request.InvoiceParam;
import com.fdkankan.ucenter.vo.request.PlaceOrderParam;
import com.fdkankan.ucenter.vo.response.OrderVo;
import com.sun.xml.bind.v2.model.core.ID;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.weaver.ast.Or;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 发票表 服务实现类
 * </p>
 *
 * @author 
 * @since 2022-07-13
 */
@Service
public class InvoiceServiceImpl extends ServiceImpl<IInvoiceMapper, Invoice> implements IInvoiceService {

    @Autowired
    IUserService userService;
    @Autowired
    IOrderService orderService;
    @Autowired
    IIncrementOrderService incrementOrderService;
    @Autowired
    IVirtualOrderService virtualOrderService;
    @Autowired
    IExpansionOrderService expansionOrderService;
    @Autowired
    IDownloadOrderService downloadOrderService;

    @Override
    public Invoice getByOrderId(Long orderId) {
        LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Invoice::getOrderId,orderId);
        List<Invoice> list = this.list(wrapper);
        if(list != null && list.size() >0){
            return list.get(0);
        }
        return null;
    }

    @Override
    public Invoice getByIncrementOrderId(Long incrementOrderId) {
        LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Invoice::getIncrementOrderId,incrementOrderId);
        List<Invoice> list = this.list(wrapper);
        if(list != null && list.size() >0){
            return list.get(0);
        }
        return null;
    }

    @Override
    public HashMap<Long, Invoice> getByIncrementOrderIds(Set<Long> ids) {
        HashMap<Long, Invoice> map = new HashMap<>();
        if(ids.size() >0){
            LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(Invoice::getIncrementOrderId,ids);
            List<Invoice> list = this.list(wrapper);
            list.forEach(entity ->map.put(entity.getIncrementOrderId(),entity));
        }

        return map;
    }

    @Override
    public Invoice getByDownId(Long downId) {
        LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Invoice::getDownloadOrderId,downId);
        List<Invoice> list = this.list(wrapper);
        if(list != null && list.size() >0){
            return list.get(0);
        }
        return null;
    }

    @Override
    public void saveByOrder(Long userId, PlaceOrderParam orderParam) {
        InvoiceParam param = orderParam.getInvoice();
        Invoice invoiceEntity = new Invoice();
        invoiceEntity.setUserId(userId);
        invoiceEntity.setType(Integer.valueOf(param.getInvoiceType()));
        invoiceEntity.setTitle(param.getTitle());
        invoiceEntity.setCode(param.getCode());
        invoiceEntity.setEmailAddress(param.getEmailAddress());

        if (3 == invoiceEntity.getType()){
            invoiceEntity.setOrganizedAddress(param.getOrganizedAddress());
            invoiceEntity.setRegisterPhone(param.getRegisterPhone());
            invoiceEntity.setBankName(param.getBankName());
            invoiceEntity.setBankAccount(param.getBankAccount());
        }
        invoiceEntity.setConsumeType(0);
        invoiceEntity.setOrderId(orderParam.getOrderId());
        invoiceEntity.setShipName(orderParam.getReceiver().getShipName());
        invoiceEntity.setShipMobile(orderParam.getReceiver().getShipMobile());
        invoiceEntity.setShipAddress(orderParam.getReceiver().getShipAddress());
        invoiceEntity.setShipAreaPath(orderParam.getReceiver().getShipAreaPath());
        invoiceEntity.setEmailAddress(param.getEmailAddress());

        invoiceEntity.setMoney(new BigDecimal(param.getAmount()));
        this.save(invoiceEntity);
    }

    @Override
    public void checkInvoice(Long orderId, Integer consumeType) {
        LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
        switch (consumeType){
            case 0 : wrapper.eq(Invoice::getOrderId,orderId); break;
            case 1 : wrapper.eq(Invoice::getVirtualOrderId,orderId); break;
            case 2 :
            case 3 : wrapper.eq(Invoice::getIncrementOrderId,orderId); break;
        }
        long count =  this.count(wrapper);
        if(count >0){
            throw new BusinessException(AppConstant.FAILURE_CODE_4021, AppConstant.FAILURE_MSG_4021);
        }
    }

    @Override
    public BigDecimal getMoney(Long orderId, Integer consumeType) {
        LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
        BigDecimal money = BigDecimal.ZERO;
        switch (consumeType){
            case 0 :
                Order order = orderService.getById(orderId);
                if(order.getPaymentStatus().equals("paid")){
                    money = order.getTotalAmount();
                }
                break;
            case 1 :
                VirtualOrder virtualOrder = virtualOrderService.getById(orderId);
                if(virtualOrder.getPayStatus() == 1){
                    money = virtualOrder.getAmount();
                }
                break;
            case 2 :
                IncrementOrder incrementOrder = incrementOrderService.getById(orderId);
                if(incrementOrder.getPayStatus() == 1){
                    money = incrementOrder.getAmount();
                }
                break;

            case 3 :
                DownloadOrder downloadOrder = downloadOrderService.getById(orderId);
                if(downloadOrder.getPayStatus() == 1){
                    money = downloadOrder.getAmount();
                }
                break;
        }
        return money;
    }

    @Override
    public Invoice openInvoice(InvoiceParam param, String token) {
        User user = userService.getByToken(token);
        //验证是否已经开票
        this.checkInvoice(param.getOrderId(),param.getConsumeType());
        //获取开票金额
        BigDecimal money = this.getMoney(param.getOrderId(),param.getConsumeType());

        Invoice dbEntity = new Invoice();

        BeanUtils.copyProperties(param, dbEntity);

        dbEntity.setOrderId(null);
        switch (param.getConsumeType()){
            case 0: dbEntity.setOrderId(param.getOrderId()); break;
            case 1: dbEntity.setVirtualOrderId(param.getOrderId()); break;
            case 2: dbEntity.setIncrementOrderId(param.getOrderId()); break;
            case 3: dbEntity.setDownloadOrderId(param.getOrderId()); break;
        }

        if (param.getCameraId() != null){
            dbEntity.setCameraId(param.getCameraId());
        }
        dbEntity.setUserId(user.getId());
        dbEntity.setMoney(money);
        this.save(dbEntity);

        return dbEntity;
    }

    @Override
    public JSONObject getMaxInvoice(InvoiceParam param) {
        JSONObject object = new JSONObject();
        int maxInvoice = this.getMaxInvoice(param.getCameraId());
        object.put("maxInvoice", maxInvoice < 0 ? 0 : maxInvoice);
        return object;
    }



    private Integer getMaxInvoice(Long cameraId){
        List<VirtualOrder> virtualOrderList = virtualOrderService.getByCameraId(cameraId);
        int maxInvoice = 0;
        for (VirtualOrder virtualOrderEntity : virtualOrderList){
            if (virtualOrderEntity.getStatus() == 1 && virtualOrderEntity.getPayStatus() == 1){
                maxInvoice += virtualOrderEntity.getPoints();
            }else if (virtualOrderEntity.getStatus() == -2){
                maxInvoice -= virtualOrderEntity.getPoints();
            }
        }
        List<ExpansionOrder> expansionOrderList =   expansionOrderService.getByCameraId(cameraId);
        for(ExpansionOrder expansionOrderEntity : expansionOrderList){
            if(expansionOrderEntity.getPayStatus() == 1){
                maxInvoice += expansionOrderEntity.getAmount().intValue();
            }else if (expansionOrderEntity.getPayStatus() == -2){
                maxInvoice -= expansionOrderEntity.getAmount().intValue();
            }
        }
        LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Invoice::getCameraId,cameraId);
        wrapper.eq(Invoice::getConsumeType,1);
        List<Invoice> list = this.list(wrapper);
        for (Invoice entity : list){
            if (entity.getMoney() != null){
                maxInvoice -= entity.getMoney().intValue();
            }
        }
        return maxInvoice;
    }


    @Override
    public PageInfo getInvoices(String token, InvoiceParam param) {
        LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
        if(StringUtils.isNotBlank(token)){
            User user = userService.getByToken(token);
            wrapper.eq(Invoice::getUserId,user.getId());
        }
        if(param.getCameraId() != null){
            wrapper.eq(Invoice::getCameraId,param.getCameraId());
        }
        if(param.getType() != null){
            if(param.getType() != 0){
                wrapper.ne(Invoice::getType,0);
            }else {
                wrapper.eq(Invoice::getType,1);
            }
        }
        wrapper.ne(Invoice::getConsumeType ,-1);
        wrapper.orderByDesc(Invoice::getCreateTime);
        Page<Invoice> page = this.page(new Page<>(param.getPageNum(), param.getPageSize()), wrapper);
        return PageInfo.PageInfo(page);
    }

    @Override
    public Invoice getMyInvoiceInfo(String token, InvoiceParam param) {
        User user = userService.getByToken(token);
        return getMineInvoice(user.getId(),param.getInvoiceType());
    }

    private Invoice getMineInvoice(Long userId,String invoiceType){
        LambdaQueryWrapper<Invoice> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Invoice::getUserId,userId);
        wrapper.eq(Invoice::getConsumeType ,-1);
        wrapper.eq(Invoice::getType ,invoiceType);
        wrapper.last("LIMIT 1");
        List<Invoice> list = this.list(wrapper);
        if(list == null || list.size()<=0){
            return null;
        }
        return list.get(0);
    }

    @Override
    public void saveInvoice(String token, InvoiceParam param) {
        User user = userService.getByToken(token);
        Invoice dbEntity = getMineInvoice(user.getId(),param.getInvoiceType());
        if (dbEntity == null){
            dbEntity = new Invoice();
        }
        if (param.getCameraId() != null){
            dbEntity.setCameraId(param.getCameraId());
        }
        dbEntity.setType(Integer.valueOf(param.getInvoiceType()));
        dbEntity.setUserId(user.getId());
        dbEntity.setBankAccount(param.getBankAccount());
        dbEntity.setBankName(param.getBankName());
        dbEntity.setRegisterPhone(param.getRegisterPhone());
        dbEntity.setOrganizedAddress(param.getOrganizedAddress());
        dbEntity.setCode(param.getCode());
        dbEntity.setTitle(param.getTitle());
        dbEntity.setEmailAddress(param.getEmailAddress());
        dbEntity.setConsumeType(-1);
        if(!this.saveOrUpdate(dbEntity)){
            throw new BusinessException(ErrorCode.ERROR_MSG);
        }
    }

    @Override
    public void addInvoice(String token, InvoiceParam param) {
        User user = userService.getByToken(token);
        if(param.getAmount() == null){
            throw new BusinessException(ErrorCode.MISSING_REQUIRED_PARAMETERS);
        }
        int maxInvoice = this.getMaxInvoice(param.getCameraId());

        if(Integer.parseInt(param.getAmount()) > maxInvoice){
            throw new BusinessException(ErrorCode.FAILURE_CODE_8003);
        }
        saveOrUpdateInvoice(user.getId(),param,null);
    }

    @Override
    public void updateInvoice(String token, InvoiceParam param) {
        User user = userService.getByToken(token);
        if(param.getInvoiceId() != null){
            Invoice invoiceEntity = this.getById(param.getInvoiceId());
            if(invoiceEntity == null){
                throw new BusinessException(ErrorCode.NOT_RECORD);
            }
             this.saveOrUpdateInvoice(user.getId(), param,invoiceEntity);

        }else {

            OrderVo order = orderService.getOrderDetail(param.getOrderId());
            if(order == null || order.getTotalAmount() == null){
                throw new BusinessException(ErrorCode.ERROR_MSG);
            }
            param.setAmount(order.getTotalAmount().toString());
            this.saveOrUpdateInvoice(user.getId(),param,null);
        }

    }

    private void saveOrUpdateInvoice(Long userId, InvoiceParam param, Invoice invoiceEntity){
        if(invoiceEntity == null){
            invoiceEntity = new Invoice();
            invoiceEntity.setUserId(userId);
            invoiceEntity.setOrderId(param.getOrderId());
        }
        invoiceEntity.setType(Integer.valueOf(param.getInvoiceType()));
        invoiceEntity.setTitle(param.getTitle());
        invoiceEntity.setCode(param.getCode());

        if (3 == invoiceEntity.getType()){
            invoiceEntity.setOrganizedAddress(param.getOrganizedAddress());
            invoiceEntity.setRegisterPhone(param.getRegisterPhone());
            invoiceEntity.setBankName(param.getBankName());
            invoiceEntity.setBankAccount(param.getBankAccount());
        }

        invoiceEntity.setCameraId(param.getCameraId());
        invoiceEntity.setMoney(new BigDecimal(param.getAmount()));
        invoiceEntity.setConsumeType(param.getConsumeType() == null? 1 : param.getConsumeType());

        invoiceEntity.setShipName(param.getShipName());
        invoiceEntity.setShipMobile(param.getShipMobile());
        invoiceEntity.setShipAddress(param.getShipAddress());
        invoiceEntity.setShipAreaPath(param.getShipAreaPath());
        invoiceEntity.setCameraId(param.getCameraId());
        invoiceEntity.setEmailAddress(param.getEmailAddress());
        if(!this.saveOrUpdate(invoiceEntity)){
            throw new BusinessException(ErrorCode.ERROR_MSG);
        }
    }
}
