package com.rj.service;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.rj.config.HxrAdminProperties;
import com.rj.dto.hxr.HxrGoodsApiContext;
import com.rj.dto.hxr.HxrLbGoodsSelectResponse;
import com.rj.util.HxrGoodsSignUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 调用 hxrd {@code /api/order/goods} 货品列表（请求头 {@code token} + {@code S/T/N} 鉴权）。
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class HxrAdminGoodsService {

    public static final int GOODS_PAGE_SIZE = 20;

    private static final SecureRandom RANDOM = new SecureRandom();

    private static final String USER_AGENT =
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                    + "(KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0";

    private static final ObjectMapper JSON = new ObjectMapper()
            .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    private final HxrAdminProperties properties;

    /**
     * 分页拉取货品列表（使用 {@code hxr.admin} 配置文件，兼容旧调用）。
     *
     * @param page  页码，从 1 开始
     * @param token 可选；非空时作为请求头 token，否则使用 {@code hxr.admin.goods-api-token}
     */
    public Optional<HxrLbGoodsSelectResponse> fetchGoodsPage(int page, String token) throws Exception {
        String resolvedToken = resolveToken(token);
        if (resolvedToken == null) {
            log.warn("未提供 token 且 hxr.admin 未配置 goods-api-token，跳过 /api/order/goods");
            return Optional.empty();
        }
        String appStr = properties.getGoodsApiAppStr();
        if (appStr == null || appStr.isBlank()) {
            log.warn("hxr.admin 未配置 goods-api-app-str，跳过 /api/order/goods");
            return Optional.empty();
        }
        HxrGoodsApiContext ctx = new HxrGoodsApiContext(
                stripQuery(properties.getGoodsApiUrl()),
                GOODS_PAGE_SIZE,
                properties.getGoodsApiOrigin(),
                properties.getGoodsApiReferer(),
                resolvedToken,
                appStr.trim());
        return fetchGoodsPage(page, ctx);
    }

    /**
     * 分页拉取货品列表（使用租户 {@code lb_third_integration_config} 解析出的运行时配置）。
     */
    public Optional<HxrLbGoodsSelectResponse> fetchGoodsPage(int page, HxrGoodsApiContext ctx) throws Exception {
        if (ctx == null) {
            log.warn("货品 API 配置为空，跳过 /api/order/goods");
            return Optional.empty();
        }
        String resolvedToken = ctx.token();
        if (resolvedToken == null || resolvedToken.isBlank()) {
            log.warn("未提供 token，跳过 /api/order/goods");
            return Optional.empty();
        }
        String appStr = ctx.appStr();
        if (appStr == null || appStr.isBlank()) {
            log.warn("未配置 goodsApiAppStr，跳过 /api/order/goods");
            return Optional.empty();
        }
        String goodsApiBaseUrl = ctx.goodsApiBaseUrl();
        if (goodsApiBaseUrl == null || goodsApiBaseUrl.isBlank()) {
            log.warn("未配置 goodsApiBaseUrl，跳过 /api/order/goods");
            return Optional.empty();
        }

        int pageLimit = ctx.pageLimit() > 0 ? ctx.pageLimit() : GOODS_PAGE_SIZE;
        int safePage = Math.max(1, page);
        String uri = UriComponentsBuilder.fromUriString(goodsApiBaseUrl)
                .replaceQueryParam("page", safePage)
                .replaceQueryParam("limit", pageLimit)
                .build()
                .encode()
                .toUriString();

        long timestamp = System.currentTimeMillis() / 1000;
        String noncestr = randomNoncestr();
        Map<String, Object> signParams = new LinkedHashMap<>();
        signParams.put("page", safePage);
        signParams.put("limit", pageLimit);
        signParams.put("timestamp", timestamp);
        signParams.put("noncestr", noncestr);
        String sign = HxrGoodsSignUtil.computeSign(signParams, appStr.trim());

        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(60))
                .followRedirects(HttpClient.Redirect.NORMAL)
                .build();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(uri))
                .timeout(Duration.ofSeconds(120))
                .header("Accept", "application/json,*/*")
                .header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
                .header("Origin", ctx.origin())
                .header("Referer", ctx.referer())
                .header("User-Agent", USER_AGENT)
                .header("token", resolvedToken)
                .header("S", sign)
                .header("T", String.valueOf(timestamp))
                .header("N", noncestr)
                .GET()
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        int status = response.statusCode();
        if (status < 200 || status >= 300) {
            log.warn("hxr /api/order/goods HTTP {} page={} bodyPrefix={}",
                    status, safePage, abbreviate(response.body(), 400));
            return Optional.empty();
        }

        String bodyText = response.body();
        if (bodyText == null || bodyText.isBlank()) {
            log.warn("hxr /api/order/goods empty body page={}", safePage);
            return Optional.empty();
        }

        JsonNode root = JSON.readTree(bodyText);
        int code = root.path("code").asInt(-1);
        String msg = root.path("msg").asText("");
        if (code != 0) {
            log.warn("hxr /api/order/goods api code={} msg={} page={} bodyPrefix={}",
                    code, msg, safePage, abbreviate(bodyText, 400));
            return Optional.empty();
        }

        JsonNode dataNode = root.get("data");
        if (isErrorPayload(dataNode)) {
            log.warn("hxr /api/order/goods 返回异常 data={} page={} msg={}", dataNode, safePage, msg);
            return Optional.empty();
        }

        HxrLbGoodsSelectResponse body = JSON.treeToValue(root, HxrLbGoodsSelectResponse.class);
        return Optional.of(body);
    }

    /** 从完整 URL 中去掉 query，供配置文件回退路径使用。 */
    private static String stripQuery(String url) {
        if (url == null || url.isBlank()) {
            return url;
        }
        int q = url.indexOf('?');
        return q >= 0 ? url.substring(0, q) : url;
    }

    /** 优先使用入参 token，否则回退到配置 {@code goods-api-token}。 */
    private String resolveToken(String token) {
        if (token != null && !token.isBlank()) {
            return token.trim();
        }
        String configured = properties.getGoodsApiToken();
        if (configured != null && !configured.isBlank()) {
            return configured.trim();
        }
        return null;
    }

    /** 与前端 {@code Math.random().toString(36).slice(-5)} 接近的 5 位随机串。 */
    private static String randomNoncestr() {
        String base36 = Long.toUnsignedString(Math.abs(RANDOM.nextLong()), 36);
        if (base36.length() >= 5) {
            return base36.substring(base36.length() - 5);
        }
        StringBuilder sb = new StringBuilder(base36);
        while (sb.length() < 5) {
            sb.append(Integer.toString(RANDOM.nextInt(36), 36));
        }
        return sb.toString();
    }

    private static boolean isErrorPayload(JsonNode dataNode) {
        if (dataNode == null || dataNode.isNull()) {
            return false;
        }
        if (!dataNode.isArray() || dataNode.isEmpty()) {
            return false;
        }
        return dataNode.get(0).isTextual() || dataNode.get(0).isNumber();
    }

    private static String abbreviate(String s, int maxLen) {
        if (s == null) {
            return "";
        }
        return s.length() <= maxLen ? s : s.substring(0, maxLen) + "...";
    }
}
