package repo

import (
	"encoding"
	"errors"

	e "galaxy/error"

	"galaxy/game/internal/repo/fs"
)

func NewStorageError(err error) error {
	return e.NewRepoError(err)
}

func NewGameNotInitializedError() error {
	return e.NewGameNotInitializedError()
}

func NewReportNotFoundError() error {
	return e.NewReportNotFoundError()
}

func NewStateError(msg string) error {
	return e.NewGameStateError(msg)
}

type Storage interface {
	Lock() (func() error, error)
	Exists(string) (bool, error)
	Write(string, encoding.BinaryMarshaler) error
	WriteSafe(string, encoding.BinaryMarshaler) error
	Read(string, encoding.BinaryUnmarshaler) error
	ReadSafe(string, encoding.BinaryUnmarshaler) error
}

type repo struct {
	s       Storage
	release func() error
}

func NewRepo(s Storage) (*repo, error) {
	r := &repo{
		s: s,
	}
	return r, nil
}

func NewFileRepo(path string) (*repo, error) {
	s, err := fs.NewFileStorage(path)
	if err != nil {
		return nil, err
	}
	return NewRepo(s)
}

func (r *repo) Lock() (err error) {
	if r.s == nil {
		return errors.New("storage is closed")
	}
	if r.release != nil {
		return errors.New("storage already locked")
	}
	r.release, err = r.s.Lock()
	if err != nil {
		r.close()
		return
	}
	return nil
}

func (r *repo) Release() (err error) {
	if r.s == nil {
		return errors.New("storage is closed")
	}
	if r.release == nil {
		return errors.New("storage was never locked")
	}
	err = r.release()
	if err != nil {
		return
	}
	r.close()
	return nil
}

func (r *repo) close() {
	r.release = nil
	r.s = nil
}
