package redis

import (
	"bytes"
	"compress/flate"
	"compress/zlib"
	"context"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/redis/rueidis"
)

var Client *rueidis.Client

func InitClient(ctx context.Context) error {
	var err error
	host := os.Getenv("REDIS_HOST")
	//host := "redis"
	port := os.Getenv("REDIS_PORT")
	password := os.Getenv("REDIS_PASSWORD")

	client, err := rueidis.NewClient(rueidis.ClientOption{
		InitAddress: []string{host + ":" + port},
		Password:    password,
	})
	if err != nil {
		return err
	}
	err = client.Do(ctx, client.B().Ping().Build()).Error()
	if err != nil {
		return err
	}
	Client = &client
	return nil
}

func CloseClient() {
	if Client != nil {
		(*Client).Close()
	}
}

// WriteProtoMessage compresses and writes a protobuf message to Redis
func WriteProtoMessage(ctx context.Context, key string, message proto.Message, ttl ...time.Duration) error {
	if Client == nil {
		return fmt.Errorf("redis client not initialized")
	}
	if message == nil {
		return fmt.Errorf("message is nil")
	}

	// Marshal protobuf message
	bytesMessage, err := proto.Marshal(message)
	if err != nil {
		return fmt.Errorf("failed to marshal proto message: %w", err)
	}

	// Compress with zlib
	var buf bytes.Buffer
	w, err := zlib.NewWriterLevel(&buf, flate.BestCompression)
	if err != nil {
		return fmt.Errorf("failed to create zlib writer: %w", err)
	}
	defer w.Close() // гарантированное закрытие

	if _, err := w.Write(bytesMessage); err != nil {
		return fmt.Errorf("failed to write to zlib writer: %w", err)
	}
	if err := w.Close(); err != nil { // финализируем сжатие
		return fmt.Errorf("failed to close zlib writer: %w", err)
	}

	var ttlDuration time.Duration
	if len(ttl) > 0 {
		ttlDuration = ttl[0]
	} else {
		ttlDuration = 3 * time.Hour // Default TTL of 24 hours
	}
	// Write to Redis
	if err := (*Client).Do(ctx, (*Client).B().
		Set().
		Key(key).
		Value(rueidis.BinaryString(buf.Bytes())).
		Ex(ttlDuration).
		Build()).
		Error(); err != nil {
		return fmt.Errorf("failed to write compressed data to Redis: %w", err)
	}
	return nil
}

// ReadProtoMessage reads and decompresses a protobuf message from Redis
func ReadProtoMessage(ctx context.Context, key string, message proto.Message) error {
	if Client == nil {
		return fmt.Errorf("redis client not initialized")
	}
	if message == nil {
		return fmt.Errorf("message is nil")
	}

	// Get bytes from Redis
	resp, err := (*Client).Do(ctx, (*Client).B().Get().Key(key).Build()).AsBytes()
	if err != nil {
		return fmt.Errorf("failed to read data from Redis: %w", err)
	}
	if resp == nil {
		return fmt.Errorf("no data found for key: %s", key)
	}

	// Decompress
	reader, err := zlib.NewReader(bytes.NewReader(resp))
	if err != nil {
		return fmt.Errorf("failed to create zlib reader: %w", err)
	}
	defer reader.Close()

	decompressed, err := io.ReadAll(reader)
	if err != nil {
		return fmt.Errorf("failed to decompress data: %w", err)
	}

	// Unmarshal protobuf
	if err := proto.Unmarshal(decompressed, message); err != nil {
		return fmt.Errorf("failed to unmarshal proto message: %w", err)
	}

	return nil
}
