package com.haha.sdk;

import com.haha.sdk.api.*;
import com.haha.sdk.config.HahaConfig;
import com.haha.sdk.exception.HahaException;
import com.haha.sdk.util.HttpUtils;
import com.haha.sdk.util.SignUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 哈哈零售SDK客户端
 * 提供对哈哈零售平台API的统一访问入口
 * 
 * @author haha-sdk
 * @version 1.0.0
 */
@Slf4j
@Getter
public class HahaClient {
    
    /**
     * SDK配置信息
     */
    private final HahaConfig config;
    
    /**
     * HTTP工具类
     */
    private final HttpUtils httpUtils;
    
    /**
     * 访问令牌
     */
    private String accessToken;

    /**
     * 回调验签票据（从 /token/get 接口返回，用于验证哈哈平台的回调通知签名）
     */
    private String ticket;

    /**
     * 令牌过期时间（时间戳，毫秒）
     */
    private long tokenExpireTime;
    
    /**
     * 设备相关API
     */
    private final DeviceApi deviceApi;
    
    /**
     * 商品相关API
     */
    private final GoodsApi goodsApi;
    
    /**
     * 订单相关API
     */
    private final OrderApi orderApi;
    
    /**
     * 构造方法
     * 
     * @param config SDK配置信息
     */
    public HahaClient(HahaConfig config) {
        this.config = config;
        this.httpUtils = new HttpUtils();
        
        // 初始化各个API模块
        this.deviceApi = new DeviceApi(this);
        this.goodsApi = new GoodsApi(this);
        this.orderApi = new OrderApi(this);
        
        log.info("哈哈零售SDK初始化成功, AppId: {}", config.getAppId());
    }
    
    /**
     * 获取访问令牌
     * 如果令牌不存在或即将过期（2天内），则自动刷新
     * 
     * @return 访问令牌
     * @throws HahaException 获取令牌失败时抛出
     */
    public synchronized String getAccessToken() throws HahaException {
        // 检查令牌是否存在或已过期
        long currentTime = System.currentTimeMillis();
        long twoDay = TimeUnit.DAYS.toMillis(2);
        
        if (accessToken == null || tokenExpireTime - currentTime <= twoDay) {
            refreshAccessToken();
        }
        
        return accessToken;
    }
    
    /**
     * 刷新访问令牌
     * 
     * @throws HahaException 刷新令牌失败时抛出
     */
    private void refreshAccessToken() throws HahaException {
        log.info("开始刷新access_token");
        
        try {
            // 构建请求参数
            Map<String, String> params = new HashMap<>();
            params.put("app_id", config.getAppId());
            params.put("app_secret", config.getAppSecret());
            // 生成6位随机字符串（小写字母和数字）
            params.put("rand_str", generateRandStr());
            
            // 发送请求（token获取接口不需要签名）
            String url = config.getApiBaseUrl() + "/token/get";
            String response = httpUtils.post(url, params);
            
            // 解析响应
            Map<String, Object> result = httpUtils.parseResponse(response);
            
            if (result == null || !Integer.valueOf(1).equals(result.get("code"))) {
                throw new HahaException("获取access_token失败: " + result);
            }
            
            @SuppressWarnings("unchecked")
            Map<String, Object> data = (Map<String, Object>) result.get("data");
            this.accessToken = (String) data.get("access_token");
            this.ticket = (String) data.get("ticket");

            // 计算过期时间，使用文档中的exprire字段（注意拼写）
            Integer expire = (Integer) data.get("exprire");
            if (expire != null) {
                this.tokenExpireTime = TimeUnit.SECONDS.toMillis(expire);
            } else {
                // 如果没有返回过期时间，默认15天
                this.tokenExpireTime = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(15);
            }
            
            log.info("access_token刷新成功，有效期至: {}", new java.util.Date(tokenExpireTime));
            
        } catch (Exception e) {
            log.error("刷新access_token失败", e);
            throw new HahaException("刷新access_token失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 生成6位随机字符串（小写字母和数字）
     * 
     * @return 随机字符串
     */
    private String generateRandStr() {
        String chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder sb = new StringBuilder(6);
        for (int i = 0; i < 6; i++) {
            int index = (int) (Math.random() * chars.length());
            sb.append(chars.charAt(index));
        }
        return sb.toString();
    }
    
    /**
     * 手动设置访问令牌
     * 如果商户自行管理令牌，可通过此方法设置
     * 
     * @param accessToken 访问令牌
     * @param expiresIn 过期时间（秒）
     */
    public void setAccessToken(String accessToken, int expiresIn) {
        this.accessToken = accessToken;
        this.tokenExpireTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiresIn);
        log.info("手动设置access_token成功，有效期至: {}", new java.util.Date(tokenExpireTime));
    }
    
    /**
     * 关闭客户端，释放资源
     */
    public void shutdown() {
        if (httpUtils != null) {
            httpUtils.close();
        }
        log.info("哈哈零售SDK已关闭");
    }
}
