package share

import (
	"time"

	fberrors "github.com/filebrowser/filebrowser/v2/errors"
)

// StorageBackend is the interface to implement for a share storage.
type StorageBackend interface {
	All() ([]*Link, error)
	FindByUserID(id uint) ([]*Link, error)
	GetByHash(hash string) (*Link, error)
	GetPermanent(path string, id uint) (*Link, error)
	Gets(path string, id uint) ([]*Link, error)
	Save(s *Link) error
	Delete(hash string) error
	DeleteWithPathPrefix(path string, userID uint) error
}

// Storage is a storage.
type Storage struct {
	back StorageBackend
}

// NewStorage creates a share links storage from a backend.
func NewStorage(back StorageBackend) *Storage {
	return &Storage{back: back}
}

// All wraps a StorageBackend.All.
func (s *Storage) All() ([]*Link, error) {
	links, err := s.back.All()

	if err != nil {
		return nil, err
	}

	for i, link := range links {
		if link.Expire != 0 && link.Expire <= time.Now().Unix() {
			if err := s.Delete(link.Hash); err != nil {
				return nil, err
			}
			links = append(links[:i], links[i+1:]...)
		}
	}

	return links, nil
}

// FindByUserID wraps a StorageBackend.FindByUserID.
func (s *Storage) FindByUserID(id uint) ([]*Link, error) {
	links, err := s.back.FindByUserID(id)

	if err != nil {
		return nil, err
	}

	for i, link := range links {
		if link.Expire != 0 && link.Expire <= time.Now().Unix() {
			if err := s.Delete(link.Hash); err != nil {
				return nil, err
			}
			links = append(links[:i], links[i+1:]...)
		}
	}

	return links, nil
}

// GetByHash wraps a StorageBackend.GetByHash.
func (s *Storage) GetByHash(hash string) (*Link, error) {
	link, err := s.back.GetByHash(hash)
	if err != nil {
		return nil, err
	}

	if link.Expire != 0 && link.Expire <= time.Now().Unix() {
		if err := s.Delete(link.Hash); err != nil {
			return nil, err
		}
		return nil, fberrors.ErrNotExist
	}

	return link, nil
}

// GetPermanent wraps a StorageBackend.GetPermanent
func (s *Storage) GetPermanent(path string, id uint) (*Link, error) {
	return s.back.GetPermanent(path, id)
}

// Gets wraps a StorageBackend.Gets
func (s *Storage) Gets(path string, id uint) ([]*Link, error) {
	links, err := s.back.Gets(path, id)

	if err != nil {
		return nil, err
	}

	for i, link := range links {
		if link.Expire != 0 && link.Expire <= time.Now().Unix() {
			if err := s.Delete(link.Hash); err != nil {
				return nil, err
			}
			links = append(links[:i], links[i+1:]...)
		}
	}

	return links, nil
}

// GetsByPath returns all non-expired links for a path.
func (s *Storage) GetsByPath(path string) ([]*Link, error) {
	links, err := s.All()
	if err != nil {
		return nil, err
	}

	filtered := make([]*Link, 0, len(links))
	for _, link := range links {
		if link.Path == path {
			filtered = append(filtered, link)
		}
	}

	return filtered, nil
}

// Save wraps a StorageBackend.Save
func (s *Storage) Save(l *Link) error {
	return s.back.Save(l)
}

// Delete wraps a StorageBackend.Delete
func (s *Storage) Delete(hash string) error {
	return s.back.Delete(hash)
}

func (s *Storage) DeleteWithPathPrefix(path string, userID uint) error {
	return s.back.DeleteWithPathPrefix(path, userID)
}
