package auth

import (
	"context"
	"encoding/json"
	"errors"
	"strings"
	"time"

	"github.com/redis/go-redis/v9"
	"hyapp/pkg/appcode"
)

type RedisIPDecisionCache struct {
	client *redis.Client
}

func NewRedisIPDecisionCache(client *redis.Client) *RedisIPDecisionCache {
	return &RedisIPDecisionCache{client: client}
}

func NewLoginRiskRedisClient(ctx context.Context, addr string, password string, db int) (*redis.Client, error) {
	client := redis.NewClient(&redis.Options{
		Addr:     strings.TrimSpace(addr),
		Password: password,
		DB:       db,
	})
	if err := client.Ping(ctx).Err(); err != nil {
		_ = client.Close()
		return nil, err
	}
	return client, nil
}

func (c *RedisIPDecisionCache) GetIPDecision(ctx context.Context, appCode string, clientIPHash string) (IPDecision, bool, error) {
	raw, err := c.client.Get(ctx, loginRiskIPDecisionKey(appCode, clientIPHash)).Result()
	if errors.Is(err, redis.Nil) {
		return IPDecision{}, false, nil
	}
	if err != nil {
		return IPDecision{}, false, err
	}
	var decision IPDecision
	if err := json.Unmarshal([]byte(raw), &decision); err != nil {
		return IPDecision{}, false, err
	}
	return decision, true, nil
}

func (c *RedisIPDecisionCache) SetIPDecision(ctx context.Context, appCode string, clientIPHash string, decision IPDecision, ttl time.Duration) error {
	raw, err := json.Marshal(decision)
	if err != nil {
		return err
	}
	return c.client.Set(ctx, loginRiskIPDecisionKey(appCode, clientIPHash), raw, ttl).Err()
}

func (c *RedisIPDecisionCache) SetRevokedSession(ctx context.Context, appCode string, sessionID string, reason string, ttl time.Duration) error {
	return c.client.Set(ctx, revokedSessionKey(appCode, sessionID), strings.TrimSpace(reason), ttl).Err()
}

func loginRiskIPDecisionKey(appCode string, clientIPHash string) string {
	return "login_risk:ip:" + appcode.Normalize(appCode) + ":" + strings.TrimSpace(clientIPHash)
}

func revokedSessionKey(appCode string, sessionID string) string {
	return "auth:revoked_session:" + appcode.Normalize(appCode) + ":" + strings.TrimSpace(sessionID)
}
