package storage

import (
	"errors"

	"github.com/glebarez/sqlite"
	"gitlab.com/mstarongitlab/linstrom/storage/cache"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

// Storage is responsible for all database, cache and media related actions
// and serves as the lowest layer of the cake
type Storage struct {
	db    *gorm.DB
	cache *cache.Cache
}

var ErrInvalidData = errors.New("invalid data")

// Build a new storage using sqlite as database backend
func NewStorageSqlite(filePath string, cache *cache.Cache) (*Storage, error) {
	db, err := gorm.Open(sqlite.Open(filePath))
	if err != nil {
		return nil, err
	}
	return storageFromEmptyDb(db, cache)
}

func NewStoragePostgres(dbUrl string, cache *cache.Cache) (*Storage, error) {
	db, err := gorm.Open(postgres.Open(dbUrl))
	if err != nil {
		return nil, err
	}
	return storageFromEmptyDb(db, cache)
}

func storageFromEmptyDb(db *gorm.DB, cache *cache.Cache) (*Storage, error) {
	// AutoMigrate ensures the db is in a state where all the structs given here
	// have their own tables and relations setup. It also updates tables if necessary
	err := db.AutoMigrate(
		MediaMetadata{},
		Account{},
		RemoteServer{},
		Note{},
		Role{},
		PasskeySession{},
		InboundJob{},
		OutboundJob{},
	)
	if err != nil {
		return nil, err
	}

	// And finally, build the actual storage struct
	return &Storage{
		db:    db,
		cache: cache,
	}, nil
}
