package automation

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

	"github.com/tidbcloud/AgentTeam/internal/identity/trusted"
	contentstoreservice "github.com/tidbcloud/AgentTeam/internal/service/content"
	contentstorestore "github.com/tidbcloud/AgentTeam/internal/store/content"
	"github.com/tidbcloud/AgentTeam/internal/store/resource/repos"
	"gorm.io/gorm"
)

type automationContentRepositoryStore interface {
	Get(context.Context, string, string, string, string) (repos.ResourceRepository, error)
}

// AutomationContentAccessPolicy is the live product boundary for native
// Automation content. It revalidates workspace membership and the exact
// repository binding on every Content Store operation.
type AutomationContentAccessPolicy struct {
	workspace    contentstoreservice.WorkspaceAccessVerifier
	repositories automationContentRepositoryStore
	authority    AutomationContentAuthority
}

func NewAutomationContentAccessPolicy(workspace contentstoreservice.WorkspaceAccessVerifier, repositories automationContentRepositoryStore, authority AutomationContentAuthority) (*AutomationContentAccessPolicy, error) {
	if workspace == nil || repositories == nil || authority == nil {
		return nil, fmt.Errorf("automation content access dependencies are required")
	}
	return &AutomationContentAccessPolicy{workspace: workspace, repositories: repositories, authority: authority}, nil
}

func (policy *AutomationContentAccessPolicy) AuthorizeContentAccess(ctx context.Context, request contentstoreservice.AccessRequest) error {
	if policy == nil || strings.TrimSpace(request.Domain) != "automation" {
		return contentstoreservice.ErrAccessDenied
	}
	if err := (contentstoreservice.ScopedAccessPolicy{}).AuthorizeContentAccess(ctx, request); err != nil {
		return err
	}
	workspaceID := strings.TrimSpace(request.WorkspaceID)
	subject := strings.TrimSpace(request.Principal.Subject)
	allowed, err := policy.workspace.HasWorkspaceAccess(ctx, workspaceID, subject)
	if err != nil {
		return fmt.Errorf("verify automation content workspace access: %w", err)
	}
	if !allowed {
		return contentstoreservice.ErrAccessDenied
	}
	bindingKey := strings.TrimSpace(request.BindingKey)
	owner, repository, ok := strings.Cut(bindingKey, "/")
	if !ok || strings.TrimSpace(owner) == "" || strings.TrimSpace(repository) == "" {
		return contentstoreservice.ErrAccessDenied
	}
	resourceID := repos.AutomationRepositoryResourceID(bindingKey)
	repo, err := policy.repositories.Get(ctx, workspaceID, repos.ResourceTypeAutomationRepository, resourceID, repos.RepoRolePrimary)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return contentstoreservice.ErrAccessDenied
	}
	if err != nil {
		return fmt.Errorf("load automation content repository binding: %w", err)
	}
	if !strings.EqualFold(strings.TrimSpace(repo.RepoFullName), bindingKey) {
		return contentstoreservice.ErrAccessDenied
	}
	authority, found, err := policy.authority.GetAuthority(ctx, workspaceID, "automation", bindingKey)
	if err != nil {
		return fmt.Errorf("resolve automation content authority: %w", err)
	}
	if found {
		switch authority.State {
		case contentstorestore.AuthorityStateLegacyActive:
			if authority.Generation != 0 {
				return contentstoreservice.ErrAccessDenied
			}
		case contentstorestore.AuthorityStateNativeActive:
			if authority.Generation == 0 || (request.Generation != 0 && request.Generation != authority.Generation) {
				return contentstoreservice.ErrAccessDenied
			}
		default:
			return contentstoreservice.ErrAccessDenied
		}
	}
	return nil
}

func WithAutomationContentAccess(ctx context.Context, workspaceID, bindingKey, contentID string, generation uint64, subject string, actions ...contentstoreservice.AccessAction) context.Context {
	subject = strings.TrimSpace(subject)
	if subject == "" {
		identity, _ := trusted.IdentityFromContext(ctx)
		subject = strings.TrimSpace(identity.Subject)
	}
	return contentstoreservice.WithAccessPrincipal(ctx, contentstoreservice.AccessPrincipal{
		Subject: subject, WorkspaceID: strings.TrimSpace(workspaceID), Domain: "automation",
		BindingKey: strings.TrimSpace(bindingKey), ContentID: strings.TrimSpace(contentID),
		Generation: generation, Actions: append([]contentstoreservice.AccessAction(nil), actions...),
	})
}

var _ contentstoreservice.ContentAccessPolicy = (*AutomationContentAccessPolicy)(nil)
