package dialectsfcrm

import (
	"context"
	"net/http"
	"testing"

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

// conflictLockstepContracts is a focused MetadataAuthoring fake for the #3484
// lockstep behavior of ToolingCustomObjectCreateNeutralHandler. It embeds the
// shared stub and overrides only CreateSchema + GetEffectiveSchemaForAuthoring,
// with a call-sequenced effective-schema result so a test can distinguish the
// pre-create existence check from the post-Conflict re-verify.
type conflictLockstepContracts struct {
	*uiAPITestContracts
	createErr        error
	effectiveByCall  []*models.ObjectMetadata
	effectiveDefault *models.ObjectMetadata
	calls            int
}

func (c *conflictLockstepContracts) CreateSchema(context.Context, contract.MetadataSchemaCreateRequest) (*models.ObjectMetadata, error) {
	if c.createErr != nil {
		return nil, c.createErr
	}
	return nil, nil
}

func (c *conflictLockstepContracts) GetEffectiveSchemaForAuthoring(context.Context, string, *models.UserSession) *models.ObjectMetadata {
	i := c.calls
	c.calls++
	if i < len(c.effectiveByCall) {
		return c.effectiveByCall[i]
	}
	return c.effectiveDefault
}

func newConflictLockstepDeps(c *conflictLockstepContracts) dialect.Deps {
	return dialect.Deps{Contracts: contract.Deps{MetadataAuthoring: c}}
}

// TestToolingCustomObjectCreate_ConflictWithoutLiveObjectIsNotSuccess is the
// load-bearing lockstep guard for #3484 review finding #1 (the false-success
// laundering / hunted 4th miss). After the core strict-create lands, a create on
// an orphan-blocked name returns Kind=Conflict with NO live object. The handler
// must NOT launder that into 200 OK — it must re-verify liveness and, finding
// none, surface a non-2xx error so ensure-exists deploy loops fail loudly.
//
// Load-bearing: revert the handler to the unconditional `return
// toolingSuccess(200)` on Conflict and this fails (a false 200 with no object).
func TestToolingCustomObjectCreate_ConflictWithoutLiveObjectIsNotSuccess(t *testing.T) {
	c := &conflictLockstepContracts{
		uiAPITestContracts: newUIAPITestContracts(),
		createErr:          &contract.MetadataAuthoringError{Kind: contract.MetadataAuthoringErrorConflict, Message: "blocked by orphaned physical table"},
		// Pre-create check: nil (no live object). Re-verify after Conflict: nil.
		effectiveDefault: nil,
	}
	resp, err := ToolingCustomObjectCreateNeutralHandler(context.Background(), &dialect.Request{
		Body: []byte(`{"name":"Widget__c","label":"Widget"}`),
	}, newConflictLockstepDeps(c))
	if err != nil {
		t.Fatalf("handler error: %v", err)
	}
	if resp.Status >= 200 && resp.Status < 300 {
		t.Fatalf("status = %d, want a non-2xx error (orphan-blocked create must not report success)", resp.Status)
	}
}

// TestToolingCustomObjectCreate_ConflictWithLiveObjectStaysIdempotent is the
// companion: a Conflict whose re-verify DOES find a live object preserves the
// idempotent-deploy 200 OK (a genuine "already exists" re-run). Here the
// pre-create check returns nil (so the handler reaches CreateSchema) and the
// post-Conflict re-verify returns a live schema.
func TestToolingCustomObjectCreate_ConflictWithLiveObjectStaysIdempotent(t *testing.T) {
	live := &models.ObjectMetadata{APIName: "Widget__c", Label: "Widget"}
	c := &conflictLockstepContracts{
		uiAPITestContracts: newUIAPITestContracts(),
		createErr:          &contract.MetadataAuthoringError{Kind: contract.MetadataAuthoringErrorConflict, Message: "already exists"},
		// Call 0 (pre-create) = nil so CreateSchema runs; call 1 (re-verify) = live.
		effectiveByCall:  []*models.ObjectMetadata{nil, live},
		effectiveDefault: live,
	}
	resp, err := ToolingCustomObjectCreateNeutralHandler(context.Background(), &dialect.Request{
		Body: []byte(`{"name":"Widget__c","label":"Widget"}`),
	}, newConflictLockstepDeps(c))
	if err != nil {
		t.Fatalf("handler error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200 (Conflict with a live object stays idempotent)", resp.Status)
	}
}
