package main

/*
CREATE TABLE main_storage (
	key TEXT NOT NULL PRIMARY KEY,
	value BLOB NOT NULL,
	counter INTEGER NOT NULL,
	expires_at INTEGER NOT NULL
) STRICT;
*/

import (
	"database/sql"
	"errors"
	"fmt"
	"time"

	"github.com/faroedev/faroe"
)

type mainStorageStruct struct {
	db *sql.DB
}

func newMainStorage(db *sql.DB) *mainStorageStruct {
	storage := &mainStorageStruct{
		db: db,
	}
	return storage
}

func (mainStorage *mainStorageStruct) Get(key string) ([]byte, int32, error) {
	row := mainStorage.db.QueryRow("SELECT value, counter FROM main_storage WHERE key = ?", key)

	var value []byte
    var counter int32
	err := row.Scan(&value, &counter)
	if err != nil && errors.Is(err, sql.ErrNoRows) {
		return nil, 0, faroe.ErrMainStorageEntryNotFound
	}
	if err != nil {
		return nil, 0, fmt.Errorf("failed to run query: %s", err.Error())
	}

	return value, counter, nil
}

func (mainStorage *mainStorageStruct) Set(key string, value []byte, expiresAt time.Time) error {
	_, err := mainStorage.db.Exec("INSERT INTO main_storage (key, value, counter, expires_at) VALUES (?, ?, ?, ?)", key, value, 0, expiresAt.Unix())
	if err != nil {
		return fmt.Errorf("failed to run query: %s", err.Error())
	}

	return nil
}

func (mainStorage *mainStorageStruct) Update(key string, value []byte, expiresAt time.Time, counter int32) error {
	result, err := mainStorage.db.Exec("UPDATE main_storage SET value = ?, counter = counter + 1, expires_at = ? WHERE key = ? AND counter = ?", value, expiresAt, key, counter)
	if err != nil {
		return fmt.Errorf("failed to run query: %s", err.Error())
	}
	rowsAffected, _ := result.RowsAffected()
	if rowsAffected < 1 {
		return faroe.ErrMainStorageEntryNotFound
	}

	return nil
}

func (mainStorage *mainStorageStruct) Delete(key string) error {
	result, err := mainStorage.db.Exec("DELETE FROM main_storage WHERE key = ?", key)
	if err != nil {
		return fmt.Errorf("failed to run query: %s", err.Error())
	}
	rowsAffected, _ := result.RowsAffected()
	if rowsAffected < 1 {
		return faroe.ErrMainStorageEntryNotFound
	}

	return nil
}