package store

import (
	"errors"
	"path/filepath"
	"sync"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/philippgille/gokv"
	"github.com/philippgille/gokv/badgerdb"
	"github.com/philippgille/gokv/encoding"
)

func NewExcludedFiles(metaDir string, storage TorrentFileDeleter) (*ExlcudedFiles, error) {
	excludedFilesStore, err := badgerdb.NewStore(badgerdb.Options{
		Dir:   filepath.Join(metaDir, "excluded-files"),
		Codec: encoding.JSON,
	})

	if err != nil {
		return nil, err
	}

	r := &ExlcudedFiles{
		excludedFiles: excludedFilesStore,
		storage:       storage,
	}

	return r, nil
}

type ExlcudedFiles struct {
	m             sync.RWMutex
	excludedFiles gokv.Store
	storage       TorrentFileDeleter
}

var ErrNotFound = errors.New("not found")

type TorrentFileDeleter interface {
	DeleteFile(file *torrent.File) error
}

func (r *ExlcudedFiles) ExcludeFile(file *torrent.File) error {
	r.m.Lock()
	defer r.m.Unlock()

	hash := file.Torrent().InfoHash()
	var excludedFiles []string
	found, err := r.excludedFiles.Get(hash.AsString(), &excludedFiles)
	if err != nil {
		return err
	}
	if !found {
		excludedFiles = []string{}
	}
	excludedFiles = unique(append(excludedFiles, file.Path()))

	err = r.storage.DeleteFile(file)
	if err != nil {
		return err
	}

	return r.excludedFiles.Set(hash.AsString(), excludedFiles)
}

func (r *ExlcudedFiles) ExcludedFiles(hash metainfo.Hash) ([]string, error) {
	r.m.Lock()
	defer r.m.Unlock()

	var excludedFiles []string
	found, err := r.excludedFiles.Get(hash.AsString(), &excludedFiles)
	if err != nil {
		return nil, err
	}
	if !found {
		return nil, nil
	}

	return excludedFiles, nil
}

func unique[C comparable](intSlice []C) []C {
	keys := make(map[C]bool)
	list := []C{}
	for _, entry := range intSlice {
		if _, value := keys[entry]; !value {
			keys[entry] = true
			list = append(list, entry)
		}
	}
	return list
}