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 org.lingniu.idp.enums.RevokeReason;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Arrays;

/**
 * Refresh Token 信息
 * 存储在 Redis Hash 结构中
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RefreshTokenInfo {
    private String tokenValue;
    
    /**
     * 用户ID
     */
    private String userId;
    
    /**
     * 用户名
     */
    private String username;
    
    /**
     * 客户端ID
     */
    private String clientId;
    
    /**
     * 设备ID（唯一标识设备）
     */
    private String deviceId;
    
    /**
     * 设备名称（用户自定义）
     */
    private String deviceName;
    
    /**
     * 设备类型
     */
    private DeviceType deviceType;
    
    /**
     * 操作系统
     */
    private String operatingSystem;
    
    /**
     * 浏览器/客户端类型
     */
    private String clientType;
    
    /**
     * 权限范围
     */
    private Set<String> scopes;
    
    /**
     * 创建时间
     */
    private Instant createdAt;
    
    /**
     * 最后使用时间
     */
    private Instant lastUsedAt;
    
    /**
     * 过期时间
     */
    private Instant expiresAt;
    
    /**
     * IP地址（创建时的IP）
     */
    private String ipAddress;
    
    /**
     * 用户代理（创建时的User-Agent）
     */
    private String userAgent;
    
    /**
     * 是否已撤销
     */
    private boolean revoked;
    
    /**
     * 撤销时间
     */
    private Instant revokedAt;
    
    /**
     * 撤销原因
     */
    private RevokeReason revokeReason;
    /**
     * 对应accessToken
     */
    private String accessToken;
    /**
     * 关联的Access Token数量（用于统计）
     */
    private int accessTokenCount;
    
    /**
     * 使用次数
     */
    private int usageCount;
    
    /**
     * 地理位置信息（可选）
     */
    private String location;
    
    /**
     * 附加数据（JSON格式）
     */
    private String additionalInfo;
    
    /**
     * 是否记住登录
     */
    private boolean rememberMe;
    

    

    
    /**
     * 检查Refresh Token是否过期
     */
    public boolean isExpired() {
        return expiresAt != null && Instant.now().isAfter(expiresAt);
    }
    
    /**
     * 检查Refresh 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();
    }
    
    /**
     * 获取活跃天数（创建到现在）
     */
    public long getActiveDays() {
        if (createdAt == null) {
            return 0;
        }
        Instant end = revoked ? (revokedAt != null ? revokedAt : Instant.now()) : Instant.now();
        long seconds = end.getEpochSecond() - createdAt.getEpochSecond();
        return seconds / (24 * 3600);
    }
    
    /**
     * 获取闲置天数（最后使用到现在）
     */
    public long getIdleDays() {
        if (lastUsedAt == null) {
            return getActiveDays();
        }
        Instant now = Instant.now();
        long seconds = now.getEpochSecond() - lastUsedAt.getEpochSecond();
        return seconds / (24 * 3600);
    }
    
    /**
     * 增加使用计数
     */
    public void incrementUsage() {
        this.usageCount++;
        this.lastUsedAt = Instant.now();
    }

    public void incrementAccessTokenUsage(String accessToken) {
        this.accessTokenCount++;
        this.accessToken = accessToken;
    }
    
    /**
     * 撤销Token
     */
    public void revoke(RevokeReason reason) {
        this.revoked = true;
        this.revokedAt = Instant.now();
        this.revokeReason = reason;
    }
    
    /**
     * 转换为Map，便于Redis存储
     */
    public Map<String, String> toMap() {
        Map<String, String> hash = new HashMap<>();
        
        hash.put("userId", userId != null ? userId : "");
        hash.put("username", username != null ? username : "");
        hash.put("clientId", clientId != null ? clientId : "");
        hash.put("deviceId", deviceId != null ? deviceId : "");
        hash.put("tokenValue", tokenValue != null ? tokenValue : "");
        hash.put("deviceName", deviceName != null ? deviceName : "");
        hash.put("deviceType", deviceType != null ? deviceType.name() : DeviceType.OTHER.name());
        hash.put("operatingSystem", operatingSystem != null ? operatingSystem : "");
        hash.put("clientType", clientType != null ? clientType : "");
        hash.put("accessToken", accessToken != null ? accessToken : "");
        hash.put("scopes", scopes != null ? String.join(",", scopes) : "");
        hash.put("createdAt", createdAt != null ? createdAt.toString() : "");
        hash.put("lastUsedAt", lastUsedAt != null ? lastUsedAt.toString() : "");
        hash.put("expiresAt", expiresAt != null ? expiresAt.toString() : "");
        hash.put("ipAddress", ipAddress != null ? ipAddress : "");
        hash.put("userAgent", userAgent != null ? userAgent : "");
        hash.put("revoked", Boolean.toString(revoked));
        hash.put("revokedAt", revokedAt != null ? revokedAt.toString() : "");
        hash.put("revokeReason", revokeReason != null ? revokeReason.name() : "");
        hash.put("accessTokenCount", Integer.toString(accessTokenCount));
        hash.put("usageCount", Integer.toString(usageCount));
        hash.put("location", location != null ? location : "");
        hash.put("additionalInfo", additionalInfo != null ? additionalInfo : "");
        hash.put("rememberMe", Boolean.toString(rememberMe));
        
        return hash;
    }
    public Map<String, String> toUpdateMap() {
        Map<String, String> hash = new HashMap<>();
        hash.put("lastUsedAt", lastUsedAt != null ? lastUsedAt.toString() : "");
        hash.put("accessTokenCount", Integer.toString(accessTokenCount));
        hash.put("accessToken", accessToken != null ? accessToken : "");
        hash.put("usageCount", Integer.toString(usageCount));
        return hash;
    }
    public Map<String, String> toRevokeMap() {
        Map<String, String> hash = new HashMap<>();
        hash.put("revoked", Boolean.toString(revoked));
        hash.put("revokedAt", revokedAt != null ? revokedAt.toString() : "");
        hash.put("revokeReason", revokeReason != null ? revokeReason.name() : "");
        return hash;
    }
    
    /**
     * 从Redis Hash创建RefreshTokenInfo
     */
    public static RefreshTokenInfo fromMap(Map<String, Object> hash) {
        if (hash == null || hash.isEmpty()) {
            return null;
        }
        
        RefreshTokenInfoBuilder builder = RefreshTokenInfo.builder();
        
        builder.userId((String) hash.getOrDefault("userId", ""));
        builder.username((String) hash.getOrDefault("username", ""));
        builder.clientId((String) hash.getOrDefault("clientId", ""));
        builder.deviceId((String) hash.getOrDefault("deviceId", ""));
        builder.deviceName((String) hash.getOrDefault("deviceName", ""));
        builder.accessToken((String) hash.getOrDefault("accessToken", ""));
        builder.tokenValue((String) hash.getOrDefault("tokenValue", ""));

        // 处理deviceType
        String deviceTypeStr = (String)hash.get("deviceType");
        if (deviceTypeStr != null && !deviceTypeStr.isEmpty()) {
            try {
                builder.deviceType(DeviceType.valueOf(deviceTypeStr));
            } catch (IllegalArgumentException e) {
                builder.deviceType(DeviceType.OTHER);
            }
        } else {
            builder.deviceType(DeviceType.OTHER);
        }
        
        builder.operatingSystem((String) hash.getOrDefault("operatingSystem", ""));
        builder.clientType((String) hash.getOrDefault("clientType", ""));
        
        // 处理scopes
        String scopesStr = (String)hash.get("scopes");
        if (scopesStr != null && !scopesStr.isEmpty()) {
            builder.scopes(new HashSet<>(Arrays.asList(scopesStr.split(","))));
        } else {
            builder.scopes(new HashSet<>());
        }
        
        // 处理时间字段
        String createdAtStr = (String)hash.get("createdAt");
        if (createdAtStr != null && !createdAtStr.isEmpty()) {
            try {
                builder.createdAt(Instant.parse(createdAtStr));
            } catch (Exception e) {
                // 解析失败，使用当前时间
                builder.createdAt(Instant.now());
            }
        }
        
        String lastUsedAtStr = (String)hash.get("lastUsedAt");
        if (lastUsedAtStr != null && !lastUsedAtStr.isEmpty()) {
            try {
                builder.lastUsedAt(Instant.parse(lastUsedAtStr));
            } catch (Exception e) {
                // 解析失败，忽略
            }
        }
        
        String expiresAtStr = (String)hash.get("expiresAt");
        if (expiresAtStr != null && !expiresAtStr.isEmpty()) {
            try {
                builder.expiresAt(Instant.parse(expiresAtStr));
            } catch (Exception e) {
                // 解析失败，忽略
            }
        }
        
        builder.ipAddress((String) hash.getOrDefault("ipAddress", ""));
        builder.userAgent((String) hash.getOrDefault("userAgent", ""));
        
        // 处理布尔值
        String revokedStr = (String)hash.get("revoked");
        builder.revoked(Boolean.parseBoolean(revokedStr));
        
        String revokedAtStr = (String)hash.get("revokedAt");
        if (revokedAtStr != null && !revokedAtStr.isEmpty()) {
            try {
                builder.revokedAt(Instant.parse(revokedAtStr));
            } catch (Exception e) {
                // 解析失败，忽略
            }
        }
        
        // 处理撤销原因
        String revokeReasonStr = (String)hash.get("revokeReason");
        if (revokeReasonStr != null && !revokeReasonStr.isEmpty()) {
            try {
                builder.revokeReason(RevokeReason.valueOf(revokeReasonStr));
            } catch (IllegalArgumentException e) {
                builder.revokeReason(RevokeReason.OTHER);
            }
        }
        
        // 处理数值字段
        String accessTokenCountStr = (String)hash.get("accessTokenCount");
        if (accessTokenCountStr != null && !accessTokenCountStr.isEmpty()) {
            try {
                builder.accessTokenCount(Integer.parseInt(accessTokenCountStr));
            } catch (NumberFormatException e) {
                builder.accessTokenCount(0);
            }
        }
        
        String usageCountStr = (String)hash.get("usageCount");
        if (usageCountStr != null && !usageCountStr.isEmpty()) {
            try {
                builder.usageCount(Integer.parseInt(usageCountStr));
            } catch (NumberFormatException e) {
                builder.usageCount(0);
            }
        }
        
        builder.location((String) hash.getOrDefault("location", ""));
        builder.additionalInfo((String) hash.getOrDefault("additionalInfo", ""));
        
        String rememberMeStr = (String)hash.get("rememberMe");
        builder.rememberMe(Boolean.parseBoolean(rememberMeStr));
        
        return builder.build();
    }
    
    /**
     * 简化的设备信息（用于前端展示）
     */
    public Map<String, Object> toSimpleDeviceInfo() {
        Map<String, Object> info = new HashMap<>();
        info.put("deviceId", deviceId);
        info.put("deviceName", deviceName);
        info.put("deviceType", deviceType != null ? deviceType.name() : null);
        info.put("deviceTypeDesc", deviceType != null ? deviceType.getDescription() : null);
        info.put("operatingSystem", operatingSystem);
        info.put("clientType", clientType);
        info.put("ipAddress", ipAddress);
        info.put("location", location);
        info.put("createdAt", createdAt != null ? createdAt.toEpochMilli() : null);
        info.put("lastUsedAt", lastUsedAt != null ? lastUsedAt.toEpochMilli() : null);
        info.put("active", !revoked && !isExpired());
        return info;
    }
    
    /**
     * 获取可读的设备信息
     */
    public String getReadableDeviceInfo() {
        StringBuilder sb = new StringBuilder();
        
        if (deviceName != null && !deviceName.isEmpty()) {
            sb.append(deviceName);
        } else if (deviceType != null) {
            sb.append(deviceType.getDescription());
        }
        
        if (operatingSystem != null && !operatingSystem.isEmpty()) {
            sb.append(" (").append(operatingSystem);
            if (clientType != null && !clientType.isEmpty()) {
                sb.append(" - ").append(clientType);
            }
            sb.append(")");
        } else if (clientType != null && !clientType.isEmpty()) {
            sb.append(" (").append(clientType).append(")");
        }
        
        return sb.toString();
    }
}