package platform

import (
	"context"
	"errors"
	"fmt"
)

type UserType string
type ResourceType string

// available user resource types.
const (
	Owner                 UserType     = "owner"
	Member                UserType     = "member"
	DashboardResourceType ResourceType = "dashboard"
	BucketResourceType    ResourceType = "bucket"
	TaskResourceType      ResourceType = "task"
	OrgResourceType       ResourceType = "org"
	ViewResourceType      ResourceType = "view"
	TelegrafResourceType  ResourceType = "telegraf"
	TokenResourceType     ResourceType = "token"
	UserResourceType      ResourceType = "user"
)

// UserResourceMappingService maps the relationships between users and resources
type UserResourceMappingService interface {
	// FindUserResourceMappings returns a list of UserResourceMappings that match filter and the total count of matching mappings.
	FindUserResourceMappings(ctx context.Context, filter UserResourceMappingFilter, opt ...FindOptions) ([]*UserResourceMapping, int, error)

	// CreateUserResourceMapping creates a user resource mapping
	CreateUserResourceMapping(ctx context.Context, m *UserResourceMapping) error

	// DeleteUserResourceMapping deletes a user resource mapping
	DeleteUserResourceMapping(ctx context.Context, resourceID ID, userID ID) error
}

// UserResourceMapping represents a mapping of a resource to its user
type UserResourceMapping struct {
	ResourceID   ID           `json:"resource_id"`
	ResourceType ResourceType `json:"resource_type"`
	UserID       ID           `json:"user_id"`
	UserType     UserType     `json:"user_type"`
}

// Validate reports any validation errors for the mapping.
func (m UserResourceMapping) Validate() error {
	if !m.ResourceID.Valid() {
		return errors.New("resourceID is required")
	}
	if !m.UserID.Valid() {
		return errors.New("userID is required")
	}
	if m.UserType != Owner && m.UserType != Member {
		return errors.New("a valid user type is required")
	}
	switch m.ResourceType {
	case DashboardResourceType, BucketResourceType, TaskResourceType, OrgResourceType, ViewResourceType, TelegrafResourceType:
	default:
		return errors.New("a valid resource type is required")
	}
	return nil
}

// UserResourceMapping represents a set of filters that restrict the returned results.
type UserResourceMappingFilter struct {
	ResourceID   ID
	ResourceType ResourceType
	UserID       ID
	UserType     UserType
}

var ownerActions = []action{WriteAction, CreateAction, DeleteAction}
var memberActions = []action{ReadAction}

// ToPermission converts a user resource mapping into a set of permissions.
func (m *UserResourceMapping) ToPermissions() []Permission {
	// TODO(desa): we'll have to do something more fine-grained eventually
	// but this should be good enough for now.
	ps := []Permission{}
	r := resource(fmt.Sprintf("%s/%s", m.ResourceType, m.ResourceID))
	if m.UserType == Owner {
		for _, a := range ownerActions {
			p := Permission{
				Resource: r,
				Action:   a,
			}
			ps = append(ps, p)
		}
	}

	for _, a := range memberActions {
		p := Permission{
			Resource: r,
			Action:   a,
		}
		ps = append(ps, p)
	}

	return ps
}