package persistencewiring

import (
	"context"
	"errors"
	"fmt"

	database "github.com/nexuscrm/db"
	"github.com/nexuscrm/persistence"
)

// EnsurePermissionStateInitialized establishes the durable authority singleton
// required by every protected permission-state writer. Physical DDL callers
// must create the authority table before calling this helper; managed-schema
// and higher-level control-plane entry points share this one initializer.
func EnsurePermissionStateInitialized(
	ctx context.Context,
	connection *database.TiDBConnection,
) error {
	if connection == nil || connection.DB() == nil {
		return errors.New("controlplane persistence wiring: database connection is required")
	}
	repository, err := persistence.NewPermissionStateRepository(connection.DB())
	if err != nil {
		return fmt.Errorf("initialize permission state: construct repository: %w", err)
	}
	if _, err := repository.EnsureInitialized(ctx); err != nil {
		return fmt.Errorf("initialize permission state: %w", err)
	}
	return nil
}

// PermissionStateDirectWriters owns the one transaction manager, authority,
// and direct-writer coordinator used by a control-plane composition. Callers
// bind every repository that can mutate a permission-state surface to this
// same value rather than constructing repository-local authorities.
type PermissionStateDirectWriters struct {
	Transactions *persistence.TransactionManager
	Authority    *persistence.PermissionStateAuthority
	Coordinator  *persistence.PermissionStateDirectWriterCoordinator
}

func NewPermissionStateDirectWriters(
	connection *database.TiDBConnection,
) (*PermissionStateDirectWriters, error) {
	if connection == nil || connection.DB() == nil {
		return nil, errors.New("controlplane persistence wiring: database connection is required")
	}
	return NewPermissionStateDirectWritersForTransactionManager(
		persistence.NewTransactionManager(connection),
	)
}

// NewPermissionStateDirectWritersForTransactionManager binds direct writers
// to an existing composition's exact transaction owner. A service graph must
// call this variant: creating a second TransactionManager over the same
// *sql.DB would split transaction identity and fail closed inside nested pack
// writes.
func NewPermissionStateDirectWritersForTransactionManager(
	transactions *persistence.TransactionManager,
) (*PermissionStateDirectWriters, error) {
	if transactions == nil {
		return nil, errors.New("controlplane persistence wiring: transaction manager is required")
	}
	authority, err := persistence.NewPermissionStateAuthority(transactions)
	if err != nil {
		return nil, fmt.Errorf("controlplane persistence wiring: permission state authority: %w", err)
	}
	coordinator, err := persistence.NewPermissionStateDirectWriterCoordinator(
		transactions,
		authority,
	)
	if err != nil {
		return nil, fmt.Errorf("controlplane persistence wiring: direct writer coordinator: %w", err)
	}
	return &PermissionStateDirectWriters{
		Transactions: transactions,
		Authority:    authority,
		Coordinator:  coordinator,
	}, nil
}

func (w *PermissionStateDirectWriters) BindSchema(repository *persistence.SchemaRepository) error {
	if w == nil || w.Coordinator == nil {
		return errors.New("controlplane persistence wiring: direct writer composition is required")
	}
	if err := repository.SetPermissionStateDirectWriterCoordinator(w.Coordinator); err != nil {
		return fmt.Errorf("controlplane persistence wiring: schema repository: %w", err)
	}
	return nil
}

func (w *PermissionStateDirectWriters) BindMetadata(repository *persistence.MetadataRepository) error {
	if w == nil || w.Coordinator == nil {
		return errors.New("controlplane persistence wiring: direct writer composition is required")
	}
	if err := repository.SetPermissionStateDirectWriterCoordinator(w.Coordinator); err != nil {
		return fmt.Errorf("controlplane persistence wiring: metadata repository: %w", err)
	}
	return nil
}

func (w *PermissionStateDirectWriters) BindPermission(repository *persistence.PermissionRepository) error {
	if w == nil || w.Coordinator == nil {
		return errors.New("controlplane persistence wiring: direct writer composition is required")
	}
	if err := repository.SetPermissionStateDirectWriterCoordinator(w.Coordinator); err != nil {
		return fmt.Errorf("controlplane persistence wiring: permission repository: %w", err)
	}
	return nil
}

func (w *PermissionStateDirectWriters) BindUser(repository *persistence.UserRepository) error {
	if w == nil || w.Coordinator == nil {
		return errors.New("controlplane persistence wiring: direct writer composition is required")
	}
	if err := repository.SetPermissionStateDirectWriterCoordinator(w.Coordinator); err != nil {
		return fmt.Errorf("controlplane persistence wiring: user repository: %w", err)
	}
	return nil
}
