package roles

import (
	"context"
	"fmt"
	"strings"

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

type rolePackageAuthorityReader interface {
	GetAuthority(context.Context, string, string, string) (content.Authority, bool, error)
}

type rolePackageNativeReadGate interface {
	NativeRolePackageRead(context.Context, string, string) (bool, error)
}

// AuthorityRoutedRolePackageContentStore is the Role-owned cutover seam. It
// chooses exactly one remote writer from persisted binding authority and never
// retries a failed native operation through AGS.
type AuthorityRoutedRolePackageContentStore struct {
	NativeEnabled bool
	Authority     rolePackageAuthorityReader
	Legacy        RolePackageContentStore
	Native        RolePackageContentStore
}

type rolePackageWriteRoute struct {
	native     bool
	generation uint64
}

func (s AuthorityRoutedRolePackageContentStore) StoreRolePackage(ctx context.Context, input StoreRolePackageInput) (StoreRolePackageOutput, error) {
	if s.Authority == nil || s.Legacy == nil {
		return StoreRolePackageOutput{}, rolePackageDependencyError(fmt.Errorf("role package authority is not configured"))
	}
	authority, found, err := s.Authority.GetAuthority(ctx, input.WorkspaceID, rolePackageContentDomain, input.RoleID)
	if err != nil {
		return StoreRolePackageOutput{}, rolePackageDependencyError(fmt.Errorf("resolve role package authority: %w", err))
	}
	state := content.AuthorityStateLegacyActive
	if found {
		state = strings.TrimSpace(authority.State)
	}
	route := rolePackageWriteRoute{native: state == content.AuthorityStateNativeActive}
	if found {
		route.generation = authority.Generation
	}
	store, err := s.writerForState(state)
	if err != nil {
		return StoreRolePackageOutput{}, err
	}
	if input.RequireNative && state != content.AuthorityStateNativeActive {
		return StoreRolePackageOutput{}, invalidRolePackageContract("native role package write is not active")
	}
	if err := s.ensureRolePackageWriteCurrent(ctx, input.WorkspaceID, input.RoleID, route); err != nil {
		return StoreRolePackageOutput{}, err
	}
	return store.StoreRolePackage(ctx, input)
}

func (s AuthorityRoutedRolePackageContentStore) ensureRolePackageWriteCurrent(ctx context.Context, workspaceID, roleID string, route rolePackageWriteRoute) error {
	authority, found, err := s.Authority.GetAuthority(ctx, workspaceID, rolePackageContentDomain, roleID)
	if err != nil {
		return rolePackageDependencyError(fmt.Errorf("recheck role package authority: %w", err))
	}
	// Implicit legacy rows preserve the established one-way cutover contract.
	if route.generation == 0 && !found {
		return nil
	}
	state := content.AuthorityStateLegacyActive
	if found {
		state = strings.TrimSpace(authority.State)
	}
	want := content.AuthorityStateLegacyActive
	if route.native {
		want = content.AuthorityStateNativeActive
	}
	if state != want || (found && authority.Generation != route.generation) {
		return rolePackageDependencyError(content.ErrAuthorityConflict)
	}
	return nil
}

func (s AuthorityRoutedRolePackageContentStore) ResolveRolePackage(ctx context.Context, input ResolveRolePackageInput) (string, error) {
	if s.Authority == nil || s.Legacy == nil {
		return "", rolePackageDependencyError(fmt.Errorf("role package authority is not configured"))
	}
	authority, found, err := s.Authority.GetAuthority(ctx, input.WorkspaceID, rolePackageContentDomain, input.RoleID)
	if err != nil {
		return "", rolePackageDependencyError(fmt.Errorf("resolve role package authority: %w", err))
	}
	store, err := s.readerForAuthority(authority, found)
	if err != nil {
		return "", err
	}
	return store.ResolveRolePackage(ctx, input)
}

// NativeRolePackageRead reports whether one exact Role binding currently
// reads from Content Store. Missing bindings remain implicit legacy and this
// check never materializes an authority row.
func (s AuthorityRoutedRolePackageContentStore) NativeRolePackageRead(ctx context.Context, workspaceID, roleID string) (bool, error) {
	if s.Authority == nil || s.Legacy == nil {
		return false, rolePackageDependencyError(fmt.Errorf("role package authority is not configured"))
	}
	authority, found, err := s.Authority.GetAuthority(ctx, workspaceID, rolePackageContentDomain, roleID)
	if err != nil {
		return false, rolePackageDependencyError(fmt.Errorf("resolve role package authority: %w", err))
	}
	return s.nativeReaderForAuthority(authority, found)
}

func (s AuthorityRoutedRolePackageContentStore) writerForState(state string) (RolePackageContentStore, error) {
	switch strings.TrimSpace(state) {
	case content.AuthorityStateLegacyActive:
		return s.Legacy, nil
	case content.AuthorityStateNativeActive:
		if !s.NativeEnabled || s.Native == nil {
			return nil, rolePackageDependencyError(fmt.Errorf("native role package content is unavailable"))
		}
		return s.Native, nil
	default:
		return nil, rolePackageDependencyError(fmt.Errorf("unknown role package authority state %q", state))
	}
}

func (s AuthorityRoutedRolePackageContentStore) readerForAuthority(authority content.Authority, found bool) (RolePackageContentStore, error) {
	native, err := s.nativeReaderForAuthority(authority, found)
	if err != nil {
		return nil, err
	}
	if native {
		return s.Native, nil
	}
	return s.Legacy, nil
}

func (s AuthorityRoutedRolePackageContentStore) nativeReaderForAuthority(authority content.Authority, found bool) (bool, error) {
	state := content.AuthorityStateLegacyActive
	if found {
		state = strings.TrimSpace(authority.State)
	}
	switch state {
	case content.AuthorityStateLegacyActive:
		return false, nil
	case content.AuthorityStateNativeActive:
		if !s.NativeEnabled || s.Native == nil {
			return false, rolePackageDependencyError(fmt.Errorf("native role package content is unavailable"))
		}
		return true, nil
	default:
		return false, rolePackageDependencyError(fmt.Errorf("unknown role package authority state %q", state))
	}
}

var _ RolePackageContentStore = AuthorityRoutedRolePackageContentStore{}
