// Package sessioncache add by blacknc <blacknc@163.com> at 2023/07/31 16:52:43
package sessioncache

import (
	"context"
	"dataservices/commons/errs"
	"dataservices/commons/redisutil"
	"encoding/json"
	"time"

	"github.com/go-redis/redis/v8"
	tierrs "github.com/tidbcloud/tiinf/errs"
)

type redisSession struct {
	rds *redis.Client
}

type localSession struct{}

func RegisterRedisDriver(ctx context.Context) {
	sessionImpl = &redisSession{rds: redisutil.MasterRedisClient()}
}

func RegisterLocalDriver(ctx context.Context) {
	sessionImpl = &localSession{}
}

func (s *redisSession) Get(ctx context.Context, sessionID string) (*SessionItem, error) {
	key := sessionKey(sessionID)
	encryptSession, err := s.rds.Get(ctx, key).Bytes()
	if err != nil {
		return nil, tierrs.NewInternalRedis("failed to get session", err)
	}
	if len(encryptSession) == 0 {
		return nil, errs.ErrInvalidSession
	}
	var sessionItem SessionItem
	if err := json.Unmarshal(encryptSession, &sessionItem); err != nil {
		return nil, tierrs.NewInternalGeneralBusiness("got invalid session", err)
	}
	return &sessionItem, nil
}

func (s *redisSession) Load(ctx context.Context, sessionItem *SessionItem) error {
	key := sessionKey(sessionItem.SessionID)
	encryptSession, err := s.rds.Get(ctx, key).Bytes()
	if err != nil {
		return tierrs.NewInternalRedis("failed to get session", err)
	}
	if len(encryptSession) == 0 {
		return errs.ErrInvalidSession
	}
	if err := json.Unmarshal(encryptSession, sessionItem); err != nil {
		return tierrs.NewInternalGeneralBusiness("failed to parse session", err)
	}
	return nil
}

func (s *redisSession) Save(ctx context.Context, sessionItem *SessionItem) error {
	key := sessionKey(sessionItem.SessionID)
	sessionBytes, err := json.Marshal(sessionItem)
	if err != nil {
		return tierrs.NewInternalGeneralBusiness("got invalid session", err)
	}
	_, err = s.rds.Set(ctx, key, string(sessionBytes), time.Duration(sessionItem.ExpireSec)*time.Second).Result()
	if err != nil {
		return tierrs.NewInternalRedis("failed to save session", err)
	}
	return nil
}

func (s *redisSession) Delete(ctx context.Context, sessionID string) error {
	key := sessionKey(sessionID)
	_, err := s.rds.Del(ctx, key).Result()
	if err != nil {
		return tierrs.NewInternalRedis("failed to delete session", err)
	}
	return nil
}

func (s *localSession) Get(ctx context.Context, sessionID string) (*SessionItem, error) {
	sessionItem, err := loadSession(sessionID)
	if err != nil {
		return nil, errs.ErrInvalidSession
	}
	return sessionItem, nil
}

func (s *localSession) Load(ctx context.Context, sessionItem *SessionItem) error {
	current, err := loadSession(sessionItem.SessionID)
	if err != nil {
		return errs.ErrInvalidSession
	}
	*sessionItem = *current
	return nil
}

func (s *localSession) Save(ctx context.Context, sessionItem *SessionItem) error {
	storage.sessionMap.Store(sessionItem.SessionID, sessionItem)
	return nil
}

func (s *localSession) Delete(ctx context.Context, sessionID string) error {
	if sessionItem, err := loadSession(sessionID); err == nil {
		closeSessionDB(sessionItem)
	}
	storage.sessionMap.Delete(sessionID)
	return nil
}
