package authz

import (
	"context"
	"errors"
)

type contextKey string

const principalKey contextKey = "principal"

const (
	RoleAdmin = "admin"
	RoleUser  = "user"
)

const (
	VisibilityPrivate         = "private"
	VisibilityWorkspaceShared = "workspace_shared"
	VisibilityGlobalShared    = "global_shared"
)

var (
	ErrUnauthenticated = errors.New("authentication required")
	ErrForbidden       = errors.New("permission denied")
)

type Principal struct {
	UserID            string
	Username          string
	Role              string
	WorkspaceID       string
	WorkspaceName     string
	Namespace         string
	DefaultClusterID  string
	QuotaCPU          string
	QuotaMemory       string
	QuotaGPU          string
	QuotaGPUMem       string
	Permissions       []string
	PermissionVersion int
}

func WithPrincipal(ctx context.Context, principal *Principal) context.Context {
	return context.WithValue(ctx, principalKey, principal)
}

func PrincipalFromContext(ctx context.Context) (*Principal, bool) {
	principal, ok := ctx.Value(principalKey).(*Principal)
	return principal, ok && principal != nil
}

func RequirePrincipal(ctx context.Context) (*Principal, error) {
	principal, ok := PrincipalFromContext(ctx)
	if !ok {
		return nil, ErrUnauthenticated
	}
	return principal, nil
}

func (p *Principal) IsAdmin() bool {
	return p != nil && p.Role == RoleAdmin
}

func CanReadResource(p *Principal, workspaceID, ownerID, visibility string) bool {
	if p == nil {
		return false
	}
	if p.IsAdmin() {
		return true
	}
	switch visibility {
	case VisibilityGlobalShared:
		return true
	case VisibilityWorkspaceShared:
		return workspaceID != "" && workspaceID == p.WorkspaceID
	default:
		return ownerID != "" && ownerID == p.UserID
	}
}

func CanWriteResource(p *Principal, workspaceID, ownerID, visibility string) bool {
	if p == nil {
		return false
	}
	if p.IsAdmin() {
		return true
	}
	if visibility == VisibilityGlobalShared {
		return false
	}
	return workspaceID != "" && workspaceID == p.WorkspaceID && ownerID != "" && ownerID == p.UserID
}

func NormalizeVisibility(role, requested string) string {
	switch requested {
	case VisibilityWorkspaceShared:
		if role == RoleAdmin {
			return requested
		}
		return VisibilityPrivate
	case VisibilityGlobalShared:
		if role == RoleAdmin {
			return requested
		}
		return VisibilityPrivate
	case VisibilityPrivate:
		return requested
	default:
		return VisibilityPrivate
	}
}

func PermissionsForRole(role string) []string {
	if role == RoleAdmin {
		return []string{
			"*",
			"home:view",
			"workspaces:manage",
			"users:manage",
			"configuration:clusters:manage",
			"configuration:registries:manage",
			"artifact:registries:view",
			"artifact:instances:manage",
			"monitoring:clusters:view",
			"clusters:manage:any",
			"registries:manage:any",
			"instances:manage:any",
			"kubeconfig:issue:any",
		}
	}
	return []string{
		"home:view",
		"configuration:clusters:manage_own",
		"configuration:registries:manage_own",
		"artifact:registries:view",
		"artifact:instances:manage_own",
		"monitoring:clusters:view",
		"clusters:manage:own",
		"registries:manage:own",
		"instances:manage:own",
		"kubeconfig:issue:own",
	}
}
