package store

import (
	"context"
	"strings"

	"github.com/grafana/grafana/pkg/infra/filestorage"
	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/services/user"
)

const (
	ActionFilesRead   = "files:read"
	ActionFilesWrite  = "files:write"
	ActionFilesDelete = "files:delete"
)

var (
	denyAllPathFilter  = filestorage.NewDenyAllPathFilter()
	allowAllPathFilter = filestorage.NewAllowAllPathFilter()
)

func isValidAction(action string) bool {
	return action == ActionFilesRead || action == ActionFilesWrite || action == ActionFilesDelete
}

type storageAuthService interface {
	newGuardian(ctx context.Context, user *user.SignedInUser, prefix string) fileGuardian
}

type fileGuardian interface {
	canView(path string) bool
	canWrite(path string) bool
	canDelete(path string) bool
	can(action string, path string) bool

	getPathFilter(action string) filestorage.PathFilter
}

type pathFilterFileGuardian struct {
	ctx                context.Context
	user               *user.SignedInUser
	prefix             string
	pathFilterByAction map[string]filestorage.PathFilter
	log                log.Logger
}

func (a *pathFilterFileGuardian) getPathFilter(action string) filestorage.PathFilter {
	if !isValidAction(action) {
		a.log.Warn("Unsupported action", "action", action)
		return denyAllPathFilter
	}

	if filter, ok := a.pathFilterByAction[action]; ok {
		return filter
	}

	return denyAllPathFilter
}

func (a *pathFilterFileGuardian) canWrite(path string) bool {
	return a.can(ActionFilesWrite, path)
}

func (a *pathFilterFileGuardian) canView(path string) bool {
	return a.can(ActionFilesRead, path)
}

func (a *pathFilterFileGuardian) canDelete(path string) bool {
	return a.can(ActionFilesDelete, path)
}

func (a *pathFilterFileGuardian) can(action string, path string) bool {
	if path == a.prefix {
		path = filestorage.Delimiter
	} else {
		path = strings.TrimPrefix(path, a.prefix)
	}
	allow := false

	if !isValidAction(action) {
		a.log.Warn("Unsupported action", "action", action, "path", path)
		return false
	}

	pathFilter, ok := a.pathFilterByAction[action]

	if !ok {
		a.log.Warn("Missing path filter", "action", action, "path", path)
		return false
	}

	allow = pathFilter.IsAllowed(path)
	if !allow {
		a.log.Warn("Denying", "action", action, "path", path)
	}
	return allow
}

type denyAllFileGuardian struct {
}

func (d denyAllFileGuardian) canView(path string) bool {
	return d.can(ActionFilesRead, path)
}

func (d denyAllFileGuardian) canWrite(path string) bool {
	return d.can(ActionFilesWrite, path)
}

func (d denyAllFileGuardian) canDelete(path string) bool {
	return d.can(ActionFilesDelete, path)
}

func (d denyAllFileGuardian) can(action string, path string) bool {
	return false
}

func (d denyAllFileGuardian) getPathFilter(action string) filestorage.PathFilter {
	return denyAllPathFilter
}
