package runtimeports

import (
	"context"
	"database/sql"

	database "github.com/nexuscrm/db"
	"github.com/nexuscrm/db/pkg/schemashape"
	"github.com/nexuscrm/kernel/pkg/schema"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/assertion"
)

// SchemaProvisioner is the composition seam for the control plane's physical
// schema / DDL / reconcile operations. It collects the internal/bootstrap
// functions that create and reconcile L0 table shapes, discover physical
// tables, run design assertions, and verify schema readiness, so that
// control-plane consumers (internal/controlplane and internal/controlplane/packs)
// depend on this neutral interface instead of importing internal/bootstrap for
// these DDL writes.
//
// The concrete implementation (internal/bootstrap.SchemaProvisionerImpl)
// delegates each method to the canonical bootstrap function of the same name, so
// behavior is byte-for-byte identical to calling the bootstrap function directly.
// Unlike the field-provenance port, these functions mutate the database, so each
// method KEEPS its per-call *sql.DB / *database.TiDBConnection / context.Context
// argument exactly as the bootstrap function declares it. The impl is therefore
// stateless: no DB is bound at construction, and the package-init seam default
// works without wiring. The concrete impl is wired at the runtime composition
// root (pkg/serverruntime).
//
// Every type named in this interface lives in a leaf module that imports nothing
// backend-internal (github.com/nexuscrm/db, github.com/nexuscrm/kernel/pkg/schema,
// nexuscrm-contract/pkg/assertion, nexuscrm-contract/pkg/schemashape), so this
// package stays a pure leaf: it imports only stdlib and those neutral packages,
// never internal/bootstrap. It carries no behavior, DB access, or global state of
// its own; like every symbol in this package it is interface-only.
type SchemaProvisioner interface {
	// InitializeSchema runs the full physical schema initialization for the
	// managed L0 substrate. Delegates to bootstrap.InitializeSchema.
	InitializeSchema(db *database.TiDBConnection) error

	// InitializeSchemaL0 runs the L0-only physical schema initialization.
	// Delegates to bootstrap.InitializeSchemaL0.
	InitializeSchemaL0(db *database.TiDBConnection) error

	// EnsureL0SchemaDDL ensures the L0 schema DDL is applied (idempotent).
	// Delegates to bootstrap.EnsureL0SchemaDDL.
	EnsureL0SchemaDDL(db *database.TiDBConnection) error

	// ReconcileTableColumns reconciles physical columns to match the supplied
	// table definitions. Delegates to bootstrap.ReconcileTableColumns.
	ReconcileTableColumns(rawDB *sql.DB, tableDefs []schema.TableDefinition) error

	// ReconcileSystemObjectTableColumns reconciles the columns of the managed
	// system-object tables. Delegates to bootstrap.ReconcileSystemObjectTableColumns.
	ReconcileSystemObjectTableColumns(rawDB *sql.DB) error

	// ReconcileSystemObjectTableTypes reconciles the column types of the managed
	// system-object tables. Delegates to bootstrap.ReconcileSystemObjectTableTypes.
	ReconcileSystemObjectTableTypes(ctx context.Context, db *sql.DB) error

	// EnsureStandardIndexes ensures the standard indexes exist on managed tables.
	// Delegates to bootstrap.EnsureStandardIndexes.
	EnsureStandardIndexes(dbInstance *database.TiDBConnection) error

	// DiscoverAndRegisterPhysicalTables discovers physical tables and registers
	// them. Delegates to bootstrap.DiscoverAndRegisterPhysicalTables.
	DiscoverAndRegisterPhysicalTables(dbInstance *database.TiDBConnection) error

	// RunAssertions executes all design assertions and returns the violations.
	// Delegates to bootstrap.RunAssertions.
	RunAssertions(db *database.TiDBConnection, strictMode bool) (*assertion.AssertionResult, error)

	// EnsureFolderSharingModels ensures the folder-sharing models / tables exist.
	// Delegates to bootstrap.EnsureFolderSharingModels.
	EnsureFolderSharingModels(ctx context.Context, db *sql.DB) error

	// DropGroupMemberUserFK drops the legacy group-member -> user foreign key.
	// Delegates to bootstrap.DropGroupMemberUserFK.
	DropGroupMemberUserFK(ctx context.Context, db *sql.DB) error

	// NormalizeLegacyDDLDefaults normalizes legacy DDL column defaults.
	// Delegates to bootstrap.NormalizeLegacyDDLDefaults.
	NormalizeLegacyDDLDefaults(ctx context.Context, dbConn *database.TiDBConnection) error

	// BackfillFieldCapabilities backfills field-capability metadata.
	// Delegates to bootstrap.BackfillFieldCapabilities.
	BackfillFieldCapabilities(dbConn *database.TiDBConnection) error

	// VerifySchemaReadiness verifies the schema is ready for serving.
	// Delegates to bootstrap.VerifySchemaReadiness.
	VerifySchemaReadiness(db *database.TiDBConnection) error

	// LoadPhysicalColumnShapes loads the physical column shapes keyed by
	// table then column. Delegates to bootstrap.LoadPhysicalColumnShapes.
	LoadPhysicalColumnShapes(rawDB *sql.DB) (map[string]map[string]schemashape.PhysicalColumnShape, error)

	// DesiredColumnType returns the resolved physical column type for a column
	// definition (the type the substrate should provision). Delegates to
	// bootstrap.DesiredColumnType.
	DesiredColumnType(col schema.ColumnDefinition) string

	// ColumnShapeMatches reports whether an existing physical column shape
	// matches the desired column definition (type / nullability / default).
	// Delegates to bootstrap.ColumnShapeMatches.
	ColumnShapeMatches(existing schemashape.PhysicalColumnShape, desired schema.ColumnDefinition) bool

	// ReconcileManagedStandardObjectAccessPoliciesWithDefs reconciles the
	// managed access-policy metadata (SharingModel, EnableHierarchySharing,
	// AccessPolicy) for managed standard objects using the supplied table
	// definitions. Delegates to
	// bootstrap.ReconcileManagedStandardObjectAccessPoliciesWithDefs.
	ReconcileManagedStandardObjectAccessPoliciesWithDefs(rawDB *sql.DB, tableDefs []schema.TableDefinition) error

	// AssertManagedAccessPolicyConsistency checks managed access-policy
	// consistency for standard objects and appends any violations to result.
	// Delegates to bootstrap.AssertManagedAccessPolicyConsistency.
	AssertManagedAccessPolicyConsistency(db *sql.DB, result *assertion.AssertionResult, managedPolicies map[string]schema.ManagedStandardObjectPolicy)

	// AssertManagedRelationshipConsistency checks managed relationship
	// consistency against the supplied definitions and appends any violations
	// to result. Delegates to bootstrap.AssertManagedRelationshipConsistency.
	AssertManagedRelationshipConsistency(db *sql.DB, result *assertion.AssertionResult, defs []schema.ManagedRelationshipSemantics, tableDefs ...[]schema.TableDefinition)

	// BuildManagedRelationshipFieldMetadata derives field-level relationship
	// metadata (keyed by child|parent|field) from the supplied table
	// definitions. Delegates to bootstrap.BuildManagedRelationshipFieldMetadata.
	BuildManagedRelationshipFieldMetadata(tableDefs []schema.TableDefinition) map[string]ManagedRelationshipFieldMetadata

	// ManagedRelationshipKey builds the lower-cased child|parent|field key.
	// Delegates to bootstrap.ManagedRelationshipKey.
	ManagedRelationshipKey(childObjectAPIName, parentObjectAPIName, fieldAPIName string) string

	// NeedsOverlayRow reports whether the managed definition requires an
	// explicit _System_Relationship overlay row given the field-derived
	// metadata. Delegates to bootstrap.NeedsOverlayRow.
	NeedsOverlayRow(meta ManagedRelationshipFieldMetadata, def schema.ManagedRelationshipSemantics) bool

	// LookupReferenceRepairTargets builds the authoritative bootstrap-owned
	// reference_to target map for managed lookup/master-detail fields.
	// Delegates to bootstrap.LookupReferenceRepairTargets.
	LookupReferenceRepairTargets() LookupReferenceTargets
}

// ManagedRelationshipFieldMetadata captures the field-derived relationship
// shape used to decide whether an explicit _System_Relationship overlay row is
// needed. It is the neutral form of bootstrap.ManagedRelationshipFieldMetadata
// (which is a type alias to this type), so the SchemaProvisioner port can name
// the metadata returned by BuildManagedRelationshipFieldMetadata and consumed
// by NeedsOverlayRow without control-plane callers importing internal/bootstrap.
type ManagedRelationshipFieldMetadata struct {
	RelationshipName string
	RelationshipType string
}

// LookupReferenceTargets is the read-only view of the authoritative
// bootstrap-owned reference_to target map returned by
// LookupReferenceRepairTargets. The concrete bootstrap.LookupReferenceTargets
// value satisfies this interface; the interface lets control-plane callers
// resolve repair targets without importing internal/bootstrap.
type LookupReferenceTargets interface {
	// ReferenceTo resolves the authoritative reference_to targets for a field,
	// returning (targets, inScope).
	ReferenceTo(objectAPIName, fieldAPIName string) ([]string, bool)
}
