package com.yuchen.saas.park.service.service.property.bill;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.yuchen.saas.park.api.dto.property.CreateBillDto;
import com.yuchen.saas.park.api.dto.property.PropertyBusinessCreateBillDto;
import com.yuchen.saas.park.api.dto.property.PropertyMeterCreateBillDto;
import com.yuchen.saas.park.api.dto.property.PropertyTempBillCreateDto;
import com.yuchen.saas.park.api.entity.property.PropertyBill;
import com.yuchen.saas.park.api.entity.property.PropertyBillSub;
import com.yuchen.saas.park.api.entity.property.PropertyFeeItem;
import com.yuchen.saas.park.api.entity.property.PropertyFeeStandard;
import com.yuchen.saas.park.api.enums.property.*;
import com.yuchen.saas.park.api.vo.property.PropertyTenantOwnerVo;
import com.yuchen.saas.park.service.constant.PropertyCacheConstant;
import com.yuchen.saas.park.service.service.property.IPropertyFeeItemService;
import com.yuchen.saas.park.service.service.property.IPropertyHouseResidentService;
import com.yuchen.saas.park.service.service.property.PropertyBillingSettingsService;
import com.yuchen.saas.park.service.util.ManualValidUtil;
import com.yuchen.saas.parking.api.util.DateUtils;
import com.yuchen.saas.payment.api.feign.ISaasInvoiceTaxCategoryFeign;
import com.yuchen.saas.payment.api.vo.invoice.SaasInvoiceTaxCategoryVO;
import lombok.extern.slf4j.Slf4j;
import org.nest.springwrap.core.log.exception.ServiceException;
import org.nest.springwrap.core.redis.cache.NestRedis;
import org.nest.springwrap.core.secure.utils.AuthUtil;
import org.nest.springwrap.core.tool.api.R;
import org.nest.springwrap.core.tool.utils.Func;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.groups.Default;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class BasePropertyBillService {
    @Resource
    private ManualValidUtil manualValidUtil;
    @Resource
    private IPropertyHouseResidentService propertyHouseResidentService;
    @Resource
    private PropertyBillingSettingsService propertyBillingSettingsService;
    @Resource
    private IPropertyFeeItemService propertyFeeItemService;
    @Resource
    private NestRedis nestRedis;
    @Resource
    private ISaasInvoiceTaxCategoryFeign iSaasInvoiceTaxCategoryFeign;



    /**
     * 入参校验
     * @param createBillDto
     */
    @SuppressWarnings("unchecked")
    protected void validParam(CreateBillDto createBillDto) {
        if (createBillDto.getCreateBillTypeEnum() == null) {
            throw new ServiceException("账单生成类型参数不能为空");
        }
        List<Class> groupList = CollUtil.newArrayList(Default.class);
        switch (createBillDto.getCreateBillTypeEnum()) {
            case CYCLE:
                manualValidUtil.valid(createBillDto.getCycleCreateBillDto(), groupList);
                return;
            case METER:
                if (Func.isEmpty(createBillDto.getMeterCreateBillDtoList())) {
                    throw new ServiceException("生成抄表账单参数不能为空");
                }
                List<PropertyMeterCreateBillDto> billList = createBillDto.getMeterCreateBillDtoList();
                billList.forEach(param -> {
                    manualValidUtil.valid(param, groupList);
                });
                return;
            case SHARE_FEE:
                manualValidUtil.valid(createBillDto.getShareFeeCreateBillDto(), groupList);
                return;
            case DEPOSIT:
                manualValidUtil.valid(createBillDto.getDepositCreateBillDto(), groupList);
                return;
            case TEMPORARY:
                if (Func.isEmpty(createBillDto.getTempBillCreateDtoList())) {
                    throw new ServiceException("临时收费参数不能为空");
                }
                List<PropertyTempBillCreateDto> tempBillList = createBillDto.getTempBillCreateDtoList();
                tempBillList.forEach(param -> {
                    manualValidUtil.valid(param, groupList);
                });
                return;
            case BILL_SPLIT:
                manualValidUtil.valid(createBillDto.getSlipCreateBillDto(), groupList);
                return;
            case BUSINESS:
                if (Func.isEmpty(createBillDto.getBusinessCreateBillDtoList())) {
                    throw new ServiceException("经营性收费参数不能为空");
                }
                List<PropertyBusinessCreateBillDto> busiBillList = createBillDto.getBusinessCreateBillDtoList();
                busiBillList.forEach(param -> {
                    manualValidUtil.valid(param, groupList);
                });
                return;
            default:

        }
    }


     /**
     * 账单公用属性组装
     * @param fee
     * @param createBillTypeEnum
     * @return
     */
    protected PropertyBill assembleBaseBill(PropertyFeeStandard fee, CreateBillTypeEnum createBillTypeEnum) {
//        PropertyHouses houses = propertyHousesService.getById(dto.getHouseId());
        PropertyBill bill = new PropertyBill();
        bill.setParkId(fee.getParkId());
//        bill.setBuildingId(houses.getBuildingId());
//        bill.setHouseId(dto.getHouseId());
        bill.setFeeItemId(fee.getFeeItemId());
        bill.setFeeId(fee.getId());
        bill.setBillStatus(BillStatusEnum.NO_OUT.getValue());
        bill.setPayStatus(PayStatusEnum.NO_PAY.getValue());
        bill.setPriceFen(fee.getPriceFen());
        bill.setPriceUnitType(fee.getPriceUnitType());
        bill.setCreateUser(AuthUtil.getUserId() == -1L ? 0L : AuthUtil.getUserId());
        bill.setCreateTime(new Date());
        bill.setCreateBillType(createBillTypeEnum.getValue());
//        bill.setOutBillTime(new Date());
        return bill;
    }

    /**
     * 校验费用项目是否已开启
     * @param feeItemId
     * @return
     */
    protected Boolean checkFeeItemStatus(Long feeItemId) {
        String key = PropertyCacheConstant.BILL_CYCLE_FEE_ITEM + ":" + feeItemId;
        Boolean flag = nestRedis.get(key);
        if (flag == null) {
            PropertyFeeItem feeItem = propertyFeeItemService.getById(feeItemId);
            flag = feeItem != null && feeItem.getEnableStatus() != YesOrNoEnum.NO.getValue();
            nestRedis.setEx(key,flag,300L);
        }
        return flag;
    }

    /**
     * 费用项目税率
     * @param feeItemId
     * @return
     */
    @Cacheable(cacheNames = PropertyCacheConstant.BILL_CYCLE_FEE_ITEM_RATE + "#300", key = "#feeItemId")
    public int getFeeItemRate(Long feeItemId) {
        String key = PropertyCacheConstant.BILL_CYCLE_FEE_ITEM_RATE + ":" + feeItemId;
        Integer taxRate = nestRedis.get(key);
        if (taxRate == null) {
            PropertyFeeItem feeItem = propertyFeeItemService.getById(feeItemId);
            if (feeItem == null || feeItem.getEnableStatus() == YesOrNoEnum.NO.getValue()) {
                taxRate = 0;
            } else if (feeItem.getIsTaxInclusive() == null || feeItem.getIsTaxInclusive() == YesOrNoEnum.YES.getValue()) {
                taxRate = 0;
            } else {
                R<SaasInvoiceTaxCategoryVO> feignResult = iSaasInvoiceTaxCategoryFeign.getTaxCategoryById(feeItem.getTaxCategoryId());
                if (feignResult.isSuccess() && Func.isNotEmpty(feignResult.getData())) {
                    Integer tmpTaxRate = feignResult.getData().getGeneralTaxRate();
                    taxRate = tmpTaxRate == null ? 0 : tmpTaxRate;
                } else {
                    taxRate = 0;
                }
            }
            nestRedis.setEx(key,taxRate,300L);
        }

        return taxRate;
    }

    /**
     * 税费计算
     * @param feeItemId
     * @param receivableAmtFen
     * @return
     */
    protected long calcTaxAmtFen(Long feeItemId,long receivableAmtFen) {
        int taxRate = this.getFeeItemRate(feeItemId);
        return receivableAmtFen * taxRate / 100;
    }


    /**
     * 账单应收日期
     * @param startDate 账单开始时间
     * @param endDate 账单结束时间
     * @param fee 收费标准
     * @return
     */
    protected Date getReceivableDate(Date startDate,Date endDate,PropertyFeeStandard fee) {
        Date date;
        if (Func.isEmpty(fee.getReceivableMonth())) {
            return null;
        }
        if (fee.getReceivableMonth() == BilDateEnum.START_DATE.getValue()) {
            date = DateUtil.offsetMonth(startDate,fee.getReceivableMonthAfter());
        } else {
            date = DateUtil.offsetMonth(endDate,fee.getReceivableMonthAfter());
        }

        if (fee.getReceivableType() == 1) { //应收月份当前的月末
            date = DateUtil.endOfMonth(date);
        } else if (fee.getReceivableType() == 2) { // 应收月份当前的第X天
            date = DateUtil.parse(DateUtil.format(date,"yyyy-MM") + "-" + getDay(fee.getReceivableArg1()));
        } else if (fee.getReceivableType() == 3) {
            int d = fee.getReceivableArg2() == BilDateEnum.START_DATE.getValue() ? DateUtil.dayOfMonth(startDate) : DateUtil.dayOfMonth(endDate);
            date = DateUtil.offsetDay(DateUtil.parse(DateUtil.format(date,"yyyy-MM") + "-" + getDay(d)),fee.getReceivableArg3());
        }

        return DateUtils.getMonthLastDayDate(date);
    }


    private String getDay(int day) {
        String strDay = String.valueOf(day);
        if (strDay.length() < 2) {
            strDay = "0" + day;
        }
        return strDay;
    }


    /**
     * 滞纳金计算日期
     * @param receivableDate 应收日期
     * @param startDate 账单起始日期
     * @param endDate 账单结束日期
     * @param fee
     * @return
     */
    protected Date getBreachDate(Date receivableDate, Date startDate,Date endDate,PropertyFeeStandard fee) {
        if (fee.getBreachStatue() == YesOrNoEnum.NO.getValue()) {
            return null;
        }
        switch (BreachDateTypeEnum.convert(fee.getBreachDateType())) {
            case RECEIVABLE_DATE:
                if (receivableDate == null) {
                    return null;
                }
                return DateUtil.offsetDay(receivableDate,fee.getBreachDateVal());
            case START_DATE:
                return DateUtil.offsetDay(startDate,fee.getBreachDateVal());
            case END_DATE:
                return DateUtil.offsetDay(endDate,fee.getBreachDateVal());
            default:
                return null;
        }
    }


    /**
     * 账单子表数据组装
     * @param fee
     * @return
     */
    protected PropertyBillSub assembleBillSub(PropertyFeeStandard fee) {
        PropertyBillSub sub = new PropertyBillSub();
        sub.setFeeId(fee.getId());
        sub.setFeeMark(fee.getFeeMark());
        sub.setCycleType(fee.getCycleType());
        sub.setCycleMonthType(fee.getCycleMonthType());
        sub.setCalcType(fee.getCalcType());
        sub.setPriceDayFen(fee.getPriceDayFen());
        sub.setCreateTime(new Date());
        if (fee.getBreachStatue() == YesOrNoEnum.YES.getValue()) {
            sub.setBreachCalcType(fee.getBreachCalcType());
            sub.setBreachRate(fee.getBreachRate());
        }
        return sub;
    }

    /**
     * 小数点设置
     * @return
     */
    protected long decimalPoint(long parkId,long amtFen) {
       return propertyBillingSettingsService.getBillAmtByFen(parkId,amtFen);
    }


    /**
     * 根据房源id获取客户档案id
     */
    protected Long getHouseCustomerId(long houseId) {
        PropertyTenantOwnerVo vo = propertyHouseResidentService.getTenantOrOwnerInfo(houseId);
        if (vo != null) {
            return vo.getCustomerArchivesId();
        }
        return 0L;
    }

}
