package main

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

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 = $1", 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 ($1, $2, $3, $4)", 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 = $1, counter = counter + 1, expires_at = $2 WHERE key = $3 AND counter = $4", 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 = $1", 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
}