package org.jeecg.modules.adweb.dmp.controller;

import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import jakarta.servlet.http.HttpServletRequest;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.FastJsonUtil;
import org.jeecg.modules.adweb.common.util.AdwebRedisUtil;
import org.jeecg.modules.adweb.quota.entity.ResourceQuota;
import org.jeecg.modules.adweb.quota.service.IResourceQuotaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;

import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 外贸大数据 - TradeSparq API转发
 *
 * @author wfansh
 */
@RestController
@RequestMapping(TradeSparqController.BASE_PATH)
@Slf4j
public class TradeSparqController {

    static final String BASE_PATH = "/tradesparq";

    @Value("${tradesparq.api.host}")
    private String apiHost;

    @Value("${tradesparq.api.id}")
    private String apiId;

    @Value("${tradesparq.api.secret}")
    private String apiSecret;

    @Autowired private IResourceQuotaService resourceQuotaService;

    @Autowired private AdwebRedisUtil adwebRedisUtil;

    @Autowired private RestTemplate restTemplate;

    // 用户级加锁，确保TradeSparq API串行调用，准确控制用户额度
    private final Cache<String, Object> userLocks =
            CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.HOURS).build();

    @PostMapping("/**")
    @ResponseBody
    public Result<JSONObject> forwardPost(
            HttpServletRequest httpRequest, @RequestBody JSONObject requestBody)
            throws ExecutionException {
        LoginUser loginUser = ((LoginUser) SecurityUtils.getSubject().getPrincipal());
        if (Objects.isNull(loginUser)) {
            return Result.error("用户信息未找到");
        }

        // 获取用户锁
        Object userLock = userLocks.get(loginUser.getId(), Object::new);
        synchronized (userLock) {
            String apiPath = httpRequest.getServletPath().split(BASE_PATH)[1]; // 获取TradeSparq API路径
            String sortedParams =
                    requestBody.keySet().stream()
                            .sorted()
                            .map(key -> FastJsonUtil.toJSONString(requestBody.get(key)))
                            .collect(Collectors.joining());

            // 0. 检查Redis缓存
            String redisKey =
                    String.format(
                            "tradesparq:%s:%s",
                            apiPath, DigestUtils.md5DigestAsHex(sortedParams.getBytes()));
            JSONObject cachedResult = (JSONObject) adwebRedisUtil.get(redisKey);
            if (Objects.nonNull(cachedResult)) {
                log.info(
                        "TradeSparq返回Redis缓存数据，uid = {}, name = {}, path = {}, request body = {}",
                        loginUser.getId(),
                        loginUser.getUsername(),
                        apiPath,
                        requestBody);
                return Result.ok(cachedResult);
            }

            // 1. 校验用户资源额度
            boolean isQuotaLimited = this.isQuotaLimited(httpRequest, requestBody);
            if (isQuotaLimited) {
                Pair<Integer, Integer> customsDataQuota =
                        resourceQuotaService.getCustomsDataQuota(loginUser.getId());
                if (customsDataQuota.getLeft() <= customsDataQuota.getRight()) {
                    log.warn("用户 {} {} 海关查询资源额度不足", loginUser.getId(), loginUser.getUsername());
                    return Result.error(
                            String.format(
                                    "查询失败。用户资源额度为%d，已使用%d",
                                    customsDataQuota.getLeft(), customsDataQuota.getRight()),
                            null);
                }
            }

            // 2. 生成签名
            String sign = DigestUtils.md5DigestAsHex((apiSecret + sortedParams).getBytes());

            // 3. 构建并发送POST请求
            RequestEntity<String> requestEntity =
                    RequestEntity.post(apiHost + apiPath)
                            .headers(this.getHttpHeaders(sign))
                            .body(requestBody.toJSONString());
            try {
                ResponseEntity<JSONObject> responseEntity =
                        restTemplate.exchange(requestEntity, JSONObject.class);
                log.info(
                        "TradeSparq API调用成功，uid = {}, name = {}, path = {}, request body = {}",
                        loginUser.getId(),
                        loginUser.getUsername(),
                        apiPath,
                        requestBody);

                // 3.1 记录资源额度消耗
                if (isQuotaLimited) {
                    resourceQuotaService.consumeCustomsDataQuota(loginUser.getId());
                }

                // 3.2 缓存到Redis，TTL为3小时
                adwebRedisUtil.set(redisKey, responseEntity.getBody(), 60 * 60 * 6);

                return Result.ok(responseEntity.getBody());
            } catch (RestClientResponseException e) {
                log.error(
                        "TradeSparq API调用失败，uid = {}, name = {}, path = {}, request body = {}",
                        loginUser.getId(),
                        loginUser.getUsername(),
                        apiPath,
                        requestBody,
                        e);
                return Result.error(e.getStatusCode().value(), e.getMessage());
            }
        }
    }

    private HttpHeaders getHttpHeaders(String sign) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        httpHeaders.set("X-API-UID", apiId);
        httpHeaders.set("X-API-REQUEST-SIGN", sign);

        return httpHeaders;
    }

    /** 判断TradeSparq API请求是否受资源额度限制 - {@link ResourceQuota} */
    private boolean isQuotaLimited(HttpServletRequest httpRequest, JSONObject requestBody) {
        // TODO: 假定当前只限制详单查询(/cds_v2/old_api/record)第一页请求
        //        return StringUtils.endsWithIgnoreCase(
        //                        httpRequest.getServletPath(), "/cds_v2/old_api/record")
        //                && Objects.equals(requestBody.getInteger("page"), 1);

        return true; // 所有API请求都受限
    }
}
