package metadata

import (
	"context"
	"fmt"
	"log"
	"regexp"
	"time"

	apperrors "github.com/nexuscrm/backend/pkg/errors"
	"github.com/nexuscrm/shared/pkg/constants"
	sharedEvents "github.com/nexuscrm/shared/pkg/events"
	"github.com/nexuscrm/shared/pkg/models"
)

// BatchCreateFields creates multiple fields for an object in a single transaction/DDL
// Returns the list of fields that were successfully processed/validated for further action (e.g. permissions)
func (ms *MetadataService) BatchCreateFields(ctx context.Context, objectAPIName string, fields []models.FieldMetadata) ([]*models.FieldMetadata, error) {
	ms.mu.Lock()
	defer ms.mu.Unlock()

	if len(fields) == 0 {
		return nil, nil
	}

	// 1. Get the object
	obj, err := ms.repo.GetSchemaByAPIName(ctx, objectAPIName)
	if err != nil || obj == nil {
		return nil, apperrors.NewNotFoundError("Object", objectAPIName)
	}

	var batchFields []models.FieldMetadata
	var validFields []*models.FieldMetadata

	// 2. Process and Validate each field
	for i := range fields {
		field := &fields[i]

		// Basic Validation (Copy of CreateField logic)
		if ms.validationSvc != nil {
			if err := ms.validationSvc.ValidateFieldMetadata(field); err != nil {
				return nil, apperrors.NewValidationError(field.APIName, fmt.Sprintf("invalid field: %v", err))
			}
		}

		// Regex Validation
		validName := regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
		if !validName.MatchString(field.APIName) {
			return nil, apperrors.NewValidationError("api_name", fmt.Sprintf("invalid field API name '%s'", field.APIName))
		}

		// Allow existing fields to pass through to Repo for adoption (e.g. Ensure Unique Constraint)
		// Repo handles idempotency.

		batchFields = append(batchFields, *field) // Copy for batch
		validFields = append(validFields, field)  // Ptr for return

		// Polymorphic Extra Column
		if field.Type == constants.FieldTypeReference && len(field.ReferenceTo) > 1 {
			typeColName := GetPolymorphicTypeColumnName(field.APIName)

			// Synthesize FieldMetadata for the type column
			typeField := models.FieldMetadata{
				APIName:  typeColName,
				Label:    field.Label + " Type",
				Type:     constants.FieldTypeString,
				Required: false,
				IsSystem: false,
			}
			// Note: CreateField previously used `AddColumn` with `ColumnDefinition`.
			// `AddColumn` calls `registerField` which registers it.
			// So we MUST include it in `batchFields` to be created and registered.

			batchFields = append(batchFields, typeField)
		}
	}

	if len(batchFields) == 0 {
		return validFields, nil
	}

	// 3. Execute Batch DDL via Repository
	if err := ms.schemaMgr.BatchAddColumns(objectAPIName, batchFields); err != nil {
		return nil, apperrors.NewInternalError("batch creation failed", err)
	}

	// 4. Post-Creation: Layouts & Auto-Numbers (Loop)
	sawAutoNumber := false
	for _, field := range validFields {
		// Layout
		if err := ms.addFieldToLayout(ctx, objectAPIName, field.APIName); err != nil {
			log.Printf("Warning: Failed to add field %s to layout: %v", field.APIName, err)
		}

		// AutoNumber (v2 engine — PRD §7.2 step k)
		if field.Type == constants.FieldTypeAutonumber {
			format := "{0}"
			if field.DefaultValue != nil && *field.DefaultValue != "" {
				format = *field.DefaultValue
			}
			if err := ms.repo.ConfigureAutoNumberField(ctx, objectAPIName, field.APIName, format, 1); err != nil {
				log.Printf("⚠️ Failed to configure auto-number field %s.%s: %v", objectAPIName, field.APIName, err)
			}
			sawAutoNumber = true
		}
	}
	// Round-5 Codex V1-P2 / V2-H3: invalidate the autonumber engine's
	// MetadataSource once per batch so the new fields become visible
	// to LookupByObject without a process restart. Done once after
	// the loop to avoid N full refreshes for a large batch.
	if sawAutoNumber && ms.autoNumberInvalidator != nil {
		if err := ms.autoNumberInvalidator.InvalidateObject(ctx, objectAPIName); err != nil {
			log.Printf("⚠️ Failed to invalidate autonumber metadata for %s: %v",
				objectAPIName, err)
		}
	}

	ms.invalidateCacheLocked()

	// Publish FieldCreated events for downstream listeners (for example formula views).
	for _, field := range validFields {
		ms.publishMetadataEvent(ctx, sharedEvents.FieldCreated, sharedEvents.SchemaEventPayload{
			EventType:     sharedEvents.FieldCreated,
			ObjectAPIName: objectAPIName,
			FieldAPIName:  field.APIName,
			Timestamp:     time.Now().Format(time.RFC3339),
		})
	}

	return validFields, nil
}
