package com.competition.modules.leai.service;

import com.competition.common.exception.BusinessException;
import com.competition.modules.leai.config.LeaiConfig;
import com.competition.modules.leai.util.LeaiUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 乐读派 API 客户端
 * 使用 RestTemplate + Jackson 对接乐读派后端
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LeaiApiClient {

    private final RestTemplate restTemplate;
    private final LeaiConfig leaiConfig;
    private final ObjectMapper objectMapper;

    // ── 状态常量（V4.0 数值状态机） ──
    public static final int STATUS_FAILED     = -1;
    public static final int STATUS_DRAFT      =  0;
    public static final int STATUS_PENDING    =  1;
    public static final int STATUS_PROCESSING =  2;
    public static final int STATUS_COMPLETED  =  3;
    public static final int STATUS_CATALOGED  =  4;
    public static final int STATUS_DUBBED     =  5;

    /**
     * 换取 Session Token
     * POST /api/v1/auth/session
     */
    public String exchangeToken(String phone) {
        String url = leaiConfig.getApiUrl() + "/api/v1/auth/session";

        Map<String, String> body = new HashMap<>();
        body.put("orgId", leaiConfig.getOrgId());
        body.put("appSecret", leaiConfig.getAppSecret());
        body.put("phone", phone);

        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<String> entity = new HttpEntity<>(objectMapper.writeValueAsString(body), headers);
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);

            Map<String, Object> result = objectMapper.readValue(response.getBody(),
                    new TypeReference<Map<String, Object>>() {});

            int code = LeaiUtil.toInt(result.get("code"), 0);
            if (code != 200) {
                throw new BusinessException(502, "令牌交换失败: code=" + code
                        + ", msg=" + LeaiUtil.toString(result.get("msg"), "unknown"));
            }

            @SuppressWarnings("unchecked")
            Map<String, Object> data = (Map<String, Object>) result.get("data");
            if (data == null) {
                throw new BusinessException(502, "令牌交换失败: data 为 null");
            }

            String sessionToken = LeaiUtil.toString(data.get("sessionToken"), null);
            if (sessionToken == null || sessionToken.isEmpty()) {
                throw new BusinessException(502, "令牌交换失败: sessionToken 为空");
            }

            log.info("[乐读派] 令牌交换成功, phone={}, expiresIn={}s", phone, data.get("expiresIn"));
            return sessionToken;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(502, "令牌交换请求失败: " + e.getMessage());
        }
    }

    /**
     * B2 查询作品详情
     * GET /api/v1/query/work/{workId}
     */
    public Map<String, Object> fetchWorkDetail(String workId) {
        Map<String, String> queryParams = new TreeMap<>();
        queryParams.put("orgId", leaiConfig.getOrgId());

        Map<String, String> hmacHeaders = buildHmacHeaders(queryParams);

        try {
            String url = leaiConfig.getApiUrl() + "/api/v1/query/work/"
                    + URLEncoder.encode(workId, StandardCharsets.UTF_8)
                    + "?orgId=" + URLEncoder.encode(leaiConfig.getOrgId(), StandardCharsets.UTF_8);

            HttpHeaders headers = new HttpHeaders();
            hmacHeaders.forEach(headers::set);
            headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));

            HttpEntity<Void> entity = new HttpEntity<>(headers);
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);

            Map<String, Object> result = objectMapper.readValue(response.getBody(),
                    new TypeReference<Map<String, Object>>() {});

            int code = LeaiUtil.toInt(result.get("code"), 0);
            if (code != 200) {
                log.warn("[乐读派] B2查询失败: workId={}, code={}", workId, code);
                return null;
            }

            @SuppressWarnings("unchecked")
            Map<String, Object> data = (Map<String, Object>) result.get("data");
            return data;

        } catch (Exception e) {
            log.error("[乐读派] B2查询异常: workId={}", workId, e);
            return null;
        }
    }

    /**
     * B3 批量查询作品
     * GET /api/v1/query/works
     */
    public List<Map<String, Object>> queryWorks(String updatedAfter) {
        Map<String, String> queryParams = new TreeMap<>();
        queryParams.put("orgId", leaiConfig.getOrgId());
        queryParams.put("updatedAfter", updatedAfter);
        queryParams.put("page", "1");
        queryParams.put("size", "100");

        Map<String, String> hmacHeaders = buildHmacHeaders(queryParams);

        try {
            StringBuilder queryString = new StringBuilder();
            for (Map.Entry<String, String> e : queryParams.entrySet()) {
                if (queryString.length() > 0) queryString.append("&");
                queryString.append(URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8))
                           .append("=")
                           .append(URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8));
            }

            String url = leaiConfig.getApiUrl() + "/api/v1/query/works?" + queryString;

            HttpHeaders headers = new HttpHeaders();
            hmacHeaders.forEach(headers::set);
            headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));

            HttpEntity<Void> entity = new HttpEntity<>(headers);
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);

            Map<String, Object> result = objectMapper.readValue(response.getBody(),
                    new TypeReference<Map<String, Object>>() {});

            int code = LeaiUtil.toInt(result.get("code"), 0);
            if (code != 200) {
                log.warn("[乐读派] B3查询失败: code={}, msg={}", code, result.get("msg"));
                return Collections.emptyList();
            }

            @SuppressWarnings("unchecked")
            Map<String, Object> dataMap = (Map<String, Object>) result.get("data");
            if (dataMap == null) return Collections.emptyList();

            Object recordsObj = dataMap.get("records");
            if (recordsObj instanceof List) {
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> records = (List<Map<String, Object>>) recordsObj;
                return records;
            }

            return Collections.emptyList();

        } catch (Exception e) {
            log.error("[乐读派] B3查询异常", e);
            return Collections.emptyList();
        }
    }

    /**
     * 生成 HMAC 签名请求头
     */
    public Map<String, String> buildHmacHeaders(Map<String, String> queryParams) {
        String ts = String.valueOf(System.currentTimeMillis());
        String nonce = Long.toHexString(System.currentTimeMillis()) + Long.toHexString(System.nanoTime());

        TreeMap<String, String> allParams = new TreeMap<>(queryParams);
        allParams.put("timestamp", ts);
        allParams.put("nonce", nonce);

        StringBuilder signStr = new StringBuilder();
        for (Map.Entry<String, String> entry : allParams.entrySet()) {
            if (signStr.length() > 0) signStr.append("&");
            signStr.append(entry.getKey()).append("=").append(entry.getValue());
        }

        String sig = hmacSha256(signStr.toString(), leaiConfig.getAppSecret());

        Map<String, String> headers = new LinkedHashMap<>();
        headers.put("X-App-Key", leaiConfig.getOrgId());
        headers.put("X-Timestamp", ts);
        headers.put("X-Nonce", nonce);
        headers.put("X-Signature", sig);
        return headers;
    }

    /**
     * 验证 Webhook 签名
     */
    public boolean verifyWebhookSignature(String webhookId, String timestamp, String rawBody, String signature) {
        String signData = webhookId + "." + timestamp + "." + rawBody;
        String expected = "HMAC-SHA256=" + hmacSha256(signData, leaiConfig.getAppSecret());
        return MessageDigest.isEqual(
                expected.getBytes(StandardCharsets.UTF_8),
                signature.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * HMAC-SHA256 签名
     */
    private String hmacSha256(String data, String secret) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec keySpec = new SecretKeySpec(
                    secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            mac.init(keySpec);
            byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder(hash.length * 2);
            for (byte b : hash) {
                sb.append(String.format("%02x", b & 0xff));
            }
            return sb.toString();
        } catch (Exception e) {
            throw new BusinessException(500, "HMAC-SHA256 签名失败");
        }
    }

    /**
     * 获取 2 小时前的 UTC 时间 ISO 格式（用于 B3 对账）
     */
    public static String getUtcTwoHoursAgoIso() {
        return ZonedDateTime.ofInstant(Instant.now().minusSeconds(7200), ZoneOffset.UTC)
                .format(DateTimeFormatter.ISO_INSTANT);
    }
}
