package schema

import (
	"time"

	"github.com/nexuscrm/shared/pkg/formulastorage"
	"github.com/nexuscrm/shared/pkg/models"
)

// ColumnDefinition represents a single column in a table
type ColumnDefinition struct {
	// MetadataID is the exact _System_Field identity to use when this column is
	// first registered. Interactive authoring mints a fresh value for every
	// create; declarative convergence leaves it empty and retains the existing
	// deterministic identity owner.
	MetadataID    string   `json:"-"`
	Name          string   `json:"name"`
	Label         string   `json:"label,omitempty"` // User-friendly label (defaults to Title Cased Name)
	Type          string   `json:"type"`
	PhysicalName  string   `json:"physicalName,omitempty"` // Explicit physical column name (e.g. `BacktickedName`)
	LogicalType   string   `json:"logicalType,omitempty"`  // Optional: Override logical type (e.g. Password, Picklist)
	PrimaryKey    bool     `json:"primaryKey,omitempty"`
	Unique        bool     `json:"unique,omitempty"`
	Nullable      bool     `json:"nullable,omitempty"`
	Default       string   `json:"default,omitempty"`
	AutoIncrement bool     `json:"autoIncrement,omitempty"`
	ReferenceTo   []string `json:"referenceTo,omitempty"`
	// WritableReferenceTo propagates a user-context writable subset for
	// polymorphic/reference targets into _System_Field. It does not affect
	// physical DDL; ReferenceTo remains the authoritative set of stored targets.
	WritableReferenceTo []string                      `json:"writableReferenceTo,omitempty"`
	IsPolymorphic       bool                          `json:"isPolymorphic,omitempty"`
	Formula             string                        `json:"formula,omitempty"`
	ReturnType          string                        `json:"returnType,omitempty"`
	FormulaMode         string                        `json:"formulaMode,omitempty"` // "active" = view-computed; "frozen" = write-time engine snapshot; "" = legacy physical column.
	FormulaStorageMode  string                        `json:"formulaStorageMode,omitempty"`
	ProjectionConfig    *models.FieldProjectionConfig `json:"projection,omitempty"`
	RollupConfig        *models.RollupConfig          `json:"rollupConfig,omitempty"`
	Validator           string                        `json:"validator,omitempty"`
	ValidatorConfig     string                        `json:"validatorConfig,omitempty"`
	OnDelete            string                        `json:"onDelete,omitempty"` // CASCADE, SET NULL, RESTRICT
	IsMasterDetail      bool                          `json:"isMasterDetail,omitempty"`
	RelationshipName    string                        `json:"relationshipName,omitempty"`
	// ChildRelationshipName carries the child-side (typically plural)
	// relationship name from the pack DSL through column registration into
	// _System_Field. Authoritative source for SOQL parent-child subqueries
	// and /describe.childRelationships emit (issue #153).
	ChildRelationshipName string                 `json:"childRelationshipName,omitempty"`
	IsNameField           bool                   `json:"isNameField,omitempty"`
	Options               models.PicklistOptions `json:"options,omitempty"`
	Length                int                    `json:"length,omitempty"`
	CollationName         string                 `json:"collationName,omitempty"`
	// Source — provenance classifier propagated into FieldMetadata.Source on
	// column registration. Callers should set this to one of the
	// constants.FieldSource* values (see shared/pkg/constants/enums.go).
	// Empty value defers classification to BackfillSource at next startup
	// (DEFAULT constants.FieldSourceDiscovered). Issue #1582 RFC v1.5 PR A3.
	Source string `json:"source,omitempty"`
	// IsCompound — propagated into FieldMetadata.IsCompound. When set, ComposedFrom
	// should also be populated. Issue #1582 RFC v1.5 PR B (declaration parsing).
	IsCompound bool `json:"isCompound,omitempty"`
	// ComposedFrom — member field ApiNames for a compound parent. Issue #1582 RFC v1.5 PR B.
	ComposedFrom []string `json:"composedFrom,omitempty"`
	// WriteProtectionPolicy propagates metadata-owned generic-write gates into
	// _System_Field. It does not affect physical DDL.
	WriteProtectionPolicy string `json:"writeProtectionPolicy,omitempty"`
	// ReadProtectionPolicy propagates metadata-owned read blocks into
	// _System_Field. It does not affect physical DDL.
	ReadProtectionPolicy string `json:"readProtectionPolicy,omitempty"`
	// SiblingMaxParentField propagates create-time sibling MAX(target)+1
	// default metadata into _System_Field. It does not affect physical DDL.
	SiblingMaxParentField string `json:"siblingMaxParentField,omitempty"`
	// GeneratedForPolymorphicReference marks an in-memory column synthesized as
	// the server-managed discriminator for the named polymorphic reference
	// field. It is derived from companion reference metadata, not from a raw
	// naming convention, and is intentionally not serialized into pack JSON.
	GeneratedForPolymorphicReference string `json:"-"`
}

func (c ColumnDefinition) FormulaStorageColumnSpec() formulastorage.ColumnSpec {
	return formulastorage.ColumnSpec{
		LogicalType:        c.LogicalType,
		Formula:            c.Formula,
		FormulaMode:        c.FormulaMode,
		FormulaStorageMode: c.FormulaStorageMode,
		ProjectionConfig:   c.ProjectionConfig,
	}
}

// IndexDefinition represents an index on a table
type IndexDefinition struct {
	Name    string   `json:"name,omitempty"`
	Columns []string `json:"columns"`
	Unique  bool     `json:"unique,omitempty"`
	Primary bool     `json:"primary,omitempty"`
}

// ForeignKeyDefinition represents a foreign key constraint
type ForeignKeyDefinition struct {
	Column     string `json:"column"`
	References string `json:"references"` // format: "tableName(columnName)"
	// LogicalOnly keeps the relationship in the managed schema model while
	// deliberately omitting a database FOREIGN KEY. Use it when the referenced
	// identity can be proven by a request-bound non-table authority (for
	// example an immutable Catalog view) as well as by a legacy physical row.
	// Runtime reference validation and delete semantics still consume the
	// relationship; only physical DDL enforcement is disabled.
	LogicalOnly bool `json:"logicalOnly,omitempty"`
	// RetainOnSoftDelete declares parent-owned configuration whose stored rows
	// survive a parent's reversible deletion. The owning lifecycle must make
	// the configuration ineffective while the parent is deleted. This is NOT
	// a physical cascade and never relaxes a hard-delete reference guard.
	RetainOnSoftDelete bool `json:"retainOnSoftDelete,omitempty"`
	// OnDelete is a physical DDL referential action. Generic record soft-delete
	// does not execute database FK actions; runtime soft-delete behavior is
	// owned by field/relationship delete semantics or explicit write-validation
	// guards.
	OnDelete string `json:"onDelete,omitempty"`
	OnUpdate string `json:"onUpdate,omitempty"`
}

// RowMutationProtectionDefinition declares one table-level, catalog-owned row
// mutation policy. ScopeFields identify the key partition, Fields identify the
// physical ownership markers, and the readiness index must contain that exact
// scope-plus-marker sequence.
type RowMutationProtectionDefinition struct {
	Policy              string   `json:"policy"`
	ScopeFields         []string `json:"scopeFields,omitempty"`
	Fields              []string `json:"fields"`
	RequiredUniqueIndex string   `json:"requiredUniqueIndex"`
}

// TableDefinition represents a complete table schema
type TableDefinition struct {
	TableName               string                     `json:"tableName"`
	KeyPrefix               string                     `json:"keyPrefix,omitempty"`
	TableType               string                     `json:"tableType"` // system_core, system_metadata, custom_object
	Category                string                     `json:"category"`  // auth, metadata, crm, etc.
	Label                   string                     `json:"label,omitempty"`
	Description             string                     `json:"description"`
	IsManaged               bool                       `json:"isManaged,omitempty"`
	SchemaVersion           string                     `json:"schemaVersion,omitempty"`
	SharingModel            string                     `json:"sharingModel,omitempty"`
	EnableHierarchySharing  bool                       `json:"enableHierarchySharing,omitempty"`
	RequiredWritePermission string                     `json:"requiredWritePermission,omitempty"`
	RequiredReadPermission  string                     `json:"requiredReadPermission,omitempty"`
	AccessPolicy            *models.ObjectAccessPolicy `json:"accessPolicy,omitempty"`
	DeleteMode              string                     `json:"deleteMode,omitempty"`
	Columns                 []ColumnDefinition         `json:"columns"`
	Indices                 []IndexDefinition          `json:"indices,omitempty"`
	ForeignKeys             []ForeignKeyDefinition     `json:"foreignKeys,omitempty"`

	RowMutationProtection *RowMutationProtectionDefinition `json:"rowMutationProtection,omitempty"`
	// LockRule is the formula expression that, when true, marks records of
	// this object as read-only (locked). Written by pack apply into
	// _System_Object.LockRule and verified by pack verify. Empty means no
	// lock rule is authored by this pack.
	LockRule string `json:"lockRule,omitempty"`
	// DisplayNameField declares which field holds the display name for records
	// of this object. Written by pack apply into _System_Object.DisplayNameField
	// and verified by pack verify. Empty means this pack does not author a
	// display name field.
	DisplayNameField string `json:"displayNameField,omitempty"`
	// ReadSource declares the metadata-owned physical base read source written
	// into _System_Object.ReadSource and verified through the same live object
	// metadata authority. Nil means this pack does not author a read-source
	// override for the logical object.
	ReadSource *models.ObjectReadSourceConfig `json:"readSource,omitempty"`
	// RetentionMaxRows caps the total number of rows to retain in this table.
	// When the table exceeds this count, the oldest rows are deleted.
	// Zero means no row-count cap. Used by the audit retention runner.
	RetentionMaxRows int `json:"retentionMaxRows,omitempty"`
	// RetentionMaxAgeSeconds caps the age (in seconds) of rows to retain.
	// Rows older than this are deleted. Zero means no age-based trim.
	RetentionMaxAgeSeconds int `json:"retentionMaxAgeSeconds,omitempty"`
	// RetentionMaxAgeMonths caps row age in whole UTC calendar months.
	// A month is deleted only after it has fully aged out. Zero means no
	// calendar-month trim.
	RetentionMaxAgeMonths int `json:"retentionMaxAgeMonths,omitempty"`
}

// FieldWithContext holds field metadata with context for batch operations
type FieldWithContext struct {
	Field            *models.FieldMetadata
	ColumnDefinition ColumnDefinition // Embed or reference column definition
	TableName        string
	ObjectID         string
	FieldID          string
	OwnerKind        string
	OwnerPack        string
	// CapabilitiesDeclared means Field.Createable/Updateable came from
	// authoritative metadata payload and should be persisted after applying
	// universal platform intrinsic bounds.
	CapabilitiesDeclared bool
}

type PackOwnedFieldConvergenceAction string

const (
	PackOwnedFieldConvergenceCreate                PackOwnedFieldConvergenceAction = "create"
	PackOwnedFieldConvergenceMetadataOnlyCreate    PackOwnedFieldConvergenceAction = "metadata_only_create"
	PackOwnedFieldConvergenceRepairMissingPhysical PackOwnedFieldConvergenceAction = "repair_missing_physical"
	PackOwnedFieldConvergenceVerifyOnly            PackOwnedFieldConvergenceAction = "verify_only"
	PackOwnedFieldConvergenceFailBeforeMutation    PackOwnedFieldConvergenceAction = "fail_before_mutation"
)

type PackOwnedFieldConvergencePlan struct {
	ObjectAPIName string
	FieldAPIName  string
	Action        PackOwnedFieldConvergenceAction
	Reason        string
	Detail        string
}

// TableRegistryItem represents a table metadata entry
type TableRegistryItem struct {
	ID               string    `json:"id"`
	TableName        string    `json:"tableName"`
	TableType        string    `json:"tableType"`
	Category         string    `json:"category"`
	Description      string    `json:"description"`
	IsManaged        bool      `json:"isManaged"`
	SchemaVersion    string    `json:"schemaVersion"`
	CreatedBy        string    `json:"createdBy"`
	CreatedDate      time.Time `json:"createdDate"`
	LastModifiedDate time.Time `json:"lastModifiedDate"`
}
