/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package redis

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

	"github.com/apache/answer-plugins/cache-redis/i18n"
	"github.com/apache/answer-plugins/util"
	"github.com/apache/answer/plugin"
	"github.com/go-redis/redis/v8"
)

var (
	configuredErr = fmt.Errorf("redis is not configured correctly")
	//go:embed  info.yaml
	Info embed.FS
)

type Cache struct {
	Config      *CacheConfig
	RedisClient *redis.Client
}

type CacheConfig struct {
	Endpoint string `json:"endpoint"`
	Username string `json:"username"`
	Password string `json:"password"`
}

func init() {
	plugin.Register(&Cache{
		Config: &CacheConfig{},
	})
}

func (c *Cache) Info() plugin.Info {
	info := &util.Info{}
	info.GetInfo(Info)

	return plugin.Info{
		Name:        plugin.MakeTranslator(i18n.InfoName),
		SlugName:    info.SlugName,
		Description: plugin.MakeTranslator(i18n.InfoDescription),
		Author:      info.Author,
		Version:     info.Version,
		Link:        info.Link,
	}
}

func (c *Cache) GetString(ctx context.Context, key string) (data string, exist bool, err error) {
	if c.RedisClient == nil {
		return "", false, configuredErr
	}
	data, err = c.RedisClient.Get(ctx, key).Result()
	if err == redis.Nil {
		return "", false, nil
	}
	if err != nil {
		return "", false, err
	}
	return data, true, nil
}

func (c *Cache) SetString(ctx context.Context, key, value string, ttl time.Duration) error {
	if c.RedisClient == nil {
		return configuredErr
	}
	return c.RedisClient.Set(ctx, key, value, ttl).Err()
}

func (c *Cache) GetInt64(ctx context.Context, key string) (data int64, exist bool, err error) {
	if c.RedisClient == nil {
		return 0, false, configuredErr
	}
	data, err = c.RedisClient.Get(ctx, key).Int64()
	if err == redis.Nil {
		return 0, false, nil
	}
	if err != nil {
		return 0, false, err
	}
	return data, true, nil
}

func (c *Cache) SetInt64(ctx context.Context, key string, value int64, ttl time.Duration) error {
	if c.RedisClient == nil {
		return configuredErr
	}
	return c.RedisClient.Set(ctx, key, value, ttl).Err()
}

func (c *Cache) Increase(ctx context.Context, key string, value int64) (data int64, err error) {
	if c.RedisClient == nil {
		return 0, configuredErr
	}
	return c.RedisClient.IncrBy(ctx, key, value).Result()
}

func (c *Cache) Decrease(ctx context.Context, key string, value int64) (data int64, err error) {
	if c.RedisClient == nil {
		return 0, configuredErr
	}
	return c.RedisClient.DecrBy(ctx, key, value).Result()
}

func (c *Cache) Del(ctx context.Context, key string) error {
	if c.RedisClient == nil {
		return configuredErr
	}
	return c.RedisClient.Del(ctx, key).Err()
}

func (c *Cache) Flush(ctx context.Context) error {
	if c.RedisClient == nil {
		return configuredErr
	}
	return c.RedisClient.FlushDB(ctx).Err()
}

func (c *Cache) ConfigFields() []plugin.ConfigField {
	return []plugin.ConfigField{
		{
			Name:        "endpoint",
			Type:        plugin.ConfigTypeInput,
			Title:       plugin.MakeTranslator(i18n.ConfigEndpointTitle),
			Description: plugin.MakeTranslator(i18n.ConfigEndpointDescription),
			Required:    true,
			UIOptions: plugin.ConfigFieldUIOptions{
				InputType: plugin.InputTypeText,
			},
			Value: c.Config.Endpoint,
		},
		{
			Name:        "username",
			Type:        plugin.ConfigTypeInput,
			Title:       plugin.MakeTranslator(i18n.ConfigUsernameTitle),
			Description: plugin.MakeTranslator(i18n.ConfigUsernameDescription),
			Required:    false,
			UIOptions: plugin.ConfigFieldUIOptions{
				InputType: plugin.InputTypeText,
			},
			Value: c.Config.Username,
		},
		{
			Name:        "password",
			Type:        plugin.ConfigTypeInput,
			Title:       plugin.MakeTranslator(i18n.ConfigPasswordTitle),
			Description: plugin.MakeTranslator(i18n.ConfigPasswordDescription),
			Required:    false,
			UIOptions: plugin.ConfigFieldUIOptions{
				InputType: plugin.InputTypePassword,
			},
			Value: c.Config.Password,
		},
	}
}

func (c *Cache) ConfigReceiver(config []byte) error {
	conf := &CacheConfig{}
	_ = json.Unmarshal(config, conf)
	c.Config = conf

	c.RedisClient = redis.NewClient(&redis.Options{
		Addr:     conf.Endpoint,
		Username: conf.Username,
		Password: conf.Password,
	})
	return nil
}
