package memcached

import (
	"context"

	"code.justin.tv/live/autohost/lib"

	"code.justin.tv/creator-collab/log/errors"
	"code.justin.tv/live/autohost/internal/hosting/config"
)

// Cache contains the higher level caching operations used by Autohost.
type Cache interface {
	GetList(ctx context.Context, userID string) ([]string, bool, error)
	SetList(ctx context.Context, userID string, list []string) error
	DeleteList(ctx context.Context, userID string) error

	GetSettings(ctx context.Context, userID string) (*lib.Settings, bool, error)
	SetSettings(ctx context.Context, userID string, settings *lib.Settings) error
	DeleteSettings(ctx context.Context, userID string) error

	// FlushAll clears all data in the memcached instance
	FlushAll() error
	// Close closes connections to memcached instance
	Close() error
}

func NewCache(conf config.Memcached) (Cache, error) {
	client, err := newClient(conf)
	if err != nil {
		return nil, err
	}

	return &cacheImpl{
		client: client,
	}, nil
}

type cacheImpl struct {
	client *memcachedClient
}

// cacheImpl should implement the Cache interface.
var _ Cache = (*cacheImpl)(nil)

func (c *cacheImpl) GetList(ctx context.Context, userID string) ([]string, bool, error) {
	key := getListKey(userID)

	var result []string
	found, err := c.client.Get(ctx, key, &result)
	if err != nil {
		return nil, false, errors.Wrap(err, "failed to retrieve AutoHost list from cache")
	} else if !found {
		return nil, false, nil
	}

	return result, true, nil
}

func (c *cacheImpl) SetList(ctx context.Context, userID string, list []string) error {
	key := getListKey(userID)

	err := c.client.Set(ctx, key, list)
	if err != nil {
		return errors.Wrap(err, "failed to cache autohost list", errors.Fields{
			"user_id": userID,
		})
	}

	return nil
}

func (c *cacheImpl) DeleteList(ctx context.Context, userID string) error {
	key := getListKey(userID)

	err := c.client.Delete(ctx, key)
	if err != nil {
		return errors.Wrap(err, "failed to delete autohost list from cache", errors.Fields{
			"user_id": userID,
		})
	}

	return nil
}

func (c *cacheImpl) GetSettings(ctx context.Context, userID string) (*lib.Settings, bool, error) {
	key := getSettingsKey(userID)

	var result lib.Settings
	found, err := c.client.Get(ctx, key, &result)
	if err != nil {
		return nil, false, errors.Wrap(err, "fetching AutoHost settings from memcached failed")
	} else if !found {
		return nil, false, nil
	}

	return &result, true, nil
}

func (c *cacheImpl) SetSettings(ctx context.Context, userID string, settings *lib.Settings) error {
	key := getSettingsKey(userID)

	err := c.client.Set(ctx, key, settings)
	if err != nil {
		return errors.Wrap(err, "storing AutoHost settings in memcached failed", errors.Fields{
			"user_id": userID,
		})
	}

	return nil
}

func (c *cacheImpl) DeleteSettings(ctx context.Context, userID string) error {
	key := getSettingsKey(userID)

	err := c.client.Delete(ctx, key)
	if err != nil {
		return errors.Wrap(err, "removing AutoHost settings from memcached failed", errors.Fields{
			"user_id": userID,
		})
	}

	return nil
}

func (c *cacheImpl) FlushAll() error {
	return c.client.FlushAll()
}

func (c *cacheImpl) Close() error {
	return c.client.Close()
}

func getListKey(userID string) string {
	return "autoHostList_" + userID
}

func getSettingsKey(userID string) string {
	return "autoHostSettings_" + userID
}
