package state

import (
	"encoding/json"
	"errors"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

type DiskStore struct {
	dataPath string
	mu       sync.Mutex
}

func NewDiskStore(dataPath string) (*DiskStore, error) {
	if err := os.MkdirAll(dataPath, 0o755); err != nil {
		return nil, err
	}

	return &DiskStore{dataPath: dataPath}, nil
}

func (ds *DiskStore) Save(room *Room) error {
	ds.mu.Lock()
	defer ds.mu.Unlock()

	persisted := room.toPersisted()
	payload, err := json.MarshalIndent(persisted, "", "  ")
	if err != nil {
		return err
	}

	finalPath := filepath.Join(ds.dataPath, room.ID+".json")
	tmpPath := finalPath + ".tmp"
	if err := os.WriteFile(tmpPath, payload, 0o600); err != nil {
		return err
	}

	return os.Rename(tmpPath, finalPath)
}

func (ds *DiskStore) LoadAll() ([]persistedRoom, error) {
	ds.mu.Lock()
	defer ds.mu.Unlock()

	entries, err := os.ReadDir(ds.dataPath)
	if err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			return nil, nil
		}
		return nil, err
	}

	rooms := make([]persistedRoom, 0)
	for _, entry := range entries {
		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
			continue
		}

		fullPath := filepath.Join(ds.dataPath, entry.Name())
		payload, readErr := os.ReadFile(fullPath)
		if readErr != nil {
			continue
		}

		var room persistedRoom
		if unmarshalErr := json.Unmarshal(payload, &room); unmarshalErr != nil {
			continue
		}
		rooms = append(rooms, room)
	}

	return rooms, nil
}

func (ds *DiskStore) Delete(roomID string) error {
	ds.mu.Lock()
	defer ds.mu.Unlock()

	finalPath := filepath.Join(ds.dataPath, roomID+".json")
	if err := os.Remove(finalPath); err != nil && !errors.Is(err, fs.ErrNotExist) {
		return err
	}

	tmpPath := finalPath + ".tmp"
	if err := os.Remove(tmpPath); err != nil && !errors.Is(err, fs.ErrNotExist) {
		return err
	}

	return nil
}
