package immudbcrudoperations

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

	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/dalmdl/coreimmudb"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/loggermdl"
	"github.com/codenotary/immudb/pkg/api/schema"
	"github.com/gin-gonic/gin"
)

type KeyValue struct {
	Key   string      `json:"key"`
	Value interface{} `json:"value"`
}

func SetKeyData(c *gin.Context) {
	p := KeyValue{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}
	byteData, err := json.Marshal(p.Value)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while marshalling JSON", err)
		return
	}

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).SetKeyData([]byte(p.Key), byteData)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while setting key data", err)
		return
	}

	c.JSON(http.StatusOK, res)
}

func SetAllKeyData(c *gin.Context) {
	p := struct {
		KeyValues []KeyValue `json:"keyValues"`
	}{}

	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}

	kvs := []coreimmudb.KeyValue{}
	for _, v := range p.KeyValues {
		byteValue, _ := json.Marshal(v.Value)
		kvs = append(kvs, coreimmudb.KeyValue{
			Key:   []byte(v.Key),
			Value: byteValue,
		})
	}

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).SetAllKeysData(kvs)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while setting all keys' data", err)
		return

	}
	c.JSON(http.StatusOK, res)

}

func SetVerifiedKeys(c *gin.Context) {

	p := KeyValue{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}

	byteData, err := json.Marshal(p.Value)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while marshalling JSON", err)
		return
	}

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).SetVerifiedKeyData([]byte(p.Key), byteData)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while setting verified key data", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func GetKeyData(c *gin.Context) {

	p := KeyValue{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while bindin data", err)
		return
	}

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).GetKeyData([]byte(p.Key))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while getting key data", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func GetAllKeysData(c *gin.Context) {
	p := struct {
		Keys []string `json:"keys"`
	}{}

	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}

	var keys [][]byte
	for _, v := range p.Keys {
		keys = append(keys, []byte(v))
	}

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).GetAllKeysData(keys)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while getting all keys data", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func GetKeyHistory(c *gin.Context) {

	type KeyHistory struct {
		Key    string `json:"key"`
		Offset uint64 `json:"offset"`
		Limit  int32  `json:"limit"`
		Desc   bool   `json:"desc"`
	}
	p := KeyHistory{}

	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while bindind data", err)
		return
	}
	keyData := []byte(p.Key)

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).GetKeyHistory(keyData, p.Offset, p.Limit, p.Desc)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while getting key history ", err)
		return
	}

	c.JSON(http.StatusOK, res)

}

func SetExpirableKey(c *gin.Context) {

	p := KeyValue{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data ", err)
		return
	}
	key := []byte(p.Key)
	value := []byte(p.Key)
	expiresAt := time.Now().Add(1 * time.Minute)

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).ExpirableKeySet(key, value, expiresAt)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while setting expirable key", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func GetVerifiedKeyData(c *gin.Context) {
	p := KeyValue{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}

	key := []byte(p.Key)

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).GetVerifiedKeyData(key)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while getting verified key data", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func DeleteKey(c *gin.Context) {
	p := struct {
		Key string `json:"key"`
	}{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}
	keys := [][]byte{[]byte(p.Key)}

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).DeleteKey(keys)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while deleting key", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func CreateDatabase(c *gin.Context) {
	p := struct {
		DbName     string                           `json:"dbName"`
		DbSettings *schema.DatabaseNullableSettings `json:"dbSettings"`
	}{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).CreateDatabase(p.DbName, p.DbSettings)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while creating database", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func CreateUser(c *gin.Context) {
	p := struct {
		Username    string `json:"userName"`
		Password    string `json:"password"`
		Dbname      string `json:"dbName"`
		Permissions int    `json:"permissions"` //Read=1,ReadWrite=2,AdminPermission=254
	}{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}

	err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).CreateUser(p.Username, p.Password, p.Dbname, p.Permissions)

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while creating new user", err)
		return
	}
	c.JSON(http.StatusOK, "User successfully created")

}

func ListUsers(c *gin.Context) {
	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).ListUsers()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while listing users", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func DeleteDatabase(c *gin.Context) {
	p := struct {
		DbName string `json:"dbName"`
	}{}
	if err := c.BindJSON(&p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while binding data", err)
		return
	}

	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).DeleteDatabase(p.DbName)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while deleting database", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func ListDatabases(c *gin.Context) {
	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).ListDatabases()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while listing databases", err)
		return
	}
	c.JSON(http.StatusOK, res)

}

func GetActiveKeys(c *gin.Context) {
	res, err := coreimmudb.GetImmuDAOWithHost(coreimmudb.DefaultHostName).GetActiveKeys()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		loggermdl.LogError("Error while getting active keys ", err)
		return
	}
	c.JSON(http.StatusOK, res)
}