package formulastorage

import (
	"fmt"
	"strings"

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

const (
	ModeViewOnly          = "view_only"
	ModeWriteMaterialized = "write_materialized"
)

type Spec struct {
	FieldType           string
	Formula             string
	FormulaMode         string
	FormulaStorageMode  string
	HasFormula          bool
	HasReturnType       bool
	HasProjectionConfig bool
}

type ColumnSpec struct {
	LogicalType        string
	Formula            string
	FormulaMode        string
	FormulaStorageMode string
	ProjectionConfig   *models.FieldProjectionConfig
}

type Column interface {
	FormulaStorageColumnSpec() ColumnSpec
}

type Resolution struct {
	FormulaBacked bool
	StorageMode   string
}

func SpecFromField(field *models.FieldMetadata) Spec {
	if field == nil {
		return Spec{}
	}
	formula := ""
	if field.Formula != nil {
		formula = *field.Formula
	}
	formulaMode := ""
	if field.FormulaMode != nil {
		formulaMode = *field.FormulaMode
	}
	formulaStorageMode := ""
	if field.FormulaStorageMode != nil {
		formulaStorageMode = *field.FormulaStorageMode
	}
	return Spec{
		FieldType:           string(field.Type),
		Formula:             formula,
		FormulaMode:         formulaMode,
		FormulaStorageMode:  formulaStorageMode,
		HasProjectionConfig: field.ProjectionConfig != nil,
	}
}

func SpecFromColumn(col Column) Spec {
	if col == nil {
		return Spec{}
	}
	return SpecFromColumnSpec(col.FormulaStorageColumnSpec())
}

func SpecFromColumnSpec(col ColumnSpec) Spec {
	return Spec{
		FieldType:           col.LogicalType,
		Formula:             col.Formula,
		FormulaMode:         col.FormulaMode,
		FormulaStorageMode:  col.FormulaStorageMode,
		HasProjectionConfig: col.ProjectionConfig != nil,
	}
}

func (r Resolution) IsWriteMaterialized() bool {
	return r.FormulaBacked && r.StorageMode == ModeWriteMaterialized
}

func (r Resolution) RequiresPhysicalColumn() bool {
	if !r.FormulaBacked {
		return true
	}
	return r.StorageMode == ModeWriteMaterialized
}

func (r Resolution) RequiresFormulaView() bool {
	return r.FormulaBacked && r.StorageMode == ModeViewOnly
}

func Resolve(spec Spec) (Resolution, error) {
	hasFormula := spec.HasFormula || strings.TrimSpace(spec.Formula) != ""
	hasProjection := spec.HasProjectionConfig
	if !hasFormula && !hasProjection {
		return Resolution{}, nil
	}

	storageMode, storageErr := normalizeMode(spec.FormulaStorageMode)

	formulaMode := strings.ToLower(strings.TrimSpace(spec.FormulaMode))
	resolved := Resolution{FormulaBacked: true, StorageMode: ModeWriteMaterialized}

	if hasProjection {
		resolved.StorageMode = ModeViewOnly
	} else if formulaMode == "active" {
		if storageMode == ModeWriteMaterialized {
			resolved.StorageMode = ModeWriteMaterialized
		} else {
			resolved.StorageMode = ModeViewOnly
		}
	}

	return resolved, storageErr
}

func IsActiveComputedField(field *models.FieldMetadata) bool {
	if field == nil {
		return false
	}
	resolved, err := Resolve(SpecFromField(field))
	_ = err
	return resolved.RequiresFormulaView()
}

func FieldRequiresPhysicalColumn(field *models.FieldMetadata) bool {
	if field == nil {
		return false
	}
	resolved, err := Resolve(SpecFromField(field))
	if err != nil {
		return false
	}
	return resolved.RequiresPhysicalColumn()
}

func SchemaFieldIsActiveComputed(schema *models.ObjectMetadata, fieldName string) bool {
	if schema == nil || fieldName == "" {
		return false
	}
	for i := range schema.Fields {
		if strings.EqualFold(schema.Fields[i].APIName, fieldName) {
			return IsActiveComputedField(&schema.Fields[i])
		}
	}
	return false
}

func ActiveComputedFields(schema *models.ObjectMetadata) []models.FieldMetadata {
	if schema == nil || len(schema.Fields) == 0 {
		return nil
	}
	result := make([]models.FieldMetadata, 0, len(schema.Fields))
	for i := range schema.Fields {
		if IsActiveComputedField(&schema.Fields[i]) {
			result = append(result, schema.Fields[i])
		}
	}
	return result
}

func ActiveComputedFieldPointers(schema *models.ObjectMetadata) []*models.FieldMetadata {
	if schema == nil || len(schema.Fields) == 0 {
		return nil
	}
	result := make([]*models.FieldMetadata, 0, len(schema.Fields))
	for i := range schema.Fields {
		if IsActiveComputedField(&schema.Fields[i]) {
			result = append(result, &schema.Fields[i])
		}
	}
	return result
}

func HasActiveComputedFields(schema *models.ObjectMetadata) bool {
	if schema == nil {
		return false
	}
	for i := range schema.Fields {
		if IsActiveComputedField(&schema.Fields[i]) {
			return true
		}
	}
	return false
}

func normalizeMode(raw string) (string, error) {
	trimmed := strings.TrimSpace(raw)
	storageMode := strings.ToLower(trimmed)
	switch storageMode {
	case "", ModeViewOnly, ModeWriteMaterialized:
		if raw != trimmed || trimmed != storageMode {
			return "", fmt.Errorf("invalid formulaStorageMode %q", raw)
		}
		return storageMode, nil
	default:
		return "", fmt.Errorf("invalid formulaStorageMode %q", raw)
	}
}
