package org.lingniu.idp.model.vo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.lingniu.idp.enums.DeviceType;

import java.time.Instant;
import java.util.*;

/**
 * Access Token 信息
 * 存储在 Redis String 结构中
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccessTokenInfo {
    private String tokenValue;
    
    /**
     * 用户ID
     */
    private String userId;
    
    /**
     * 用户名
     */
    private String username;
    
    /**
     * 客户端ID
     */
    private String clientId;
    
    /**
     * 权限范围
     */
    private Set<String> scopes;
    
    /**
     * 颁发时间
     */
    private Instant issuedAt;
    
    /**
     * 过期时间
     */
    private Instant expiresAt;
    
    /**
     * 设备ID（可选，用于设备管理）
     */
    private String deviceId;
    
    /**
     * 设备类型（WEB/IOS/ANDROID）
     */
    private DeviceType deviceType;
    
    /**
     * IP地址
     */
    private String ipAddress;
    
    /**
     * 用户代理（User-Agent）
     */
    private String userAgent;
    
    /**
     * 是否已撤销
     */
    private boolean revoked;
    
    /**
     * 撤销时间
     */
    private Instant revokedAt;
    
    /**
     * 关联的刷新Token ID
     */
    private String refreshTokenId;
    
    /**
     * JWT ID（如果是JWT token）
     */
    private String jti;
    
    /**
     * 附加数据（JSON格式）
     */
    private String additionalInfo;
    
    /**
     * 检查Token是否过期
     */
    public boolean isExpired() {
        return expiresAt != null && Instant.now().isAfter(expiresAt);
    }
    
    /**
     * 检查Token是否有效
     */
    public boolean isValid() {
        return !isExpired() && !revoked;
    }
    
    /**
     * 获取剩余有效时间（秒）
     */
    public long getRemainingSeconds() {
        if (expiresAt == null) {
            return 0;
        }
        Instant now = Instant.now();
        if (now.isAfter(expiresAt)) {
            return 0;
        }
        return expiresAt.getEpochSecond() - now.getEpochSecond();
    }
    
    /**
     * 获取Token使用时长（秒）
     */
    public long getUsedSeconds() {
        if (issuedAt == null) {
            return 0;
        }
        Instant end = revoked ? (revokedAt != null ? revokedAt : Instant.now()) : Instant.now();
        return end.getEpochSecond() - issuedAt.getEpochSecond();
    }
    public Map<String, String> toRevokeMap() {
        Map<String, String> hash = new HashMap<>();
        hash.put("revoked", Boolean.toString(revoked));
        hash.put("revokedAt", revokedAt != null ? revokedAt.toString() : "");
        return hash;
    }
    /**
     * 转换为Map，便于Redis存储
     */
    public Map<String, Object> toMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("userId", userId);
        map.put("username", username);
        map.put("clientId", clientId);
        map.put("tokenValue",tokenValue);
        map.put("scopes", scopes != null ? String.join(",", scopes) : "");
        map.put("issuedAt", issuedAt != null ? issuedAt.toString() : null);
        map.put("expiresAt", expiresAt != null ? expiresAt.toString() : null);
        map.put("deviceId", deviceId);
        map.put("deviceType", deviceType != null ? deviceType.name() : null);
        map.put("ipAddress", ipAddress);
        map.put("userAgent", userAgent);
        map.put("revoked", Boolean.toString(revoked));
        map.put("revokedAt", revokedAt != null ? revokedAt.toString() : null);
        map.put("refreshTokenId", refreshTokenId);
        map.put("jti", jti);
        map.put("additionalInfo", additionalInfo);
        return map;
    }
    
    /**
     * 从Map创建AccessTokenInfo
     */
    public static AccessTokenInfo fromMap(Map<String, Object> map) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        
        AccessTokenInfo.AccessTokenInfoBuilder builder = AccessTokenInfo.builder();
        
        builder.userId((String) map.get("userId"));
        builder.username((String) map.get("username"));
        builder.clientId((String) map.get("clientId"));
        builder.tokenValue((String) map.get("tokenValue"));
        
        // 处理scopes
        String scopesStr = (String) map.get("scopes");
        if (scopesStr != null && !scopesStr.isEmpty()) {
            builder.scopes(new HashSet<>(Arrays.asList(scopesStr.split(","))));
        }
        
        // 处理时间字段
        String issuedAtStr = (String) map.get("issuedAt");
        if (issuedAtStr != null) {
            builder.issuedAt(Instant.parse(issuedAtStr));
        }
        
        String expiresAtStr = (String) map.get("expiresAt");
        if (expiresAtStr != null) {
            builder.expiresAt(Instant.parse(expiresAtStr));
        }
        
        builder.deviceId((String) map.get("deviceId"));
        
        // 处理deviceType
        String deviceTypeStr = (String) map.get("deviceType");
        if (deviceTypeStr != null) {
            try {
                builder.deviceType(DeviceType.valueOf(deviceTypeStr));
            } catch (IllegalArgumentException e) {
                builder.deviceType(DeviceType.OTHER);
            }
        }
        
        builder.ipAddress((String) map.get("ipAddress"));
        builder.userAgent((String) map.get("userAgent"));
        
        // 处理布尔值
        String revokedStr = (String) map.get("revoked");
        if (revokedStr != null) {
            builder.revoked(Boolean.parseBoolean(revokedStr));
        }
        
        String revokedAtStr = (String) map.get("revokedAt");
        if (revokedAtStr != null) {
            builder.revokedAt(Instant.parse(revokedAtStr));
        }
        
        builder.refreshTokenId((String) map.get("refreshTokenId"));
        builder.jti((String) map.get("jti"));
        builder.additionalInfo((String) map.get("additionalInfo"));
        
        return builder.build();
    }
    
    /**
     * 简化的用户信息（用于接口返回）
     */
    public Map<String, Object> toSimpleInfo() {
        Map<String, Object> info = new HashMap<>();
        info.put("userId", userId);
        info.put("username", username);
        info.put("clientId", clientId);
        info.put("scopes", scopes);
        info.put("expiresAt", expiresAt != null ? expiresAt.toEpochMilli() : null);
        info.put("issuedAt", issuedAt != null ? issuedAt.toEpochMilli() : null);
        info.put("deviceType", deviceType != null ? deviceType.name() : null);
        info.put("valid", isValid());
        return info;
    }
}