package session

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

	"github.com/google/uuid"
	"github.com/labstack/echo/v4"
	"github.com/redis/go-redis/v9"

	"github.com/chaitin/MonkeyCode/backend/config"
)

type Session struct {
	cfg *config.Config
	rdb *redis.Client
}

func NewSession(cfg *config.Config) *Session {
	addr := net.JoinHostPort(cfg.Redis.Host, fmt.Sprint(cfg.Redis.Port))
	rdb := redis.NewClient(&redis.Options{
		Addr:     addr,
		Password: cfg.Redis.Pass,
		DB:       3,
	})

	return &Session{
		cfg: cfg,
		rdb: rdb,
	}
}

func (s *Session) Save(c echo.Context, name, domain string, data any) (string, error) {
	expire := time.Duration(s.cfg.Session.ExpireDay) * 24 * time.Hour
	id := uuid.New().String()
	b, err := json.Marshal(data)
	if err != nil {
		return "", err
	}

	if ok, err := s.rdb.SetNX(context.Background(), id, string(b), expire).Result(); !ok || err != nil {
		return "", fmt.Errorf("failed to save session: %w", err)
	}

	c.SetCookie(&http.Cookie{
		Name:     name,
		Value:    id,
		Path:     "/",
		Domain:   domain,
		MaxAge:   int(s.cfg.Session.ExpireDay) * 24 * 60 * 60,
		Secure:   false,
		HttpOnly: true,
	})

	return id, nil
}

func (s *Session) Del(c echo.Context, name string) error {
	ck, err := c.Cookie(name)
	if err != nil {
		return err
	}
	if err := s.rdb.Del(c.Request().Context(), ck.Value).Err(); err != nil {
		return err
	}
	ck.MaxAge = -1
	c.SetCookie(ck)
	return nil
}

func Get[T any](s *Session, c echo.Context, name string) (T, error) {
	var t T
	ck, err := c.Cookie(name)
	if err != nil {
		return t, err
	}
	ss, err := s.rdb.Get(c.Request().Context(), ck.Value).Result()
	if err != nil {
		return t, err
	}
	if err := json.Unmarshal([]byte(ss), &t); err != nil {
		return t, err
	}
	return t, err
}
