package actor

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/gogo/protobuf/proto"
)

type DiskStorage[V any] struct {
	*StateStorage
	path string
}

type LogStorage[V any] interface {
	LoadEvents(id uint64, grain Grain[V]) error
	AppendEvent(id uint64, msg proto.Message) error
}

func NewDiskStorage[V any](path string, registry MutationRegistry) LogStorage[V] {
	return &DiskStorage[V]{
		StateStorage: NewState(registry),
		path:         path,
	}
}

func (s *DiskStorage[V]) logPath(id uint64) string {
	return filepath.Join(s.path, fmt.Sprintf("%d.events.log", id))
}

func (s *DiskStorage[V]) LoadEvents(id uint64, grain Grain[V]) error {
	path := s.logPath(id)
	if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
		// No log -> nothing to replay
		return nil
	}

	fh, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("open replay file: %w", err)
	}
	defer fh.Close()
	return s.Load(fh, func(msg proto.Message) {
		s.registry.Apply(grain, msg)
	})
}

func (s *DiskStorage[V]) AppendEvent(id uint64, msg proto.Message) error {
	path := s.logPath(id)
	fh, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Printf("failed to open event log file: %v", err)
		return err
	}
	defer fh.Close()

	return s.Append(fh, msg)

}
