package storage

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
)

var _ Storage = (*OSStorage)(nil)

type OSStorage struct {
	root string
}

func NewOSStorage() (Storage, error) {
	urd, err := os.UserHomeDir()
	if err != nil {
		return nil, fmt.Errorf("不能获取默认的根目录 : %w", err)
	}
	return NewOSStorageRooted(urd)
}

func NewOSStorageRooted(root string) (Storage, error) {
	if !filepath.IsAbs(root) {
		return nil, fmt.Errorf("root必须是绝对路径:%s", root)
	}

	storageRoot := filepath.Join(root, ".df")

	s := &OSStorage{root: storageRoot}

	mighted, err := s.migrateDeprecatedRootStorage()
	if mighted {
		if err != nil {
			return nil, fmt.Errorf("找到不推荐使用的存储，但无法移动到新位置:%w", err)
		}
		return s, nil
	}
	err = s.mkdirIfNotExists(storageRootPath(s))
	return s, err
}

func (s *OSStorage) Root() string {
	return s.root
}

func (s *OSStorage) LoadConfig() (*Config, error) {
	configFile := configPath(s)
	f, err := os.Open(configFile)
	if os.IsNotExist(err) {
		return NewDefaultConfig(), nil
	}

	if err != nil {
		return NewDefaultConfig(), fmt.Errorf("没有读到 URI:%w", err)
	}

	defer f.Close()

	config := &Config{}
	err = json.NewDecoder(f).Decode(config)
	if err != nil {
		return NewDefaultConfig(), err
	}
	return config, nil
}

func (s *OSStorage) StoreConfig(config *Config) error {
	configFile := configPath(s)
	w, err := s.createFile(configFile)
	if err != nil {
		return err
	}

	defer w.Close()

	return json.NewEncoder(w).Encode(config)
}

func (s *OSStorage) createFile(name string) (*os.File, error) {
	return os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
}

func (s *OSStorage) isExist(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}

func (s *OSStorage) mkdirIfNotExists(dir string) error {
	if s.isExist(dir) {
		return nil
	}
	return os.MkdirAll(dir, 0700)
}

// 合并弃用的根目录
func (s *OSStorage) migrateDeprecatedRootStorage() (bool, error) {
	oldRoot, err := os.UserConfigDir()
	if err != nil {
		return false, err
	}

	src := filepath.Join(oldRoot, "fyne", ID, "vvv")
	_, err = os.Stat(src)
	if os.IsNotExist(err) {
		return false, err
	}

	dest := storageRootPath(s)
	err = os.Rename(src, dest)
	if err != nil {
		return false, err
	}

	return true, nil
}
