package skills

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/service/content"
	store "github.com/tidbcloud/AgentTeam/internal/store/resource/skills"
	"gorm.io/gorm"
)

type SkillContentAccessPolicy struct {
	workspace content.WorkspaceAccessVerifier
	skills    skillContentAccessStore
}

type skillContentAccessStore interface {
	GetSkill(context.Context, string, string) (store.Skill, error)
	GetSkillGrant(context.Context, string, string, string, string) (store.SkillGrant, error)
}

func NewSkillContentAccessPolicy(workspace content.WorkspaceAccessVerifier, skills skillContentAccessStore) (*SkillContentAccessPolicy, error) {
	if workspace == nil || skills == nil {
		return nil, fmt.Errorf("skill content workspace verifier and skill store are required")
	}
	return &SkillContentAccessPolicy{workspace: workspace, skills: skills}, nil
}

func (policy *SkillContentAccessPolicy) AuthorizeContentAccess(ctx context.Context, request content.AccessRequest) error {
	if policy == nil || strings.TrimSpace(request.Domain) != skillContentDomain {
		return content.ErrAccessDenied
	}
	if err := (content.ScopedAccessPolicy{}).AuthorizeContentAccess(ctx, request); err != nil {
		return err
	}
	workspaceID := strings.TrimSpace(request.WorkspaceID)
	skillID := strings.TrimSpace(request.BindingKey)
	subject := strings.TrimSpace(request.Principal.Subject)
	allowed, err := policy.workspace.HasWorkspaceAccess(ctx, workspaceID, subject)
	if err != nil {
		return fmt.Errorf("verify skill content workspace access: %w", err)
	}
	if !allowed {
		return content.ErrAccessDenied
	}
	skill, err := policy.skills.GetSkill(ctx, workspaceID, skillID)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return content.ErrAccessDenied
	}
	if err != nil {
		return fmt.Errorf("load skill content owner: %w", err)
	}
	grant := ""
	if strings.TrimSpace(skill.CreatedBy) == subject {
		grant = "owner"
	} else if row, grantErr := policy.skills.GetSkillGrant(ctx, workspaceID, skillID, "user", subject); grantErr == nil {
		grant = strings.ToLower(strings.TrimSpace(row.Role))
	} else if !errors.Is(grantErr, gorm.ErrRecordNotFound) {
		return fmt.Errorf("load skill content grant: %w", grantErr)
	} else if skill.VisibilityScope == SkillVisibilityScopeWorkspace {
		grant = "view"
	}
	if !skillContentGrantAllows(grant, request.Action) {
		return content.ErrAccessDenied
	}
	return nil
}

func skillContentGrantAllows(grant string, action content.AccessAction) bool {
	grant = strings.ToLower(strings.TrimSpace(grant))
	read := grant == "owner" || grant == "manage" || grant == "edit" || grant == "view"
	manage := grant == "owner" || grant == "manage" || grant == "edit"
	switch action {
	case content.AccessReadMetadata, content.AccessReadPayload,
		content.AccessReadHistory, content.AccessCompare,
		content.AccessReadArchive:
		return read
	case content.AccessCreateLineage, content.AccessPublish,
		content.AccessManagePin:
		return manage
	default:
		return false
	}
}

var _ content.ContentAccessPolicy = (*SkillContentAccessPolicy)(nil)
