package iam

import (
	"context"
	"errors"
	"time"

	"github.com/redis/go-redis/v9"
)

type tokenCache struct {
	client *redis.Client
}

func newTokenCache(addr, password string, db int) (*tokenCache, error) {
	if addr == "" {
		return nil, nil
	}
	client := redis.NewClient(&redis.Options{Addr: addr, Password: password, DB: db})
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	if err := client.Ping(ctx).Err(); err != nil {
		return nil, err
	}
	return &tokenCache{client: client}, nil
}

func (c *tokenCache) Close() error {
	if c == nil || c.client == nil {
		return nil
	}
	return c.client.Close()
}

func (c *tokenCache) SetSessionActive(ctx context.Context, sid string, ttl time.Duration) error {
	if c == nil {
		return nil
	}
	return c.client.Set(ctx, "auth:session:"+sid, "active", ttl).Err()
}

func (c *tokenCache) IsSessionActive(ctx context.Context, sid string) (bool, error) {
	if c == nil {
		return false, nil
	}
	count, err := c.client.Exists(ctx, "auth:session:"+sid).Result()
	if err != nil {
		return false, err
	}
	return count == 1, nil
}

func (c *tokenCache) DeleteSession(ctx context.Context, sid string) error {
	if c == nil {
		return nil
	}
	return c.client.Del(ctx, "auth:session:"+sid).Err()
}

func (c *tokenCache) SetRefreshHash(ctx context.Context, rid, tokenHash string, ttl time.Duration) error {
	if c == nil {
		return nil
	}
	return c.client.Set(ctx, "auth:refresh:"+rid, tokenHash, ttl).Err()
}

func (c *tokenCache) GetRefreshHash(ctx context.Context, rid string) (string, error) {
	if c == nil {
		return "", nil
	}
	v, err := c.client.Get(ctx, "auth:refresh:"+rid).Result()
	if err != nil {
		if errors.Is(err, redis.Nil) {
			return "", nil
		}
		return "", err
	}
	return v, nil
}

func (c *tokenCache) DeleteRefresh(ctx context.Context, rid string) error {
	if c == nil {
		return nil
	}
	return c.client.Del(ctx, "auth:refresh:"+rid).Err()
}

func (c *tokenCache) DenyAccessJTI(ctx context.Context, jti string, ttl time.Duration) error {
	if c == nil {
		return nil
	}
	return c.client.Set(ctx, "auth:deny:access:"+jti, "1", ttl).Err()
}

func (c *tokenCache) IsAccessJTIDenied(ctx context.Context, jti string) (bool, error) {
	if c == nil {
		return false, nil
	}
	count, err := c.client.Exists(ctx, "auth:deny:access:"+jti).Result()
	if err != nil {
		return false, err
	}
	return count == 1, nil
}
