package packs

import (
	"context"
	"encoding/json"
	"testing"

	"github.com/nexuscrm/kernel/pkg/formula"
)

type formulaStorageTransitionDeclarationPack struct {
	name     string
	payloads [][]byte
	bundles  []formula.FunctionBundle
}

func (p formulaStorageTransitionDeclarationPack) Name() string                           { return p.name }
func (p formulaStorageTransitionDeclarationPack) Category() Category                     { return AppPack }
func (p formulaStorageTransitionDeclarationPack) Apply(context.Context, ApplyDeps) error { return nil }
func (p formulaStorageTransitionDeclarationPack) Verify(context.Context, VerifyDeps) error {
	return nil
}
func (p formulaStorageTransitionDeclarationPack) RuntimeObjectSpecPayloads() [][]byte {
	return p.payloads
}
func (p formulaStorageTransitionDeclarationPack) RuntimeFormulaFunctionBundles() []formula.FunctionBundle {
	return p.bundles
}

func formulaStorageTransitionRuntimePayload(storageMode, dependencyType string) []byte {
	payload, _ := json.Marshal([]ObjectSpec{{
		APIName: "Lead",
		Fields: []FieldSpec{
			{APIName: "Email", Type: dependencyType},
			{APIName: "EmailDomain__c", Type: "formula", Formula: "LOWER(Email)", FormulaMode: "active", FormulaStorageMode: storageMode, ReturnType: "string"},
		},
	}})
	return payload
}

func formulaStorageTransitionPersistedObjectJSON(t *testing.T, payload []byte) string {
	t.Helper()
	got, err := marshalRuntimeObjectSpecPayloads([][]byte{payload})
	if err != nil {
		t.Fatal(err)
	}
	return got
}

func TestCompareFormulaStorageTransitionRuntimeDeclarationsAcceptsOnlyStorageChange(t *testing.T) {
	active := formulaStorageTransitionPersistedObjectJSON(t, formulaStorageTransitionRuntimePayload("", "string"))
	candidate := formulaStorageTransitionDeclarationPack{
		name: "candidate", payloads: [][]byte{formulaStorageTransitionRuntimePayload("write_materialized", "string")},
	}
	comparison, err := CompareFormulaStorageTransitionRuntimeDeclarations(active, "[]", candidate)
	if err != nil {
		t.Fatal(err)
	}
	if !comparison.SemanticsEqual || len(comparison.Transitions) != 1 ||
		comparison.Transitions[0].ObjectAPIName != "Lead" || comparison.Transitions[0].FieldAPIName != "EmailDomain__c" {
		t.Fatalf("comparison = %#v", comparison)
	}
}

func TestCompareFormulaStorageTransitionRuntimeDeclarationsTreatsEmptyRepresentationsAsEqual(t *testing.T) {
	candidate := formulaStorageTransitionDeclarationPack{name: "empty-candidate"}
	comparison, err := CompareFormulaStorageTransitionRuntimeDeclarations("[]", "[]", candidate)
	if err != nil {
		t.Fatal(err)
	}
	if !comparison.SemanticsEqual || len(comparison.Transitions) != 0 {
		t.Fatalf("comparison = %#v, want an unchanged empty runtime declaration", comparison)
	}
}

func TestCompareFormulaStorageTransitionRuntimeDeclarationsRejectsDependencyChange(t *testing.T) {
	active := formulaStorageTransitionPersistedObjectJSON(t, formulaStorageTransitionRuntimePayload("", "string"))
	candidate := formulaStorageTransitionDeclarationPack{
		name: "candidate", payloads: [][]byte{formulaStorageTransitionRuntimePayload("write_materialized", "email")},
	}
	comparison, err := CompareFormulaStorageTransitionRuntimeDeclarations(active, "[]", candidate)
	if err != nil {
		t.Fatal(err)
	}
	if comparison.SemanticsEqual || len(comparison.Transitions) != 1 {
		t.Fatalf("comparison = %#v, want transition with rejected candidate-wide semantics", comparison)
	}
}

func TestCompareFormulaStorageTransitionRuntimeDeclarationsRejectsFunctionBundleChange(t *testing.T) {
	active := formulaStorageTransitionPersistedObjectJSON(t, formulaStorageTransitionRuntimePayload("", "string"))
	candidate := formulaStorageTransitionDeclarationPack{
		name: "candidate", payloads: [][]byte{formulaStorageTransitionRuntimePayload("write_materialized", "string")},
		bundles: []formula.FunctionBundle{{Functions: []formula.FunctionDefinition{{Name: "changed"}}}},
	}
	comparison, err := CompareFormulaStorageTransitionRuntimeDeclarations(active, "[]", candidate)
	if err != nil {
		t.Fatal(err)
	}
	if comparison.SemanticsEqual || len(comparison.Transitions) != 1 {
		t.Fatalf("comparison = %#v, want transition with rejected function semantics", comparison)
	}
}
