package com.cyksj.web.controller.manage.group;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cyksj.common.annotation.Log;
import com.cyksj.common.annotation.NoSubmit;
import com.cyksj.common.constant.Constant;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.util.GoogleGenerator;
import com.cyksj.common.util.Jsons;
import com.cyksj.dto.Result;
import com.cyksj.enums.BusinessType;
import com.cyksj.enums.GatewayResponse;
import com.cyksj.mapper.*;
import com.cyksj.model.entity.*;
import com.cyksj.model.excel.ExcelGroupRelationData;
import com.cyksj.model.request.AccountGroupsReq;
import com.cyksj.model.request.ReplaceTripsAccountReq;
import com.cyksj.model.views.CloseGroupsTripsView;
import com.cyksj.model.views.GoodsDonSkuView;
import com.cyksj.model.views.GroupsTripsWaitingView;
import com.cyksj.service.mange.AccountCommonService;
import com.cyksj.service.mange.CmsAccountService;
import com.cyksj.service.mange.CmsGroupService;
import com.cyksj.web.util.EasyExcelUtils;
import com.ejlchina.searcher.BeanSearcher;
import com.ejlchina.searcher.SearchResult;
import com.ejlchina.searcher.param.Operator;
import com.ejlchina.searcher.util.MapBuilder;
import com.ejlchina.searcher.util.MapUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author chan
 * @date 2022/9/27 6:26 PM
 */
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/manage/group")
@RestController
public class CmsGroupController {

    private final BeanSearcher beanSearcher;

    private final HttpServletRequest request;

    private final CmsGroupService groupService;

    private final CmsAccountService accountService;

    private final GroupsRelationMapper relationMapper;

    private final AccountCommonService accountCommonService;

    private final GroupsRelationAlertMapper alertMapper;

    private final UserMapper userMapper;

    private final OrderDonMapper orderDonMapper;

    private final GroupsRelationMapper groupsRelationMapper;

    private final GoodsDonSkuMapper goodsDonSkuMapper;

    private final NetflixUserAccountStatusRecordMapper netflixUserAccountStatusRecordMapper;

    @GetMapping("/get")
    @SaCheckPermission("group:view")
    public Result<SearchResult<GroupsTrips>> get(String account, Long userId, String showId, String nickName, String submitAccount, Boolean isEmpty, String orderNo) {
        MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
        if (StrUtil.isNotBlank(showId)) {
            userId = userMapper.getUserIdByShowId(showId);
        }
        if (userId != null || StringUtils.isNotBlank(nickName) || StringUtils.isNotBlank(submitAccount)) {
            List<Long> ids = relationMapper.getAccountIdsByUserIdOrNickNameOrSubmitAccount(userId, nickName, submitAccount);
            if (ids.isEmpty()) {
                return GatewayResponse.SUCCESS.newBuilder().toResult(new SearchResult<>());
            }

            mapBuilder.field(GroupsTrips::getGroupId, ids).op(Operator.InList);
        }
        if (isEmpty != null && isEmpty) {
            mapBuilder.field(GroupsTrips::getNum, GroupsTrips::getAvailableNum).sql("$1 = $2");
        }
        if (StrUtil.isNotEmpty(orderNo)) {
            Long groupId = 0l;
            OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
                    .eq(OrderDon::getOrderNo, orderNo).last("limit 1"));
            if (orderDon != null) {
                GroupsRelation relation = relationMapper.selectById(orderDon.getRelationId());
                if (relation != null && relation.getUserId().equals(orderDon.getUserId())) {
                    groupId = relation.getGroupsId();
                }
            }
            mapBuilder.field(GroupsTrips::getId, groupId);
        }
        SearchResult<GroupsTrips> search = beanSearcher.search(GroupsTrips.class, mapBuilder
                .field(GroupsTrips::getAccount, StringUtils.isBlank(account) ? null : account).op(Operator.EndWith)
                .build()
        );
        search.getDataList().forEach(data -> {
            Number count = beanSearcher.searchCount(GroupsRelation.class, MapUtils.builder()
                    .field(GroupsRelation::getGroupsId, data.getId())
                    .field(GroupsRelation::getUserId, 0).op(Operator.NotEqual)
                    .build());
            data.setIsExists(count.intValue() > 0 ? true : false);
        });
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }

    /**
     * 导出车票列表
     */
    @GetMapping("/export")
    public void export(String account, Long userId, String showId, String nickName, String submitAccount, Boolean isEmpty, HttpServletResponse response) {
        MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
        if (StrUtil.isNotBlank(showId)) {
            userId = userMapper.getUserIdByShowId(showId);
        }
        if (userId != null || StringUtils.isNotBlank(nickName) || StringUtils.isNotBlank(submitAccount)) {
            List<Long> ids = relationMapper.getAccountIdsByUserIdOrNickNameOrSubmitAccount(userId, nickName, submitAccount);
            if (ids.isEmpty()) {
                throw BusinessRuntimeException.getInstance("暂无数据..");
            }

            mapBuilder.field(ExcelGroupRelationData::getGroupId, ids).op(Operator.InList);
        }
        if (isEmpty != null && isEmpty) {
            mapBuilder.field(ExcelGroupRelationData::getNum, ExcelGroupRelationData::getAvailableNum).sql("$1 = $2");
        }

        List<ExcelGroupRelationData> list = beanSearcher.searchAll(ExcelGroupRelationData.class, mapBuilder
                .field(ExcelGroupRelationData::getAccount, StringUtils.isBlank(account) ? null : account).op(Operator.EndWith)
                .build()
        );
        EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelGroupRelationData.class, list, "发布车票列表", "发布车票列表", ExcelTypeEnum.XLSX, null);
    }

    /**
     * 各平台待发车详情
     */
    @GetMapping("/get/plat/waiting")
    public Result<List<GroupsTripsWaitingView>> getPlatWaiting() {
        return GatewayResponse.SUCCESS.newBuilder().toResult(beanSearcher.searchAll(GroupsTripsWaitingView.class, null));
    }

    @PostMapping("/post")
    @NoSubmit
    @SaCheckPermission("group:add")
    @Log(module = "发布车票/新增车队", businessType = BusinessType.POST, isSaveRequestData = true)
    public Result<GroupsTrips> add(@RequestBody GroupsTrips groups){

        GroupsTrips groupsTrips = groupService.issuance(groups, null, false);

        return GatewayResponse.SUCCESS.newBuilder().toResult(groupsTrips);
    }

    @PutMapping("/update")
    @Log(module = "发布车票/编辑车队", businessType = BusinessType.PUT, isSaveRequestData = true)
    public Result<String> update(@RequestBody GroupsTrips groups){
        groupService.updateById(groups);
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

    @DeleteMapping("/delete/{id}")
    @Log(module = "发布车票/删除车队", businessType = BusinessType.DELETE, isSaveRequestData = true)
    public Result<String> delete(@PathVariable Long id){
        groupService.close(id);

        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

    /**
     * 配置已有账号
     */
    @PutMapping("/update/set/account")
    @Log(module = "发布车票/配置账号", businessType = BusinessType.PUT, isSaveRequestData = true)
    public Result<String> setAccount(@RequestBody GroupsTrips groupsTrips) throws Exception {
        Account account = accountService.getById(groupsTrips.getAccountId());
        if(account == null){
            throw new BusinessRuntimeException("id : " + groupsTrips.getAccount() + " 该账号不存在！");
        }
        groupService.setAccount(groupsTrips);

        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

    /**
     * 配置新账号并绑定车队
     */
    @PostMapping("/post/account/bind")
    public Result<String> createNewAccountBind(@RequestBody AccountGroupsReq req) throws Exception {
        Account account = new Account();
        BeanUtil.copyProperties(req, account);
        GroupsTrips groupsTrips = groupService.getById(req.getGroupsId());
        if (groupsTrips.getAccountId() != null) {
            throw BusinessRuntimeException.getInstance("该车队已配置账号");
        }
        Long skuId = groupsTrips.getSkuId();
        GoodsDonSkuView goodsDonSkuView = beanSearcher.searchFirst(GoodsDonSkuView.class, MapUtils.builder()
                .field(GoodsDonSkuView::getGoodsId, req.getGoodsId())
                .field(GoodsDonSkuView::getSkuId, skuId).build());
        if (goodsDonSkuView == null) {
            throw BusinessRuntimeException.getInstance("该车队规格不属于该账号平台");
        }
        if (StrUtil.isNotBlank(account.getAccount())) {
            //账号是否已经添加过
            if (!account.getAccount().contains("客服")) {
                if (accountCommonService.checkIsDupAccount(account.getGoodsId(), account.getAccount())) {
                    throw BusinessRuntimeException.getInstance("该平台下已生成该账号");
                }
            }
        }
        accountService.save(account);
        groupsTrips.setAccountId(account.getId());
        groupsTrips.setVerifyCodes(account.getVerifyCodes());
        //独立车位 车队下架
        if (goodsDonSkuView.getNum() != null && goodsDonSkuView.getNum() == 1) {
            Number number = beanSearcher.searchCount(GroupsRelation.class, MapUtils.builder()
                    .field(GroupsRelation::getGroupsId, groupsTrips.getId())
                    .field(GroupsRelation::getUserId, 0).op(Operator.NotEqual)
                    .field(GroupsRelation::getStatus, GroupsRelation.Status.validity.name())
                    .build());
            if (number.intValue() > 0) {
                groupsTrips.setStatus(GroupsTrips.Status.down);
            }
        }
        groupService.setAccount(groupsTrips);
        return GatewayResponse.SUCCESS.newBuilder().toResult("车队配置新账号成功");
    }


    /**
     * 车队替换账号
     */
    @PutMapping("/account/replace")
    public Result<String> replaceTripsAccount(@RequestBody ReplaceTripsAccountReq req) {
        long adminId = StpUtil.getLoginIdAsLong();
        req.setAdminId(adminId);
        groupService.replaceTripsAccount(req);
        return GatewayResponse.SUCCESS.newBuilder().toResult("车队替换账号成功");
    }

    /**
     * 车队替换账号记录
     */
    @GetMapping("/get/account/replace/record")
    public Result<SearchResult<GroupsTripsAccountReplaceRecord>> getAccountReplaceRecord() {
        SearchResult<GroupsTripsAccountReplaceRecord> search = beanSearcher.search(GroupsTripsAccountReplaceRecord.class, MapUtils.flatBuilder(request.getParameterMap()).build());
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }

    /**
     * 已下架的车队列表展示
     */
    @GetMapping("/get/down/trips")
    public Result<SearchResult<CloseGroupsTripsView>> getDownGroups() {
        SearchResult<CloseGroupsTripsView> search = beanSearcher.search(CloseGroupsTripsView.class, MapUtils.flatBuilder(request.getParameterMap())
                .field(CloseGroupsTripsView::getGroupsStatus, GroupsTrips.Status.down.name())
                .orderBy(CloseGroupsTripsView::getIsAbandon).asc()
                .build());
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }

    /**
     * 重新上架过期车队
     */
    @PutMapping("/up/trips/{groupsId}")
    @Log(module = "过期账号/上架过期车队", businessType = BusinessType.PUT, isSaveRequestData = true)
    public Result<String> upGroupsTrips(@PathVariable Long groupsId) {
        groupService.update(null, Wrappers.lambdaUpdate(GroupsTrips.class)
                .set(GroupsTrips::getStatus, GroupsTrips.Status.validity)
                .eq(GroupsTrips::getStatus, GroupsTrips.Status.down)
                .eq(GroupsTrips::getId, groupsId));
        return GatewayResponse.SUCCESS.newBuilder().toResult("上架成功");
    }

    /**
     * 下架车队
     */
    @PutMapping("/down/trips/{groupsId}")
    public Result<String> setGroupsDown(@PathVariable Long groupsId) {
        groupService.update(null, Wrappers.lambdaUpdate(GroupsTrips.class)
                .set(GroupsTrips::getStatus, GroupsTrips.Status.down)
                .eq(GroupsTrips::getId, groupsId));
        return GatewayResponse.SUCCESS.newBuilder().toResult("下架成功");
    }

    /**
     * 上下架车队
     */
    @PutMapping("/put/trips/status/{groupsId}")
    @Log(module = "过期账号/上下架过期车队", businessType = BusinessType.PUT, isSaveRequestData = true)
    public Result<String> updateTripsStatus(@PathVariable Long groupsId) {
        GroupsTrips groupsTrips = groupService.getById(groupsId);
        GroupsTrips.Status status = groupsTrips.getStatus();
        if (status == GroupsTrips.Status.waiting) {
            throw BusinessRuntimeException.getInstance("待发车车队无需上下架");
        }
        if (status == GroupsTrips.Status.validity) {
            groupsTrips.setStatus(GroupsTrips.Status.down);
            //奈飞车队下架 是否存在在检测的用户
            Long skuId = groupsTrips.getSkuId();
            if (skuId != null) {
                GoodsDonSku sku = goodsDonSkuMapper.selectById(skuId);
                if (sku != null && sku.getGoodsId() == Constant.NETFLIX_GID) {
                    List<Long> relationIds = groupsRelationMapper.selectList(Wrappers.lambdaUpdate(GroupsRelation.class).eq(GroupsRelation::getGroupsId, groupsTrips.getId())).stream().map(GroupsRelation::getId).collect(Collectors.toList());
                    netflixUserAccountStatusRecordMapper.update(null, Wrappers.lambdaUpdate(NetflixUserAccountStatusRecord.class)
                            .set(NetflixUserAccountStatusRecord::getState, 3)
                            .in(NetflixUserAccountStatusRecord::getRelationId, relationIds));
                }
            }
        }
        if (status == GroupsTrips.Status.down) {
            groupsTrips.setStatus(GroupsTrips.Status.validity);
        }
        groupService.updateById(groupsTrips);
        return GatewayResponse.SUCCESS.newBuilder().toResult("操作成功");
    }

    /**
     * 弃用账号
     */
    @PutMapping("/down/account/{accountId}")
    @Log(module = "过期账号/废弃过期车队", businessType = BusinessType.PUT, isSaveRequestData = true)
    public Result<String> putDownAccount(@PathVariable Long accountId) {
        accountService.update(null, Wrappers.lambdaUpdate(Account.class)
                .set(Account::getIsAbandon, true)
                .eq(Account::getIsAbandon, false)
                .eq(Account::getId, accountId));
        return GatewayResponse.SUCCESS.newBuilder().toResult("弃用成功");
    }

    /**
     * 获取双重验证码
     */
    @GetMapping("/get/verifyCode")
    public Result<String> getVerifyCode(String apiSecret) {
        if (StrUtil.isBlank(apiSecret)) throw BusinessRuntimeException.getInstance("双重密钥为空");
        DateTime now = DateTime.now();
        int second = now.second();
        if (second < 30 && 30 - second < 5) {
            ThreadUtil.sleep(30 - second + 1, TimeUnit.SECONDS);
        } else if (second > 30 && 60 - second < 5) {
            ThreadUtil.sleep(60 - second + 1, TimeUnit.SECONDS);
        }
        StringBuilder sb = new StringBuilder();
        try {
            long code = GoogleGenerator.getCode(apiSecret, now.getTime());
            sb.append(code);
            while (sb.length() < 6) {
                sb.insert(0, 0);
            }
        } catch (Exception e) {
            throw BusinessRuntimeException.getInstance("双重验证密钥错误,请生成新的验证密钥");
        }
        JSONObject re = new JSONObject();
        re.putOpt("code", sb.toString());
        int nowSecond = DateTime.now().second();
        int time = 0;
        if (nowSecond < 30) {
            time = 30 - nowSecond;
        } else {
            time = 60 - nowSecond;
        }
        re.putOpt("time", time);
        return GatewayResponse.SUCCESS.newBuilder().toResult(re.toString());
    }

    /**
     * 获取该规格下可用车位数
     */
    @GetMapping("/get/available/relation")
    public Result<Integer> getAvailableRelation(Long goodsId, Long skuId) {
        Integer available = groupService.getAvailableRelation(goodsId, skuId);
        return GatewayResponse.SUCCESS.newBuilder().toResult(available);
    }

    /**
     * 设置库存预警通知
     */
    @PutMapping("/set/relation/alert")
    public Result<String> setGroupsRelationAlert(@RequestBody @Validated GroupsRelationAlert groupsRelationAlert) throws Exception {
        if (groupsRelationAlert.getId() == null) {
            try {
                alertMapper.insert(groupsRelationAlert);
            } catch (DuplicateKeyException e) {
            }
            return GatewayResponse.SUCCESS.newBuilder().toResult();
        }
        Long id = groupsRelationAlert.getId();
        Assert.notNull(id, "预警id不存在");
        GroupsRelationAlert dbAlert = alertMapper.selectById(id);
        Assert.notNull(dbAlert, "预警不存在");
        String phoneStr = groupsRelationAlert.getPhoneStr();
        List<String> phoneList = Jsons.parseList(phoneStr, String.class);
        phoneList.forEach(phone -> {
            if (!Validator.isMobile(phone)) {
                throw BusinessRuntimeException.getInstance("请输入正确的手机格式");
            }
        });
        Integer dbNum = dbAlert.getNum();
        Integer num = groupsRelationAlert.getNum();
        if (dbNum != num) {
            groupsRelationAlert.setIsReset(true);
        }
        alertMapper.updateById(groupsRelationAlert);
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

    /**
     * 库存预警通知
     */
    @GetMapping("/get/relation/alert")
    public Result<GroupsRelationAlert> getGroupsRelationAlert(@RequestParam(required = true) Long skuId) {
        GroupsRelationAlert groupsRelationAlert = alertMapper.selectOne(Wrappers.lambdaQuery(GroupsRelationAlert.class)
                .eq(GroupsRelationAlert::getSkuId, skuId).last("limit 1"));
        return GatewayResponse.SUCCESS.newBuilder().toResult(groupsRelationAlert);
    }
}
