package services

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

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

// UploadStateStore defines the interface for storing upload state
type UploadStateStore interface {
	SetState(ctx context.Context, api *ChunkUploadInfo) error
	GetState(ctx context.Context, uploadID string) (*ChunkUploadInfo, error)
	DeleteState(ctx context.Context, uploadID string) error
}

// RedisUploadStore implements UploadStateStore using Redis
type RedisUploadStore struct {
	client *redis.Client
	ttl    time.Duration
}

// NewRedisUploadStore create a new RedisUploadStore
func NewRedisUploadStore(client *redis.Client, ttl time.Duration) *RedisUploadStore {
	return &RedisUploadStore{
		client: client,
		ttl:    ttl,
	}
}

func (s *RedisUploadStore) getKey(uploadID string) string {
	return fmt.Sprintf("veza:upload:%s", uploadID)
}

func (s *RedisUploadStore) SetState(ctx context.Context, info *ChunkUploadInfo) error {
	data, err := json.Marshal(info)
	if err != nil {
		return fmt.Errorf("failed to marshal upload info: %w", err)
	}

	key := s.getKey(info.UploadID)
	// Use the configured TTL (e.g. 24h) to automatically clean up state
	if err := s.client.Set(ctx, key, data, s.ttl).Err(); err != nil {
		return fmt.Errorf("failed to save upload state to redis: %w", err)
	}
	return nil
}

func (s *RedisUploadStore) GetState(ctx context.Context, uploadID string) (*ChunkUploadInfo, error) {
	key := s.getKey(uploadID)
	data, err := s.client.Get(ctx, key).Bytes()
	if err != nil {
		if err == redis.Nil {
			return nil, fmt.Errorf("upload not found")
		}
		return nil, fmt.Errorf("failed to get upload state from redis: %w", err)
	}

	var info ChunkUploadInfo
	if err := json.Unmarshal(data, &info); err != nil {
		return nil, fmt.Errorf("failed to unmarshal upload info: %w", err)
	}
	// Re-initialize map if nil (can happen with JSON)
	if info.Chunks == nil {
		info.Chunks = make(map[int]ChunkInfo)
	}

	return &info, nil
}

func (s *RedisUploadStore) DeleteState(ctx context.Context, uploadID string) error {
	key := s.getKey(uploadID)
	if err := s.client.Del(ctx, key).Err(); err != nil {
		return fmt.Errorf("failed to delete upload state from redis: %w", err)
	}
	return nil
}
