package com.lesingle.modules.leai.service;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.lesingle.common.exception.BusinessException;
import com.lesingle.modules.leai.config.LeaiConfig;
import com.lesingle.modules.leai.enums.LeaiCreationStatus;
import com.lesingle.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.stereotype.Component;

import org.springframework.web.multipart.MultipartFile;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
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 客户端
 * 使用 Hutool HttpRequest + Jackson 对接乐读派后端
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LeaiApiClient {

    private static final int TIMEOUT_MS = 10_000;

    private final LeaiConfig leaiConfig;
    private final ObjectMapper objectMapper;

    // ── 状态常量（引用 LeaiCreationStatus，保持向后兼容） ──
    public static final int STATUS_FAILED     = LeaiCreationStatus.FAILED;
    public static final int STATUS_DRAFT      = LeaiCreationStatus.DRAFT;
    public static final int STATUS_PENDING    = LeaiCreationStatus.PENDING;
    public static final int STATUS_PROCESSING = LeaiCreationStatus.PROCESSING;
    public static final int STATUS_COMPLETED  = LeaiCreationStatus.COMPLETED;
    public static final int STATUS_CATALOGED  = LeaiCreationStatus.CATALOGED;
    public static final int STATUS_DUBBED     = LeaiCreationStatus.DUBBED;

    /**
     * 换取 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()); // orgId 对应本项目租户 code（tenant_code）
        body.put("appSecret", leaiConfig.getAppSecret());
        body.put("phone", phone);

        try {
            String jsonBody = objectMapper.writeValueAsString(body);

            HttpResponse httpResponse = HttpRequest.post(url)
                    .body(jsonBody)
                    .contentType("application/json")
                    .timeout(TIMEOUT_MS)
                    .execute();

            String responseBody = httpResponse.body();

            Map<String, Object> result = objectMapper.readValue(responseBody,
                    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) {
        // orgId 对应本项目租户 code（tenant_code）
        Map<String, Object> queryParams = new TreeMap<>();
        queryParams.put("orgId", leaiConfig.getOrgId());

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

        try {
            String url = leaiConfig.getApiUrl() + "/api/v1/query/work/" + workId;

            HttpResponse httpResponse = HttpRequest.get(url)
                    .form(queryParams)
                    .addHeaders(hmacHeaders)
                    .timeout(TIMEOUT_MS)
                    .execute();

            String responseBody = httpResponse.body();

            Map<String, Object> result = objectMapper.readValue(responseBody,
                    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) {
        // orgId 对应本项目租户 code（tenant_code）
        Map<String, Object> 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 {
            String url = leaiConfig.getApiUrl() + "/api/v1/query/works";

            log.info("[乐读派] B3查询请求: url={}, params={}, headers={}", url, queryParams, hmacHeaders);

            HttpResponse httpResponse = HttpRequest.get(url)
                    .form(queryParams)
                    .addHeaders(hmacHeaders)
                    .timeout(TIMEOUT_MS)
                    .execute();

            String responseBody = httpResponse.body();
            int status = httpResponse.getStatus();

            log.info("[乐读派] B3查询响应: status={}, body={}", status, responseBody);

            Map<String, Object> result = objectMapper.readValue(responseBody,
                    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, ?> queryParams) {
        String ts = String.valueOf(System.currentTimeMillis());
        String nonce = Long.toHexString(System.currentTimeMillis()) + Long.toHexString(System.nanoTime());

        TreeMap<String, String> allParams = new TreeMap<>();
        queryParams.forEach((k, v) -> allParams.put(k, String.valueOf(v)));
        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());
        }

        log.debug("[乐读派签名] orgId={}, appSecret={}***", leaiConfig.getOrgId(),
                leaiConfig.getAppSecret() != null ? leaiConfig.getAppSecret().substring(0, Math.min(6, leaiConfig.getAppSecret().length())) : "null");
        log.debug("[乐读派签名] 待签名字符串: {}", signStr);

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

        log.debug("[乐读派签名] 签名结果: {}", sig);
        log.debug("[乐读派签名] 请求头: X-App-Key={}, X-Timestamp={}, X-Nonce={}", leaiConfig.getOrgId(), ts, nonce);

        Map<String, String> headers = new LinkedHashMap<>();
        headers.put("X-App-Key", leaiConfig.getOrgId()); // X-App-Key 即租户 code（tenant_code）
        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);
    }

    // ═══════════════════════════════════════════════════════════
    // 通用代理方法（后端代理前端请求，HMAC 签名）
    // ═══════════════════════════════════════════════════════════

    private static final int PROXY_TIMEOUT_MS = 120_000;

    /**
     * 通用 GET 代理（HMAC 签名）
     * @param path 乐读派 API 路径，如 /query/work/123
     * @param params 查询参数
     * @return 乐读派原始 JSON 响应
     */
    public String proxyGet(String path, Map<String, Object> params) {
        Map<String, Object> queryParams = new TreeMap<>(params);
        Map<String, String> hmacHeaders = buildHmacHeaders(queryParams);

        String url = leaiConfig.getApiUrl() + "/api/v1" + path;
        log.debug("[乐读派代理] GET {}", url);

        try {
            HttpResponse httpResponse = HttpRequest.get(url)
                    .form(queryParams)
                    .addHeaders(hmacHeaders)
                    .timeout(PROXY_TIMEOUT_MS)
                    .execute();

            String responseBody = httpResponse.body();
            log.debug("[乐读派代理] GET {} 响应: {}", url, responseBody);
            return responseBody;

        } catch (Exception e) {
            log.error("[乐读派代理] GET {} 失败", url, e);
            throw new BusinessException(502, "乐读派代理请求失败: " + e.getMessage());
        }
    }

    /**
     * 通用 POST 代理（HMAC 签名 + JSON body）
     * @param path 乐读派 API 路径，如 /creation/extract-original
     * @param body 请求体（会自动注入 orgId）
     * @return 乐读派原始 JSON 响应
     */
    public String proxyPost(String path, Map<String, Object> body) {
        // POST body 不参与签名，签名仅基于 query params
        Map<String, Object> signParams = new TreeMap<>();
        Map<String, String> hmacHeaders = buildHmacHeaders(signParams);

        String url = leaiConfig.getApiUrl() + "/api/v1" + path;
        log.debug("[乐读派代理] POST {} body={}", url, body);

        try {
            String jsonBody = objectMapper.writeValueAsString(body);

            HttpResponse httpResponse = HttpRequest.post(url)
                    .body(jsonBody)
                    .contentType("application/json")
                    .addHeaders(hmacHeaders)
                    .timeout(PROXY_TIMEOUT_MS)
                    .execute();

            String responseBody = httpResponse.body();
            log.debug("[乐读派代理] POST {} 响应: {}", url, responseBody);
            return responseBody;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("[乐读派代理] POST {} 失败", url, e);
            throw new BusinessException(502, "乐读派代理请求失败: " + e.getMessage());
        }
    }

    /**
     * 通用 PUT 代理（HMAC 签名 + JSON body）
     * @param path 乐读派 API 路径，如 /update/work/123
     * @param body 请求体
     * @return 乐读派原始 JSON 响应
     */
    public String proxyPut(String path, Map<String, Object> body) {
        Map<String, Object> signParams = new TreeMap<>();
        Map<String, String> hmacHeaders = buildHmacHeaders(signParams);

        String url = leaiConfig.getApiUrl() + "/api/v1" + path;
        log.debug("[乐读派代理] PUT {} body={}", url, body);

        try {
            String jsonBody = objectMapper.writeValueAsString(body);

            HttpResponse httpResponse = HttpRequest.put(url)
                    .body(jsonBody)
                    .contentType("application/json")
                    .addHeaders(hmacHeaders)
                    .timeout(PROXY_TIMEOUT_MS)
                    .execute();

            String responseBody = httpResponse.body();
            log.debug("[乐读派代理] PUT {} 响应: {}", url, responseBody);
            return responseBody;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("[乐读派代理] PUT {} 失败", url, e);
            throw new BusinessException(502, "乐读派代理请求失败: " + e.getMessage());
        }
    }

    /**
     * 文件上传代理（multipart → 乐读派）
     * @param path 乐读派 API 路径，如 /creation/upload
     * @param file 上传的文件
     * @return 乐读派原始 JSON 响应
     */
    public String proxyUpload(String path, MultipartFile file) {
        Map<String, Object> signParams = new TreeMap<>();
        Map<String, String> hmacHeaders = buildHmacHeaders(signParams);

        String url = leaiConfig.getApiUrl() + "/api/v1" + path;
        log.debug("[乐读派代理] UPLOAD {} fileName={}", url, file.getOriginalFilename());

        try {
            byte[] fileBytes = file.getBytes();
            String originalFilename = file.getOriginalFilename() != null ? file.getOriginalFilename() : "upload";

            HttpResponse httpResponse = HttpRequest.post(url)
                    .addHeaders(hmacHeaders)
                    .form("file", fileBytes, originalFilename)
                    .contentType("multipart/form-data")
                    .timeout(PROXY_TIMEOUT_MS)
                    .execute();

            String responseBody = httpResponse.body();
            log.debug("[乐读派代理] UPLOAD {} 响应: {}", url, responseBody);
            return responseBody;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("[乐读派代理] UPLOAD {} 失败", url, e);
            throw new BusinessException(502, "乐读派代理上传失败: " + e.getMessage());
        }
    }
}
