package session

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

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

const keyTemplate = "sso:pkce:%s"

// PKCESession holds data required to complete the OAuth2 PKCE exchange.
type PKCESession struct {
	CodeVerifier string    `json:"code_verifier"`
	Nonce        string    `json:"nonce"`
	ClientAlias  string    `json:"client_alias"`
	ClientID     string    `json:"client_id"`
	RedirectURI  string    `json:"redirect_uri"`
	Scope        string    `json:"scope"`
	ReturnTo     string    `json:"return_to,omitempty"`
	CreatedAt    time.Time `json:"created_at"`
}

// Store persists pkce sessions inside Redis using a configurable TTL.
type Store struct {
	redis *redis.Client
	ttl   time.Duration
}

func NewStore(client *redis.Client, ttl time.Duration) *Store {
	return &Store{redis: client, ttl: ttl}
}

func (s *Store) Save(ctx context.Context, state string, payload *PKCESession) error {
	if s.redis == nil {
		return fmt.Errorf("redis client is not initialised")
	}
	bytes, err := json.Marshal(payload)
	if err != nil {
		return err
	}
	return s.redis.Set(ctx, fmt.Sprintf(keyTemplate, state), bytes, s.ttl).Err()
}

func (s *Store) Get(ctx context.Context, state string) (*PKCESession, error) {
	if s.redis == nil {
		return nil, fmt.Errorf("redis client is not initialised")
	}
	raw, err := s.redis.Get(ctx, fmt.Sprintf(keyTemplate, state)).Result()
	if err != nil {
		if err == redis.Nil {
			return nil, nil
		}
		return nil, err
	}
	var payload PKCESession
	if err := json.Unmarshal([]byte(raw), &payload); err != nil {
		return nil, err
	}
	return &payload, nil
}

func (s *Store) Delete(ctx context.Context, state string) error {
	if s.redis == nil {
		return fmt.Errorf("redis client is not initialised")
	}
	return s.redis.Del(ctx, fmt.Sprintf(keyTemplate, state)).Err()
}
