package persistence

import (
	"database/sql"

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

func (r *MetadataRepository) scanObject(row Scannable) (*models.ObjectMetadata, error) {
	var obj models.ObjectMetadata
	var description, icon, pathField, listFieldsJSON, childCloneRulesJSON, accessPolicyJSON, appID, pluralLabel, themeColor, sharingModel, tableType, requiredWritePermission, requiredReadPermission, deleteMode, ownerID, createdByID, lastModifiedByID sql.NullString
	var isCustom, searchable, isDeleted bool
	var isCustomized sql.NullBool
	var enableHierarchySharing sql.NullBool
	var createdDateVal, lastModifiedDateVal interface{}

	// ColsSystemObject: ID, APIName, TableType, Label, PluralLabel, AppID, Icon, Description, ThemeColor, IsCustom, Searchable, SharingModel, IsCustomized, EnableHierarchySharing, RequiredWritePermission, RequiredReadPermission, AccessPolicy, DeleteMode, PathField, ListFields, ChildCloneRules, IsDeleted, OwnerID, CreatedByID, LastModifiedByID, CreatedDate, LastModifiedDate
	err := row.Scan(
		&obj.ID, &obj.APIName, &tableType, &obj.Label, &pluralLabel, &appID, &icon, &description,
		&themeColor, &isCustom, &searchable, &sharingModel, &isCustomized, &enableHierarchySharing, &requiredWritePermission, &requiredReadPermission, &accessPolicyJSON, &deleteMode, &pathField, &listFieldsJSON, &childCloneRulesJSON, &isDeleted,
		&ownerID, &createdByID, &lastModifiedByID, &createdDateVal, &lastModifiedDateVal,
	)
	if err != nil {
		return nil, err
	}

	if pluralLabel.Valid {
		obj.PluralLabel = pluralLabel.String
	}
	if description.Valid {
		val := description.String
		obj.Description = &val
	}
	if icon.Valid {
		obj.Icon = icon.String
	}
	if themeColor.Valid {
		val := themeColor.String
		obj.ThemeColor = &val
	}
	if appID.Valid {
		val := appID.String
		obj.AppID = &val
	}
	if pathField.Valid {
		val := pathField.String
		obj.PathField = &val
	}
	if sharingModel.Valid {
		obj.SharingModel = constants.SharingModel(sharingModel.String)
	} else {
		obj.SharingModel = constants.SharingModelPrivate
	}
	if isCustomized.Valid {
		obj.IsCustomized = isCustomized.Bool
	}

	obj.IsCustom = isCustom
	obj.Searchable = searchable
	if tableType.Valid {
		obj.TableType = constants.TableType(tableType.String)
	}
	switch constants.TableType(tableType.String) {
	case constants.TableTypeCustomObject, constants.TableTypeStandardObject:
		obj.IsSystem = false
	case constants.TableTypeSystemMetadata, constants.TableTypeSystemCore, constants.TableTypeSystemData, constants.TableTypeSystemJunction:
		obj.IsSystem = true
	default:
		obj.IsSystem = constants.IsSystemTable(obj.APIName)
	}

	// Unmarshal ListFields
	if listFieldsJSON.Valid {
		r.unmarshalJSON(listFieldsJSON.String, &obj.ListFields)
	}

	// Unmarshal ChildCloneRules (tenant-authored post-insert side effects).
	// Greenfield rows carry NULL here and obj.ChildCloneRules stays nil, which
	// is exactly what the persistence post-insert hook needs to no-op.
	if childCloneRulesJSON.Valid && childCloneRulesJSON.String != "" {
		r.unmarshalJSON(childCloneRulesJSON.String, &obj.ChildCloneRules)
	}

	if enableHierarchySharing.Valid {
		obj.EnableHierarchySharing = enableHierarchySharing.Bool
	}
	if requiredWritePermission.Valid {
		obj.RequiredWritePermission = requiredWritePermission.String
	}
	if requiredReadPermission.Valid {
		obj.RequiredReadPermission = requiredReadPermission.String
	}
	if accessPolicyJSON.Valid && accessPolicyJSON.String != "" {
		r.unmarshalJSON(accessPolicyJSON.String, &obj.AccessPolicy)
	}
	if deleteMode.Valid {
		obj.DeleteMode = deleteMode.String
	}
	obj.Fields = make([]models.FieldMetadata, 0)

	return &obj, nil
}

func (r *MetadataRepository) scanField(row Scannable) (*models.FieldMetadata, string, error) {
	var field models.FieldMetadata
	var id, objectAPIName string
	var required, unique, isSystem, trackHistory, isNameField, isMasterDetail, isPolymorphic, createable, updateable sql.NullBool
	var options, referenceTo, formula, returnType, formulaMode, defaultValue, helpText, controllingField, picklistDependency, rollupConfig, deleteRule, relationshipName, regex, regexMessage, validator, description, physicalName sql.NullString
	var minValue, maxValue sql.NullFloat64
	var minLength, maxLength sql.NullInt64

	// ColsSystemField: ID, ObjectID, APIName, Label, PhysicalName, Type, Required, IsUnique, IsSystem, IsNameField, Createable, Updateable, Indexed, Options, ReferenceTo, DeleteRule, Formula, ReturnType, FormulaMode, DefaultValue, HelpText, Description, TrackHistory, MinValue, MaxValue, MinLength, MaxLength, Regex, RegexMessage, Validator, ControllingField, PicklistDependency, RollupConfig, IsMasterDetail, IsPolymorphic, RelationshipName, IsDeleted, OwnerID, CreatedByID, LastModifiedByID, CreatedDate, LastModifiedDate, IsCustomized, AutoNumberFormat, AutoNumberScope, AutoNumberStart, AutoNumberTokenRefs, AutoNumberCanonicalHash, AutoNumberCounterResetPolicy
	//
	// Issue #1582 RFC v1.6: Source / IsCompound / ComposedFrom columns are
	// no longer part of ColsSystemField — provenance is derived at read
	// time by fieldprovenance.ClassifySource / ClassifyCompound.
	var isDeleted bool
	var ownerID, createdByID, lastModifiedByID sql.NullString
	var createdDateVal, lastModifiedDateVal interface{}
	var indexed sql.NullBool
	var isCustomized sql.NullBool
	// AutoNumber* columns are scanned here for column-parity with ColsSystemField
	// (Wave 1 Lane B added 6 new columns). FieldMetadata does not carry them —
	// autonumber configuration is consumed by the dedicated
	// autonumber.MetadataSource which runs its own SELECT. These destinations
	// exist solely to keep scanField's Scan() argument count aligned with the
	// SELECT produced by query.From(TableField).Select(ColsSystemField).
	var autoNumberFormat, autoNumberScope, autoNumberTokenRefs, autoNumberCanonicalHash, autoNumberCounterResetPolicy sql.NullString
	var autoNumberStart sql.NullInt64

	err := row.Scan(
		&id, &objectAPIName, &field.APIName, &field.Label, &physicalName, &field.Type,
		&required, &unique, &isSystem, &isNameField,
		&createable, &updateable, &indexed, &options,
		&referenceTo, &deleteRule, &formula, &returnType, &formulaMode, &defaultValue, &helpText, &description,
		&trackHistory, &minValue, &maxValue, &minLength, &maxLength,
		&regex, &regexMessage, &validator, &controllingField,
		&picklistDependency, &rollupConfig, &isMasterDetail, &isPolymorphic, &relationshipName,
		&isDeleted, &ownerID, &createdByID, &lastModifiedByID, &createdDateVal, &lastModifiedDateVal,
		&isCustomized,
		&autoNumberFormat, &autoNumberScope, &autoNumberStart,
		&autoNumberTokenRefs, &autoNumberCanonicalHash, &autoNumberCounterResetPolicy,
	)
	if err != nil {
		return nil, "", err
	}

	// Set Physical Name if present
	if physicalName.Valid {
		field.PhysicalName = physicalName.String
	}

	// Note: Field Aliasing is obsolete as Physical Names match API Names under Salesforce specifications

	field.Required = required.Bool
	field.IsUnique = unique.Bool
	field.IsSystem = isSystem.Bool
	field.TrackHistory = trackHistory.Bool
	field.IsNameField = isNameField.Bool
	field.Createable = !createable.Valid || createable.Bool // default true if NULL
	field.Updateable = !updateable.Valid || updateable.Bool // default true if NULL
	field.IsMasterDetail = isMasterDetail.Bool
	field.IsPolymorphic = isPolymorphic.Bool
	// Source / IsCompound / ComposedFrom are no longer stored — see scanField
	// header comment. Derived at read time by fieldprovenance.
	_ = isCustomized // scanned but not mapped to FieldMetadata
	// AutoNumber* values are scanned for column-parity but consumed elsewhere
	// (autonumber.MetadataSource). Blank here keeps the compiler quiet while
	// documenting that these columns intentionally do not populate FieldMetadata.
	_ = autoNumberFormat
	_ = autoNumberScope
	_ = autoNumberStart
	_ = autoNumberTokenRefs
	_ = autoNumberCanonicalHash
	_ = autoNumberCounterResetPolicy

	if formula.Valid {
		field.Formula = &formula.String
	}
	if formulaMode.Valid {
		field.FormulaMode = &formulaMode.String
	}
	if defaultValue.Valid {
		field.DefaultValue = &defaultValue.String
	}
	if helpText.Valid {
		field.HelpText = &helpText.String
	}
	if controllingField.Valid {
		field.ControllingField = &controllingField.String
	}
	if relationshipName.Valid {
		field.RelationshipName = &relationshipName.String
	}
	if regex.Valid {
		field.Regex = &regex.String
	}
	if regexMessage.Valid {
		field.RegexMessage = &regexMessage.String
	}
	if validator.Valid {
		field.Validator = &validator.String
	}
	if returnType.Valid {
		rt := models.FieldType(returnType.String)
		field.ReturnType = &rt
	}
	if deleteRule.Valid {
		dr := models.DeleteRule(deleteRule.String)
		field.DeleteRule = &dr
	}

	if minValue.Valid {
		field.MinValue = &minValue.Float64
	}
	if maxValue.Valid {
		field.MaxValue = &maxValue.Float64
	}
	if minLength.Valid {
		val := int(minLength.Int64)
		field.MinLength = &val
	}
	if maxLength.Valid {
		val := int(maxLength.Int64)
		field.MaxLength = &val
	}

	// Unmarshal JSON fields
	if referenceTo.Valid {
		r.unmarshalJSON(referenceTo.String, &field.ReferenceTo)
	}
	if options.Valid {
		r.unmarshalJSON(options.String, &field.Options)
	}
	if picklistDependency.Valid {
		r.unmarshalJSON(picklistDependency.String, &field.PicklistDependency)
	}
	if rollupConfig.Valid {
		var rc models.RollupConfig
		r.unmarshalJSON(rollupConfig.String, &rc)
		field.RollupConfig = &rc
	}

	return &field, objectAPIName, nil
}

// applyFieldAliasing is deprecated as physical names natively match Salesforce APIs
func (r *MetadataRepository) applyFieldAliasing(field *models.FieldMetadata, isCustom bool) {
}
