package org.wfc.user.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.wfc.common.core.constant.WifiConstants;
import org.wfc.common.core.domain.LoginUser;
import org.wfc.common.core.utils.ResponseUtils;
import org.wfc.common.security.utils.SecurityUtils;
import org.wfc.omada.api.client.OmadaClientApi;
import org.wfc.omada.api.client.model.ClientInfo;
import org.wfc.omada.api.client.model.OperationResponseClientGridVoClientInfo;
import org.wfc.omada.api.organization.OmadaSiteApi;
import org.wfc.omada.api.organization.model.OperationResponseGridVoSiteSummaryInfo;
import org.wfc.omada.api.organization.model.SiteSummaryInfo;
import org.wfc.user.api.IWifiApi;
import org.wfc.user.api.domain.bo.UClientBo;
import org.wfc.user.api.omada.domain.convert.OmadaConvert;
import org.wfc.user.api.omada.domain.dto.ClientRateLimitSettingDto;
import org.wfc.user.domain.UAccount;
import org.wfc.user.domain.UBill;
import org.wfc.user.domain.UBillRule;
import org.wfc.user.domain.UClient;
import org.wfc.user.domain.constant.OrderStatusEnum;
import org.wfc.user.domain.constant.OrderTypeEnum;
import org.wfc.user.domain.vo.UAccountDashboardVo;
import org.wfc.user.domain.vo.UCdrUserVo;
import org.wfc.user.domain.vo.UClientCurrentVo;
import org.wfc.user.mapper.UAccountMapper;
import org.wfc.user.mapper.UBillMapper;
import org.wfc.user.mapper.UBillRuleMapper;
import org.wfc.user.mapper.UCdrMapper;
import org.wfc.user.mapper.UClientMapper;
import org.wfc.user.service.IUAccountService;
import org.wfc.user.service.IUClientService;
import org.wfc.user.util.AccountUtil;
import org.wfc.user.util.BillRuleUtil;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户平台-账户表 服务实现类
 * </p>
 *
 * @author sys
 * @since 2024-12-23
 */
@Slf4j
@Service
public class UAccountServiceImpl extends ServiceImpl<UAccountMapper, UAccount> implements IUAccountService {

    @Autowired
    private UCdrMapper cdrMapper;

    @Autowired
    private IWifiApi wifiApi;

    @Autowired
    private UClientMapper clientMapper;

    @Autowired
    private UBillMapper billMapper;

    @Autowired
    private UBillRuleMapper billRuleMapper;

    @Autowired
    private OmadaSiteApi omadaSiteApi;

    @Autowired
    private OmadaClientApi omadaClientApi;

    @Autowired
    @Lazy
    private IUClientService uClientService;

    @Transactional(rollbackFor = Exception.class)
    public void statAndCancelAuthUser() {
        // 定时任务查询所有未过期或刚过期（失效时间+定时任务间隔时间）的账户套餐，套餐过期/流量用完/时长用完则取消授权
        Date current = new Date();
        // 查出
        List<UAccount> accounts = this.list(Wrappers.<UAccount>lambdaQuery()
                .and(wrapper -> wrapper.isNotNull(UAccount::getBalance).gt(UAccount::getBalance, 0).or()
                        .le(UAccount::getStartTime, current).gt(UAccount::getEndTime, current))
                .isNotNull(UAccount::getUserId));

        List<UAccount> packageAccounts = accounts.stream().filter(account -> AccountUtil.isPackageValid(account, current))
                .collect(Collectors.toList());

        List<UAccount> balanceAccounts = accounts.stream().filter(account -> !AccountUtil.isPackageValid(account, current))
                .collect(Collectors.toList());

        // 统计已使用余额
        if (CollUtil.isNotEmpty(balanceAccounts)) {
            List<UBillRule> billRules = billRuleMapper.selectList(Wrappers.<UBillRule>lambdaQuery().eq(UBillRule::getEnable, true));
            Optional<UBillRule> billRuleOptional = billRules.stream().findFirst();
            if (billRuleOptional.isPresent()) {
                UBillRule billRule = billRuleOptional.get();
                for (UAccount balanceAccount : balanceAccounts) {
                    // 根据使用流量扣费余额
                    Long currentTraffic = cdrMapper.getCurrentTraffic(balanceAccount.getUserId());
                    if (ObjectUtil.isNull(currentTraffic)) {
                        continue;
                    }
                    balanceAccount.setBalanceUsed(BillRuleUtil.calc(billRule, currentTraffic));
                }
                this.updateBatchById(balanceAccounts);
            }
        }

        // 统计并更新账户已使用流量，已使用时长
        for (UAccount account : packageAccounts) {
            if (ObjectUtil.isNull(account.getUserId())) {
                continue;
            }
            List<UCdrUserVo> statCdr = cdrMapper.getByUser(account.getUserId(), account.getStartTime().getTime(), account.getEndTime().getTime());
            statCdr.stream().findFirst().ifPresent(stat -> {
                account.setTrafficUsed(stat.getTrafficUp() + stat.getTrafficDown());
                account.setDurationUsed(stat.getDuration());
            });
        }
        this.updateBatchById(packageAccounts);

        // 取消授权
        cancelAuthClient();
    }

    private void cancelAuthClient() {
        // 查出所有已授权client, 根据client的site和mac去查当前user, 再查user的account是否有可用套餐和余额，没有则取消授权
        ResponseEntity<OperationResponseGridVoSiteSummaryInfo> siteRes = omadaSiteApi.getSiteList(WifiConstants.PAGE, WifiConstants.PAGE_SIZE);
        Date current = new Date();
        if (ObjectUtil.isNotNull(siteRes.getBody())) {
            ResponseUtils.logResponse(siteRes.getBody().getErrorCode(), siteRes.getBody().getMsg());
            List<SiteSummaryInfo> sites = siteRes.getBody().getResult().getData();
            for (SiteSummaryInfo site : sites) {
                ResponseEntity<OperationResponseClientGridVoClientInfo> clientRes = omadaClientApi.getGridActiveClients(site.getSiteId(), WifiConstants.PAGE, WifiConstants.PAGE_SIZE);
                if (ObjectUtil.isNull(clientRes.getBody())) {
                    continue;
                }
                ResponseUtils.logResponse(clientRes.getBody().getErrorCode(), clientRes.getBody().getMsg());
                List<ClientInfo> clients = clientRes.getBody().getResult().getData().stream()
                        .filter(c -> c.getAuthStatus() == WifiConstants.AUTH_STATUS_AUTHORIZED).collect(Collectors.toList());

                for (ClientInfo client : clients) {
                    UClient loginClient = clientMapper.selectOne(Wrappers.<UClient>lambdaQuery().eq(UClient::getSiteId, site.getSiteId())
                            .eq(UClient::getClientMac, client.getMac()), false);
                    if (ObjectUtil.isNull(loginClient) || ObjectUtil.isNull(loginClient.getUserId())) {
                        wifiApi.cancelAuthClient(site.getSiteId(), client.getMac());
                        continue;
                    }
                    UAccount account = this.getOne(Wrappers.<UAccount>lambdaQuery().eq(UAccount::getUserId, loginClient.getUserId()), false);
                    // 套餐失效，但是有余额且没有已使用余额，reconnect
                    if (AccountUtil.isPackageUnValidBalanceValid(account, current)) {
                        wifiApi.reconnectClient(site.getSiteId(), client.getMac());
                    }
                    // 套餐有效，有已使用余额且余额扣去已使用余额大于0，reconnect
                    if (AccountUtil.isPackageValidBalanceUnValid(account, current)) {
                        wifiApi.reconnectClient(site.getSiteId(), client.getMac());
                    }

                    if (ObjectUtil.isNull(account)) {
                        wifiApi.cancelAuthClient(site.getSiteId(), client.getMac());
                        continue;
                    }
                    if (!AccountUtil.isValid(account, current)) {
                        wifiApi.cancelAuthClient(site.getSiteId(), client.getMac());
                        continue;
                    }
                    // 设备数超出限制的话，取消在线时间短的设备
                    if (account.getClientNumEnable()) {
                        List<UClientCurrentVo> currentClients = uClientService.getCurrentClients(account.getUserId());
                        if (currentClients.size() <= account.getClientNum()) {
                            continue;
                        }
                        int limitNum = currentClients.size() - account.getClientNum();
                        List<UClientCurrentVo> cancelClients = currentClients.stream().sorted(Comparator.comparing(UClientCurrentVo::getUpTime))
                                .limit(limitNum).collect(Collectors.toList());
                        for (UClientCurrentVo cancelClient : cancelClients) {
                            wifiApi.cancelAuthClient(cancelClient.getSiteId(), cancelClient.getClientMac());
                        }
                    }
                }
            }
        }
    }

    public void authClientAndRateLimit(UClientBo client) {
        UAccount account = this.getOne(Wrappers.<UAccount>lambdaQuery().eq(UAccount::getUserId, client.getUserId()), false);
        if (ObjectUtil.isNull(account)) {
            return;
        }
        Date current = new Date();
        if (AccountUtil.isValid(account, current)) {

            if (account.getClientNumEnable()) {
                int onlineClientNum = uClientService.getCurrentClients(client.getUserId()).size();
                if (onlineClientNum >= account.getClientNum()) {
                    return;
                }
            }
            wifiApi.authClient(client.getSiteId(), client.getClientMac());

            // 带宽限速
            ClientRateLimitSettingDto clientRateLimitSetting = OmadaConvert.INSTANCE.toClientRateLimitSettingDto(account);
            wifiApi.updateClientRateLimitSetting(client.getSiteId(), client.getClientMac(), clientRateLimitSetting);
        }

    }

    @Override
    public UAccountDashboardVo getByUser() {
        LoginUser<Object> loginUser = SecurityUtils.getLoginUser();
        UAccount account = this.getOne(Wrappers.<UAccount>lambdaQuery().eq(UAccount::getUserId, loginUser.getUserid()), false);
        UAccountDashboardVo dashboardVo = new UAccountDashboardVo();
        if (ObjectUtil.isNull(account)) {
            dashboardVo.setId(loginUser.getUserid());
            dashboardVo.setBalance(BigDecimal.ZERO);
            dashboardVo.setTraffic(0L);
            dashboardVo.setDuration(0L);
            dashboardVo.setTrafficUsed(0L);
            dashboardVo.setDurationUsed(0L);
            dashboardVo.setClientNum(0);
            dashboardVo.setClientNumUsed(0);
            dashboardVo.setActivity(0L);
            dashboardVo.setStatus(0);
        } else {
            BeanUtils.copyProperties(account, dashboardVo);
            dashboardVo.setId(loginUser.getUserid());
            dashboardVo.setActivity(0L);
            BigDecimal balanceUsed = Optional.ofNullable(account.getBalanceUsed()).orElse(BigDecimal.ZERO);
            BigDecimal balance = Optional.ofNullable(dashboardVo.getBalance()).orElse(BigDecimal.ZERO);
            if (balance.compareTo(balanceUsed) >= 0) {
                dashboardVo.setBalance(balance.subtract(balanceUsed));

            } else {
                dashboardVo.setBalance(BigDecimal.ZERO);
            }
            if (AccountUtil.isPackageValid(account, new Date())) {
                dashboardVo.setStatus(1);
            } else {
                dashboardVo.setStatus(0);
            }

        }
        return dashboardVo;
    }

    public void generateBill(Long userId, Long cdrHistoryId) {
        // 更新当前账户的余额，并且生成一个bill账单
        UAccount account = this.getOne(Wrappers.<UAccount>lambdaQuery().eq(UAccount::getUserId, userId), false);
        if (ObjectUtil.isNull(account)) {
            return;
        }
        account.setBalance(Optional.ofNullable(account.getBalance()).orElse(BigDecimal.ZERO));
        account.setBalanceUsed(Optional.ofNullable(account.getBalanceUsed()).orElse(BigDecimal.ZERO));
        if (account.getBalanceUsed().compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        if (account.getBalance().compareTo(account.getBalanceUsed()) < 0) {
            account.setBalance(BigDecimal.ZERO);
        } else {
            account.setBalance(account.getBalance().subtract(account.getBalanceUsed()));
        }
        // 保存账单信息
        UBill bill = new UBill();
        bill.setAmount(account.getBalanceUsed());
        bill.setUserId(userId);
        bill.setCdrHistoryId(cdrHistoryId);
        bill.setType(OrderTypeEnum.RECHARGE.getCode());
        bill.setStatus(OrderStatusEnum.PAID.getCode());
        billMapper.insert(bill);

        account.setBalanceUsed(BigDecimal.ZERO);
        this.updateById(account);
    }

}
