package content

import (
	"context"
	"fmt"
	"strings"

	store "github.com/tidbcloud/AgentTeam/internal/store/content"
)

// AuthorityReader resolves one exact binding. Missing rows must be returned as
// legacy_active without materializing a default row.
type AuthorityReader interface {
	GetAuthority(context.Context, string, string, string) (store.Authority, bool, error)
}

// WorkspaceAccessVerifier resolves live workspace membership from the owning
// AgentTeam Core database. Request headers are routing claims, never access facts.
type WorkspaceAccessVerifier interface {
	HasWorkspaceAccess(context.Context, string, string) (bool, error)
}

// NativeBinding is one exact binding registered by its owning product domain.
type NativeBinding struct {
	WorkspaceID string
	Domain      string
	BindingKey  string
	// Policy is supplied by the domain that owns this exact binding. Binding
	// registration and native authority are rollout fences, not product ACLs.
	Policy ContentAccessPolicy
}

// NativeBindingPolicy gates the generic Content Store surface behind both the
// single global rollout switch and an exact registered native binding. It does
// not fall back to AGS when authority lookup or native storage fails.
type NativeBindingPolicy struct {
	enabled   bool
	authority AuthorityReader
	workspace WorkspaceAccessVerifier
	bindings  map[string]ContentAccessPolicy
}

func NewNativeBindingPolicy(enabled bool, authority AuthorityReader, workspace WorkspaceAccessVerifier, bindings []NativeBinding) (*NativeBindingPolicy, error) {
	if authority == nil || workspace == nil {
		return nil, fmt.Errorf("content authority reader and workspace access verifier are required")
	}
	policy := &NativeBindingPolicy{enabled: enabled, authority: authority, workspace: workspace, bindings: make(map[string]ContentAccessPolicy, len(bindings))}
	for _, binding := range bindings {
		workspaceID := strings.TrimSpace(binding.WorkspaceID)
		domain := strings.TrimSpace(binding.Domain)
		key := strings.TrimSpace(binding.BindingKey)
		if workspaceID == "" || domain == "" || key == "" || domain == store.AuthorityDomainDefaultBinding || key == store.AuthorityDomainDefaultBinding || binding.Policy == nil {
			return nil, fmt.Errorf("native content binding must be exact")
		}
		scopeKey := workspaceID + "\x00" + domain + "\x00" + key
		policy.bindings[scopeKey] = binding.Policy
	}
	return policy, nil
}

func (policy *NativeBindingPolicy) AuthorizeContentAccess(ctx context.Context, request AccessRequest) error {
	if policy == nil || !policy.enabled || policy.authority == nil || policy.workspace == nil {
		return ErrAccessDenied
	}
	workspaceID := strings.TrimSpace(request.WorkspaceID)
	domain := strings.TrimSpace(request.Domain)
	bindingKey := strings.TrimSpace(request.BindingKey)
	if workspaceID == "" || domain == "" || bindingKey == "" || strings.TrimSpace(request.Principal.Subject) == "" ||
		strings.TrimSpace(request.Principal.WorkspaceID) != workspaceID {
		return ErrAccessDenied
	}
	if principalDomain := strings.TrimSpace(request.Principal.Domain); principalDomain != "" && principalDomain != domain {
		return ErrAccessDenied
	}
	if principalBinding := strings.TrimSpace(request.Principal.BindingKey); principalBinding != "" && principalBinding != bindingKey {
		return ErrAccessDenied
	}
	scopeKey := workspaceID + "\x00" + domain + "\x00" + bindingKey
	domainPolicy, ok := policy.bindings[scopeKey]
	if !ok || domainPolicy == nil || !knownAccessAction(request.Action) {
		return ErrAccessDenied
	}
	allowed, err := policy.workspace.HasWorkspaceAccess(ctx, workspaceID, request.Principal.Subject)
	if err != nil {
		return fmt.Errorf("verify content workspace access: %w", err)
	}
	if !allowed {
		return ErrAccessDenied
	}
	authority, found, err := policy.authority.GetAuthority(ctx, workspaceID, domain, bindingKey)
	if err != nil {
		return fmt.Errorf("resolve native content authority: %w", err)
	}
	if !found || authority.State != store.AuthorityStateNativeActive {
		return ErrAccessDenied
	}
	if request.Generation != 0 && request.Generation != authority.Generation {
		return ErrAccessDenied
	}
	if principalGeneration := request.Principal.Generation; principalGeneration != 0 && principalGeneration != authority.Generation {
		return ErrAccessDenied
	}
	if err := domainPolicy.AuthorizeContentAccess(ctx, request); err != nil {
		return err
	}
	return nil
}
