// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT

package base

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"testing"

	"github.com/pb33f/libopenapi/datamodel"
	"github.com/pb33f/libopenapi/datamodel/low"
	lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
	"github.com/pb33f/libopenapi/index"
	"github.com/pb33f/libopenapi/utils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.yaml.in/yaml/v4"
)

func TestSchemaProxy_MarshalYAML(t *testing.T) {
	const ymlComponents = `components:
    schemas:
     rice:
       type: string
     nice:
       properties:
         rice:
           $ref: '#/components/schemas/rice'
     ice:
       properties:
         rice:
           $ref: '#/components/schemas/rice'`

	idx := func() *index.SpecIndex {
		var idxNode yaml.Node
		err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
		assert.NoError(t, err)
		return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
	}()

	const ref = "#/components/schemas/nice"
	const ymlSchema = `$ref: '` + ref + `'`
	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err := lowProxy.Build(context.Background(), nil, node.Content[0], idx)
	assert.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value: lowProxy,
	}

	sp := NewSchemaProxy(&lowRef)

	origin := sp.GetReferenceOrigin()
	assert.Nil(t, origin)

	rend, _ := sp.Render()
	assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
}

func TestCreateSchemaProxy_Fail(t *testing.T) {
	proxy := &SchemaProxy{}
	assert.Nil(t, proxy.Schema())
}

func TestSchemaProxy_Schema_NoLowLevel(t *testing.T) {
	proxy := &SchemaProxy{
		lock: &sync.Mutex{},
	}
	assert.Nil(t, proxy.Schema())
}

func TestSchemaProxy_BuildSchema_NoLock(t *testing.T) {
	buildErr := errors.New("build failed")
	proxy := &SchemaProxy{
		buildError: buildErr,
	}
	schema, err := proxy.BuildSchema()
	assert.Nil(t, schema)
	assert.Equal(t, buildErr, err)
}

func TestSchemaProxy_BuildSchema_NilProxy(t *testing.T) {
	var proxy *SchemaProxy
	schema, err := proxy.BuildSchema()
	assert.Nil(t, schema)
	assert.NoError(t, err)
}

func TestSchemaProxy_GetBuildError_NilProxy(t *testing.T) {
	var proxy *SchemaProxy
	assert.Nil(t, proxy.GetBuildError())
}

func TestSchemaProxy_GetBuildError_NoLock(t *testing.T) {
	buildErr := errors.New("build failed")
	proxy := &SchemaProxy{
		buildError: buildErr,
	}
	assert.Equal(t, buildErr, proxy.GetBuildError())
}

func TestCreateSchemaProxy(t *testing.T) {
	sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
	assert.Equal(t, "iAmASchema", sp.rendered.Description)
	assert.False(t, sp.IsReference())
	assert.Nil(t, sp.GetValueNode())
}

func TestCreateSchemaProxy_NoNilValue(t *testing.T) {
	sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
	sp.Schema()

	// jerry rig the test.
	nodeRef := low.NodeReference[*lowbase.SchemaProxy]{}
	nodeRef.ValueNode = &yaml.Node{}
	sp.schema = &nodeRef

	assert.Equal(t, "iAmASchema", sp.rendered.Description)
	assert.NotNil(t, sp.GetValueNode())
}

func TestCreateSchemaProxyRef(t *testing.T) {
	sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
	assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
	assert.True(t, sp.IsReference())
}

func TestSchemaProxy_GetReference(t *testing.T) {
	refNode := utils.CreateStringNode("#/components/schemas/MySchema")

	ref := low.Reference{}
	ref.SetReference("#/components/schemas/MySchema", refNode)

	sp := &SchemaProxy{
		schema: &low.NodeReference[*lowbase.SchemaProxy]{
			Value: &lowbase.SchemaProxy{
				Reference: ref,
			},
		},
	}
	assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
	assert.Equal(t, refNode, sp.GetReferenceNode())
}

func TestSchemaProxy_GetReference_PrefersLiveRefNodeValue(t *testing.T) {
	refNode := utils.CreateRefNode("#/components/schemas/MySchema")

	ref := low.Reference{}
	ref.SetReference("#/components/schemas/MySchema", refNode)

	sp := &SchemaProxy{
		schema: &low.NodeReference[*lowbase.SchemaProxy]{
			Value: &lowbase.SchemaProxy{
				Reference: ref,
			},
		},
	}

	refNode.Content[1].Value = "#/components/schemas/MySchema__shared"

	assert.Equal(t, "#/components/schemas/MySchema__shared", sp.GetReference())
	assert.Equal(t, refNode, sp.GetReferenceNode())
}

func TestSchemaProxy_IsReference_Nil(t *testing.T) {
	var sp *SchemaProxy
	assert.False(t, sp.IsReference())
}

func TestSchemaProxy_NoSchema_GetOrigin(t *testing.T) {
	sp := &SchemaProxy{}
	assert.Nil(t, sp.GetReferenceOrigin())
}

func TestCreateSchemaProxyRef_GetReferenceNode(t *testing.T) {
	refNode := utils.CreateRefNode("#/components/schemas/MySchema")

	sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
	assert.Equal(t, refNode, sp.GetReferenceNode())
}

func TestCreateRefNode_MarshalYAML(t *testing.T) {
	ref := low.Reference{}
	ref.SetReference("#/components/schemas/MySchema", nil)

	sp := &SchemaProxy{
		schema: &low.NodeReference[*lowbase.SchemaProxy]{
			Value: &lowbase.SchemaProxy{
				Reference: ref,
			},
		},
	}
	node, err := sp.MarshalYAML()
	require.NoError(t, err)
	assert.Equal(t, node, utils.CreateRefNode("#/components/schemas/MySchema"))
}

func TestSchemaProxy_MarshalYAML_InlineCircular(t *testing.T) {
	const ymlComponents = `openapi: 3.1
components:
  schemas:
    spice:
      properties:
        ice:
          $ref: '#/components/schemas/nice'
    nice:
      properties:
        rice:
          $ref: '#/components/schemas/nice'`

	idx := func() *index.SpecIndex {
		var idxNode yaml.Node
		err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
		assert.NoError(t, err)
		return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
	}()

	resolver := index.NewResolver(idx)
	resolver.CheckForCircularReferences()

	const ymlSchema = `properties:
  rice:
    $ref: '#/components/schemas/nice'`
	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err := lowProxy.Build(context.Background(), &node, node.Content[0], idx)
	assert.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value:   lowProxy,
		KeyNode: &node,
	}

	spEmpty := NewSchemaProxy(nil)
	assert.Nil(t, spEmpty.GetSchemaKeyNode())

	sp := NewSchemaProxy(&lowRef)
	assert.NotNil(t, sp.GetSchemaKeyNode())

	rend, _ := sp.MarshalYAMLInline()
	assert.NotNil(t, rend)
}

func TestSchemaProxy_MarshalYAML_IgnoredCircular(t *testing.T) {
	const ymlComponents = `openapi: 3.1
components:
  schemas:
    dice:
      properties:
        mice:
          anyOf:
            - $ref: '#/components/schemas/nice'
  
    spice:
      properties:
        ice:
          allOf:
            - $ref: '#/components/schemas/dice'
    nice:
      allOf:
        - $ref: '#/components/schemas/spice'
      properties:
        rice:
          oneOf: 
            - $ref: '#/components/schemas/nice'`

	idx := func() *index.SpecIndex {
		var idxNode yaml.Node
		err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
		assert.NoError(t, err)

		cfg := index.CreateOpenAPIIndexConfig()
		cfg.IgnoreArrayCircularReferences = true
		cfg.IgnorePolymorphicCircularReferences = true
		return index.NewSpecIndexWithConfig(&idxNode, cfg)
	}()

	resolver := index.NewResolver(idx)
	resolver.CheckForCircularReferences()

	const ymlSchema = `items:
  $ref: '#/components/schemas/nice'`
	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err := lowProxy.Build(context.Background(), &node, node.Content[0], idx)
	ref := low.Reference{}
	ref.SetReference("#/components/schemas/spice", node.Content[0])
	lowProxy.Reference = ref

	assert.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		KeyNode:   node.Content[0],
		Reference: ref,
	}

	spEmpty := NewSchemaProxy(nil)
	assert.Nil(t, spEmpty.GetSchemaKeyNode())

	sp := NewSchemaProxy(&lowRef)
	assert.NotNil(t, sp.GetSchemaKeyNode())

	rend, _ := sp.MarshalYAMLInline()
	assert.NotNil(t, rend)
}

func TestSchemaProxy_MarshalYAML_MatchBasePath(t *testing.T) {
	const ymlComponents = `properties:
  spice:
    allOf:
      - $ref: '#/properties/rice'
  rice:
    oneOf: 
      - $ref: './schema.yaml'`

	_ = os.WriteFile("schema.yaml", []byte(ymlComponents), 0o777)
	defer os.RemoveAll("schema.yaml")

	actualYaml := []byte("$ref: 'schema.yaml'")

	cwd, _ := os.Getwd()
	basePath := cwd

	// create an index config
	config := index.CreateOpenAPIIndexConfig()
	rolodex := index.NewRolodex(config)

	fsCfg := &index.LocalFSConfig{
		BaseDirectory: basePath,
		IndexConfig:   config,
	}

	fileFS, err := index.NewLocalFSWithConfig(fsCfg)
	if err != nil {
		panic(err)
	}

	var rootNode yaml.Node
	_ = yaml.Unmarshal(actualYaml, &rootNode)

	rolodex.SetRootNode(&rootNode)

	rolodex.AddLocalFS(basePath, fileFS)

	indexingError := rolodex.IndexTheRolodex(context.Background())
	if indexingError != nil {
		panic(indexingError)
	}

	rolodex.Resolve()

	// there should be no errors at this point
	resolvingErrors := rolodex.GetCaughtErrors()
	if resolvingErrors != nil {
		panic(resolvingErrors)
	}

	lowProxy := new(lowbase.SchemaProxy)
	err = lowProxy.Build(context.Background(), &rootNode, rootNode.Content[0], rolodex.GetRootIndex())
	assert.NoError(t, err)
	ref := low.Reference{}

	ref.SetReference("#/properties/spice", rootNode.Content[0])
	lowProxy.Reference = ref
	lowProxy.GetIndex().SetAbsolutePath(filepath.Join(basePath, "schema.yaml"))

	assert.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		KeyNode:   rootNode.Content[0],
		Reference: ref,
	}

	spEmpty := NewSchemaProxy(nil)
	assert.Nil(t, spEmpty.GetSchemaKeyNode())
	sp := NewSchemaProxy(&lowRef)

	assert.NotNil(t, sp.GetSchemaKeyNode())

	rend, _ := sp.MarshalYAMLInline()
	assert.NotNil(t, rend)
}

func TestSchemaProxy_MarshalYAML_StripBasePath(t *testing.T) {
	const ymlComponents = `properties:
  spice:
    allOf:
      - $ref: '#/properties/rice'
  rice:
    oneOf: 
      - $ref: './schema_n.yaml'`

	_ = os.WriteFile("schema_n.yaml", []byte(ymlComponents), 0o777)
	defer os.RemoveAll("schema_n.yaml")

	actualYaml := []byte("$ref: './schema_n.yaml'")

	cwd, _ := os.Getwd()
	basePath := cwd

	// create an index config
	config := index.CreateOpenAPIIndexConfig()
	rolodex := index.NewRolodex(config)

	fsCfg := &index.LocalFSConfig{
		BaseDirectory: basePath,
		IndexConfig:   config,
	}

	fileFS, err := index.NewLocalFSWithConfig(fsCfg)
	if err != nil {
		panic(err)
	}

	var rootNode yaml.Node
	_ = yaml.Unmarshal(actualYaml, &rootNode)

	rolodex.SetRootNode(&rootNode)

	rolodex.AddLocalFS(basePath, fileFS)

	indexingError := rolodex.IndexTheRolodex(context.Background())
	if indexingError != nil {
		panic(indexingError)
	}

	// there should be no errors at this point
	resolvingErrors := rolodex.GetCaughtErrors()
	if resolvingErrors != nil {
		panic(resolvingErrors)
	}

	lowProxy := new(lowbase.SchemaProxy)
	err = lowProxy.Build(context.Background(), &rootNode, rootNode.Content[0], rolodex.GetRootIndex())
	assert.NoError(t, err)
	ref := low.Reference{}

	assert.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		KeyNode:   rootNode.Content[0],
		Reference: ref,
	}

	sp := NewSchemaProxy(&lowRef)

	assert.NotNil(t, sp.GetSchemaKeyNode())

	ref.SetReference("./schema_n.yaml", rootNode.Content[0])
	lowProxy.Reference = ref

	rend, _ := sp.MarshalYAMLInline()
	assert.NotNil(t, rend)

	// should not have rendered and should be the same as the input
	// check by hashing.
	assert.Equal(t, index.HashNode(rootNode.Content[0]), index.HashNode(rend.(*yaml.Node)))
}

func TestSchemaProxy_MarshalYAML_BadSchema(t *testing.T) {
	actualYaml := []byte("$ref: './schema_k.yaml'")

	var rootNode yaml.Node
	_ = yaml.Unmarshal(actualYaml, &rootNode)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		KeyNode: rootNode.Content[0],
	}

	sp := NewSchemaProxy(&lowRef)
	rend, err := sp.MarshalYAMLInline()
	assert.Nil(t, rend)
	assert.Error(t, err)
}

func TestSchemaProxy_MarshalYAML_Inline_HTTP(t *testing.T) {
	// this triggers http code by fudging references, found when importing from URLs directly.

	first := `type: object
properties:
  cakes:
    type: array
    items: 
      $ref: 'http#/properties/cakes'`

	var rootNode yaml.Node
	_ = yaml.Unmarshal([]byte(first), &rootNode)

	cf := index.CreateOpenAPIIndexConfig()
	cf.SkipDocumentCheck = true

	rolodex := index.NewRolodex(cf)
	rolodex.SetRootNode(&rootNode)
	rErr := rolodex.IndexTheRolodex(context.Background())

	assert.NoError(t, rErr)

	circularRefs := []*index.CircularReferenceResult{
		{
			LoopPoint: &index.Reference{
				Definition:     "#/components/schemas/Ten",
				FullDefinition: "http#/properties/cakes",
			},
		},
	}

	rolodex.GetRootIndex().SetCircularReferences(circularRefs)

	lowProxy := new(lowbase.SchemaProxy)
	err := lowProxy.Build(context.Background(), nil, rootNode.Content[0], rolodex.GetRootIndex())
	assert.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value: lowProxy,
	}

	sp := NewSchemaProxy(&lowRef)

	rend, _ := sp.Schema().Properties.GetOrZero("cakes").Schema().Items.A.MarshalYAMLInline()
	assert.NotNil(t, rend)
}

func TestSchemaProxy_ConcurrentCacheAccess(t *testing.T) {
	// Create schema that will be cached
	const ymlComponents = `components:
  schemas:
    TestSchema:
      type: object`

	var idxNode yaml.Node
	err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
	assert.NoError(t, err)
	idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())

	// Create multiple proxies that will share the same cache entry
	proxies := make([]*SchemaProxy, 10)
	for i := range proxies {
		const ymlSchema = `$ref: '#/components/schemas/TestSchema'`
		var node yaml.Node
		yaml.Unmarshal([]byte(ymlSchema), &node)

		lowProxy := new(lowbase.SchemaProxy)
		lowProxy.Build(context.Background(), nil, node.Content[0], idx)

		proxies[i] = NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
			Value: lowProxy, ValueNode: node.Content[0],
		})
	}

	// Trigger race by having all proxies access Schema() simultaneously
	var wg sync.WaitGroup
	for _, proxy := range proxies {
		wg.Add(1)
		go func(p *SchemaProxy) {
			defer wg.Done()
			schema := p.Schema() // This should trigger the race with old code
			assert.NotNil(t, schema)
			// Check if ParentProxy is set - with our fix, cached schemas may not have it
			if schema.ParentProxy == nil {
				t.Logf("Warning: Schema ParentProxy is nil for cached schema")
			}
		}(proxy)
	}
	wg.Wait()
}

func TestSchemaProxy_ParentProxyPreservedForCachedSchemas(t *testing.T) {
	const ymlComponents = `components:
  schemas:
    TestSchema:
      type: object
      properties:
        name:
          type: string`

	var idxNode yaml.Node
	err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
	assert.NoError(t, err)
	idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())

	const ymlSchema = `$ref: '#/components/schemas/TestSchema'`
	var node1 yaml.Node
	yaml.Unmarshal([]byte(ymlSchema), &node1)

	lowProxy1 := new(lowbase.SchemaProxy)
	lowProxy1.Build(context.Background(), nil, node1.Content[0], idx)

	proxy1 := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value: lowProxy1, ValueNode: node1.Content[0],
	})

	schema1 := proxy1.Schema()
	assert.NotNil(t, schema1)
	assert.Equal(t, proxy1, schema1.ParentProxy, "First schema should have correct ParentProxy")

	var node2 yaml.Node
	yaml.Unmarshal([]byte(ymlSchema), &node2)

	lowProxy2 := new(lowbase.SchemaProxy)
	lowProxy2.Build(context.Background(), nil, node2.Content[0], idx)

	proxy2 := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value: lowProxy2, ValueNode: node2.Content[0],
	})

	schema2 := proxy2.Schema()
	assert.NotNil(t, schema2)
	assert.Equal(t, proxy2, schema2.ParentProxy, "Second schema should have its own ParentProxy, not the first proxy's")
	assert.NotEqual(t, schema1.ParentProxy, schema2.ParentProxy, "Different proxies should have different parent relationships")
}

func TestSetBundlingMode(t *testing.T) {
	// first, reset to known state by decrementing until we hit 0
	// this handles any leftover state from parallel tests
	for IsBundlingMode() {
		SetBundlingMode(false)
	}
	assert.False(t, IsBundlingMode(), "Bundling mode should be false initially")

	// toggle on
	SetBundlingMode(true)
	assert.True(t, IsBundlingMode(), "Bundling mode should be true after setting")

	// toggle off
	SetBundlingMode(false)
	assert.False(t, IsBundlingMode(), "Bundling mode should be false after unsetting")

	// test multiple increments (nested bundling)
	SetBundlingMode(true)
	SetBundlingMode(true)
	assert.True(t, IsBundlingMode(), "Bundling mode should be true with count=2")

	SetBundlingMode(false)
	assert.True(t, IsBundlingMode(), "Bundling mode should still be true with count=1")

	SetBundlingMode(false)
	assert.False(t, IsBundlingMode(), "Bundling mode should be false with count=0")
}

func TestInlineRenderContext_MarkRefAsPreserved(t *testing.T) {
	ctx := NewInlineRenderContext()

	// initially ref should not be marked as preserved
	assert.False(t, ctx.ShouldPreserveRef("#/components/schemas/Pet"))

	// mark the ref as preserved
	ctx.MarkRefAsPreserved("#/components/schemas/Pet")

	// now it should be preserved
	assert.True(t, ctx.ShouldPreserveRef("#/components/schemas/Pet"))

	// different ref should not be preserved
	assert.False(t, ctx.ShouldPreserveRef("#/components/schemas/Other"))
}

func TestInlineRenderContext_ShouldPreserveRef_EmptyString(t *testing.T) {
	ctx := NewInlineRenderContext()

	// empty string should not be preserved
	assert.False(t, ctx.ShouldPreserveRef(""))

	// marking empty string should be a no-op
	ctx.MarkRefAsPreserved("")
	assert.False(t, ctx.ShouldPreserveRef(""))
}

func TestInlineRenderContext_PreservedRefs_Concurrent(t *testing.T) {
	ctx := NewInlineRenderContext()

	// test concurrent access to preservedRefs
	done := make(chan bool)
	for i := 0; i < 10; i++ {
		go func(n int) {
			ref := fmt.Sprintf("#/components/schemas/Schema%d", n)
			ctx.MarkRefAsPreserved(ref)
			_ = ctx.ShouldPreserveRef(ref)
			done <- true
		}(i)
	}

	// wait for all goroutines
	for i := 0; i < 10; i++ {
		<-done
	}

	// verify all refs were preserved
	for i := 0; i < 10; i++ {
		ref := fmt.Sprintf("#/components/schemas/Schema%d", i)
		assert.True(t, ctx.ShouldPreserveRef(ref))
	}
}

func TestMarkRefAsPreserved(t *testing.T) {
	ctx := NewInlineRenderContext()
	ctx.MarkRefAsPreserved("#/components/schemas/Pet")

	proxy := CreateSchemaProxyRef("#/components/schemas/Pet")

	// MarshalYAMLInlineWithContext should return ref node when ref is marked as preserved
	result, err := proxy.MarshalYAMLInlineWithContext(ctx)
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	// should render as $ref
	assert.Equal(t, yaml.MappingNode, node.Kind)
	assert.Equal(t, 2, len(node.Content))
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Pet", node.Content[1].Value)
}

func TestMarkRefAsPreserved_WithBundlingMode(t *testing.T) {
	// first, reset to known state
	for IsBundlingMode() {
		SetBundlingMode(false)
	}

	// test interaction with bundling mode
	SetBundlingMode(true)
	defer SetBundlingMode(false)

	proxy := CreateSchemaProxyRef("#/components/schemas/Test")

	// without marking ref as preserved, should still render as ref in bundling mode via MarshalYAML
	result, err := proxy.MarshalYAML()
	require.NoError(t, err)
	node := result.(*yaml.Node)
	assert.Equal(t, "$ref", node.Content[0].Value)
}

func TestMarkRefAsPreserved_MarshalYAMLInlineWithContext(t *testing.T) {
	// test that marking ref as preserved affects MarshalYAMLInlineWithContext behavior
	ctx := NewInlineRenderContext()
	ctx.MarkRefAsPreserved("#/components/schemas/Pet")

	proxy := CreateSchemaProxyRef("#/components/schemas/Pet")

	// MarshalYAMLInlineWithContext should return ref node when ref is marked as preserved
	result, err := proxy.MarshalYAMLInlineWithContext(ctx)
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	// should render as $ref
	assert.Equal(t, yaml.MappingNode, node.Kind)
	assert.Equal(t, 2, len(node.Content))
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Pet", node.Content[1].Value)
}

func TestMarkRefAsPreserved_RefNotMarked(t *testing.T) {
	// test that unmarked refs are NOT preserved and attempt to inline
	ctx := NewInlineRenderContext()
	// mark a DIFFERENT ref as preserved
	ctx.MarkRefAsPreserved("#/components/schemas/Other")

	proxy := CreateSchemaProxyRef("#/components/schemas/Test")

	// MarshalYAMLInlineWithContext should attempt to inline when ref is not marked as preserved.
	// since this proxy has no backing schema, it returns an error - this confirms the
	// context-based preservation check is working (if it were preserved, we'd get ref node back)
	result, err := proxy.MarshalYAMLInlineWithContext(ctx)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "unable to render schema")
	assert.Nil(t, result)
}

func TestMarshalYAMLInline_BundlingMode_PreservesLocalComponentRefs(t *testing.T) {
	// Test that in bundling mode, local #/components/ refs pointing to schemas
	// in the same root document are preserved (not inlined).
	// This covers lines 356-373 in schema_proxy.go

	// Reset bundling mode state
	for IsBundlingMode() {
		SetBundlingMode(false)
	}

	// Create a document with components
	const ymlComponents = `components:
  schemas:
    Pet:
      type: object
      properties:
        name:
          type: string
    Dog:
      type: object
      properties:
        pet:
          $ref: '#/components/schemas/Pet'`

	var idxNode yaml.Node
	err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
	require.NoError(t, err)

	// Create spec index and rolodex
	cfg := index.CreateOpenAPIIndexConfig()
	idx := index.NewSpecIndexWithConfig(&idxNode, cfg)

	// Create a rolodex and set it up - use SetRootIndex to make this the root
	rolodex := index.NewRolodex(cfg)
	rolodex.SetRootNode(&idxNode)
	rolodex.SetRootIndex(idx)
	idx.SetRolodex(rolodex)

	// Build a schema proxy referencing #/components/schemas/Pet
	const ref = "#/components/schemas/Pet"
	const ymlSchema = `$ref: '` + ref + `'`
	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err = lowProxy.Build(context.Background(), nil, node.Content[0], idx)
	require.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: node.Content[0],
	}

	sp := NewSchemaProxy(&lowRef)

	// Enable bundling mode
	SetBundlingMode(true)
	defer SetBundlingMode(false)

	// MarshalYAMLInline should preserve the ref since the schema is in the root index
	result, err := sp.MarshalYAMLInline()
	require.NoError(t, err)

	resultNode, ok := result.(*yaml.Node)
	require.True(t, ok)

	// Should render as $ref
	assert.Equal(t, yaml.MappingNode, resultNode.Kind)
	require.GreaterOrEqual(t, len(resultNode.Content), 2)
	assert.Equal(t, "$ref", resultNode.Content[0].Value)
	assert.Equal(t, ref, resultNode.Content[1].Value)
}

func TestMarshalYAMLInline_CircularReferenceDetection_WithReference(t *testing.T) {
	// Test that circular reference detection returns the ref node and error
	// when a reference proxy is already being rendered within the same context.
	// This covers lines 421-425 in schema_proxy.go

	// Reset bundling mode state
	for IsBundlingMode() {
		SetBundlingMode(false)
	}

	// Create a schema proxy with a refStr to generate a render key
	ref := "#/components/schemas/CircularTest"
	proxy := &SchemaProxy{
		refStr: ref,
		lock:   &sync.Mutex{},
	}

	// Pre-load the render key to simulate being mid-render (cycle detected)
	renderKey := proxy.getInlineRenderKey()
	require.NotEmpty(t, renderKey, "render key should be generated from refStr")

	// Create context and store the key to simulate a cycle
	ctx := NewInlineRenderContext()
	ctx.StartRendering(renderKey)

	// MarshalYAMLInlineWithContext should detect the cycle and return ref node + error
	result, err := proxy.MarshalYAMLInlineWithContext(ctx)

	// Should return an error about circular reference
	require.Error(t, err)
	assert.Contains(t, err.Error(), "circular reference")
	assert.Contains(t, err.Error(), ref)

	// Result should be a ref node (fallback for reference proxy)
	resultNode, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, yaml.MappingNode, resultNode.Kind)
	assert.Equal(t, "$ref", resultNode.Content[0].Value)
}

func TestMarshalYAMLInline_CircularReferenceDetection_WithoutReference(t *testing.T) {
	// Test that circular reference detection returns an empty map node and error
	// when an inline (non-reference) proxy is already being rendered within the same context.
	// This covers lines 427-429 in schema_proxy.go

	// Reset bundling mode state
	for IsBundlingMode() {
		SetBundlingMode(false)
	}

	// Create an inline schema proxy (no refStr, with value node for position)
	valueNode := &yaml.Node{
		Kind:   yaml.MappingNode,
		Line:   10,
		Column: 5,
	}

	lowProxy := &lowbase.SchemaProxy{}

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: valueNode,
	}

	proxy := NewSchemaProxy(&lowRef)

	// Get the render key (should be position-based for inline schemas)
	renderKey := proxy.getInlineRenderKey()
	require.NotEmpty(t, renderKey, "render key should be generated from node position")

	// Create context and store the key to simulate a cycle
	ctx := NewInlineRenderContext()
	ctx.StartRendering(renderKey)

	// MarshalYAMLInlineWithContext should detect the cycle and return empty map + error
	result, err := proxy.MarshalYAMLInlineWithContext(ctx)

	// Should return an error about circular reference
	require.Error(t, err)
	assert.Contains(t, err.Error(), "circular reference")
	assert.Contains(t, err.Error(), "inline rendering")

	// Result should be an empty mapping node (fallback for inline schemas)
	resultNode, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, yaml.MappingNode, resultNode.Kind)
	assert.Equal(t, "!!map", resultNode.Tag)
}

func TestGetInlineRenderKey_ReferenceWithoutIndex(t *testing.T) {
	// Test line 324: return ref when IsReference() is true but index is nil
	// This covers the path where we have a reference but no index to get the path from
	// Need schema.Value to be non-nil but GetIndex() to return nil

	// Create a low-level proxy that is a reference but has no index
	lowProxy := &lowbase.SchemaProxy{}
	lowProxy.SetReference("#/components/schemas/TestSchema", nil)
	// Don't call Build() so index stays nil

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value: lowProxy,
	}

	proxy := &SchemaProxy{
		schema: &lowRef,
		lock:   &sync.Mutex{},
	}

	renderKey := proxy.getInlineRenderKey()

	// Should return just the ref since there's no index
	assert.Equal(t, "#/components/schemas/TestSchema", renderKey)
}

func TestGetInlineRenderKey_NilSchemaReturnsRefStr(t *testing.T) {
	// Test line 312: return refStr when schema is nil
	// This covers the early return path

	proxy := &SchemaProxy{
		refStr: "#/components/schemas/EarlyReturn",
		lock:   &sync.Mutex{},
	}

	renderKey := proxy.getInlineRenderKey()

	// Should return refStr via early return path
	assert.Equal(t, "#/components/schemas/EarlyReturn", renderKey)
}

func TestGetInlineRenderKey_NilSchemaValueReturnsRefStr(t *testing.T) {
	// Test line 312: return refStr when schema.Value is nil
	// This covers the early return path

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value: nil, // nil value
	}

	proxy := &SchemaProxy{
		refStr: "#/components/schemas/AnotherEarlyReturn",
		schema: &lowRef,
		lock:   &sync.Mutex{},
	}

	renderKey := proxy.getInlineRenderKey()

	// Should return refStr via early return path since schema.Value is nil
	assert.Equal(t, "#/components/schemas/AnotherEarlyReturn", renderKey)
}

func TestGetInlineRenderKey_InlineNodeWithoutPosition_NoIndex(t *testing.T) {
	// Nodes created via yaml.Node.Encode() will have line/column set to zero.
	// The render key should fall back to a pointer-based key to avoid collisions.

	valueNode1 := &yaml.Node{Kind: yaml.MappingNode}
	valueNode2 := &yaml.Node{Kind: yaml.MappingNode}

	lowProxy1 := &lowbase.SchemaProxy{}
	lowProxy2 := &lowbase.SchemaProxy{}

	lowRef1 := low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy1,
		ValueNode: valueNode1,
	}
	lowRef2 := low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy2,
		ValueNode: valueNode2,
	}

	proxy1 := NewSchemaProxy(&lowRef1)
	proxy2 := NewSchemaProxy(&lowRef2)

	renderKey1 := proxy1.getInlineRenderKey()
	renderKey2 := proxy2.getInlineRenderKey()

	require.NotEmpty(t, renderKey1)
	require.NotEmpty(t, renderKey2)
	assert.Contains(t, renderKey1, "inline:")
	assert.Contains(t, renderKey2, "inline:")
	assert.NotEqual(t, "inline:0:0", renderKey1)
	assert.NotEqual(t, "inline:0:0", renderKey2)
	assert.NotEqual(t, renderKey1, renderKey2)
}

func TestGetInlineRenderKey_InlineNodeWithoutPosition_WithIndex(t *testing.T) {
	// When an index is present and line/column are missing, include the index path
	// and use a pointer-based key for uniqueness.

	valueNode := &yaml.Node{Kind: yaml.MappingNode}
	lowProxy := &lowbase.SchemaProxy{}

	idx := &index.SpecIndex{}
	idx.SetAbsolutePath("/tmp/spec.yaml")

	err := lowProxy.Build(context.Background(), nil, valueNode, idx)
	require.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: valueNode,
	}

	proxy := NewSchemaProxy(&lowRef)
	renderKey := proxy.getInlineRenderKey()

	require.NotEmpty(t, renderKey)
	assert.True(t, strings.HasPrefix(renderKey, idx.GetSpecAbsolutePath()+":inline:"))
	assert.NotEqual(t, idx.GetSpecAbsolutePath()+":0:0", renderKey)
}

func TestMarshalYAMLInlineWithContext_PreserveReference_ViaLowLevel(t *testing.T) {
	// test context-based ref preservation when reference is set via low-level proxy
	// (refStr is empty, so GetReferenceNode uses low-level path)

	lowProxy := &lowbase.SchemaProxy{}
	lowProxy.SetReference("#/components/schemas/TestRef", nil)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value: lowProxy,
	}

	proxy := &SchemaProxy{
		schema: &lowRef,
		lock:   &sync.Mutex{},
	}

	// create context and mark the ref as preserved
	ctx := NewInlineRenderContext()
	ctx.MarkRefAsPreserved("#/components/schemas/TestRef")

	result, err := proxy.MarshalYAMLInlineWithContext(ctx)
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, yaml.MappingNode, node.Kind)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/TestRef", node.Content[1].Value)
}

func TestMarshalYAMLInline_BundlingMode_ViaLowLevelRef(t *testing.T) {
	// Test bundling mode preserves refs when schema is in root index
	// Reference is set via low-level proxy (not refStr)

	// Reset bundling mode state
	for IsBundlingMode() {
		SetBundlingMode(false)
	}

	// Create a minimal spec with components
	const ymlComponents = `components:
  schemas:
    TestSchema:
      type: object`

	var idxNode yaml.Node
	err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
	require.NoError(t, err)

	cfg := index.CreateOpenAPIIndexConfig()
	idx := index.NewSpecIndexWithConfig(&idxNode, cfg)

	// Create rolodex and set as root
	rolodex := index.NewRolodex(cfg)
	rolodex.SetRootNode(&idxNode)
	rolodex.SetRootIndex(idx)
	idx.SetRolodex(rolodex)

	// Create a low-level proxy using Build with the ref
	const ref = "#/components/schemas/TestSchema"
	const ymlRef = `$ref: '` + ref + `'`
	var refNode yaml.Node
	_ = yaml.Unmarshal([]byte(ymlRef), &refNode)

	lowProxy := &lowbase.SchemaProxy{}
	err = lowProxy.Build(context.Background(), nil, refNode.Content[0], idx)
	require.NoError(t, err)

	lowRef := low.NodeReference[*lowbase.SchemaProxy]{
		Value: lowProxy,
	}

	proxy := &SchemaProxy{
		schema: &lowRef,
		lock:   &sync.Mutex{},
	}

	// Enable bundling mode
	SetBundlingMode(true)
	defer SetBundlingMode(false)

	result, err := proxy.MarshalYAMLInline()
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, yaml.MappingNode, node.Kind)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/TestSchema", node.Content[1].Value)
}

// InlineRenderContext tests

func TestInlineRenderContext_NewContext(t *testing.T) {
	ctx := NewInlineRenderContext()
	assert.NotNil(t, ctx)
}

func TestInlineRenderContext_StartRendering_EmptyKey(t *testing.T) {
	ctx := NewInlineRenderContext()
	// Empty key should return false (no cycle)
	assert.False(t, ctx.StartRendering(""))
}

func TestInlineRenderContext_CycleDetection(t *testing.T) {
	ctx := NewInlineRenderContext()

	// First call - no cycle
	assert.False(t, ctx.StartRendering("file.yaml:#/Pet"))

	// Second call same key - cycle detected
	assert.True(t, ctx.StartRendering("file.yaml:#/Pet"))

	// Stop rendering
	ctx.StopRendering("file.yaml:#/Pet")

	// Can start again after stopping
	assert.False(t, ctx.StartRendering("file.yaml:#/Pet"))
}

func TestInlineRenderContext_Isolation(t *testing.T) {
	ctx1 := NewInlineRenderContext()
	ctx2 := NewInlineRenderContext()

	// Start in ctx1
	ctx1.StartRendering("key1")

	// ctx2 should NOT see ctx1's key
	assert.False(t, ctx2.StartRendering("key1"))
}

func TestInlineRenderContext_DifferentKeys(t *testing.T) {
	ctx := NewInlineRenderContext()

	ctx.StartRendering("file1.yaml:#/Pet")
	// Different key should not trigger cycle
	assert.False(t, ctx.StartRendering("file2.yaml:#/Pet"))
}

func TestInlineRenderContext_StopRendering_EmptyKey(t *testing.T) {
	ctx := NewInlineRenderContext()
	// Should not panic on empty key
	ctx.StopRendering("")
}

func TestSchemaProxy_MarshalYAMLInlineWithContext_Basic(t *testing.T) {
	// Test basic rendering with context
	const ymlComponents = `components:
    schemas:
     rice:
       type: string`

	idx := func() *index.SpecIndex {
		var idxNode yaml.Node
		err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
		assert.NoError(t, err)
		return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
	}()

	const ymlSchema = `type: object
properties:
  name:
    type: string`

	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err := lowProxy.Build(context.Background(), nil, node.Content[0], idx)
	require.NoError(t, err)

	highProxy := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: node.Content[0],
	})

	ctx := NewInlineRenderContext()
	result, err := highProxy.MarshalYAMLInlineWithContext(ctx)

	assert.NoError(t, err)
	assert.NotNil(t, result)
}

func TestSchemaProxy_MarshalYAMLInlineWithContext_Concurrent_NoFalsePositives(t *testing.T) {
	// Test that concurrent rendering with separate contexts doesn't cause false positive cycles
	const ymlComponents = `components:
    schemas:
     Pet:
       type: object
       properties:
         name:
           type: string`

	idx := func() *index.SpecIndex {
		var idxNode yaml.Node
		err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
		assert.NoError(t, err)
		return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
	}()

	const ymlSchema = `$ref: '#/components/schemas/Pet'`
	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err := lowProxy.Build(context.Background(), nil, node.Content[0], idx)
	require.NoError(t, err)

	highProxy := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: node.Content[0],
	})

	// Run concurrent renders with separate contexts
	var wg sync.WaitGroup
	errorCount := 0
	var mu sync.Mutex

	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			ctx := NewInlineRenderContext()
			_, err := highProxy.MarshalYAMLInlineWithContext(ctx)
			if err != nil && strings.Contains(err.Error(), "circular reference") {
				mu.Lock()
				errorCount++
				mu.Unlock()
			}
		}()
	}

	wg.Wait()

	// Should have NO false positive circular reference errors
	assert.Equal(t, 0, errorCount, "Should not have false positive circular reference errors")
}

func TestSchemaProxy_MarshalYAMLInlineWithContext_WrongContextType(t *testing.T) {
	// Test the fallback branch when wrong context type is passed
	// The function should fall back to creating a fresh InlineRenderContext
	const ymlComponents = `components:
    schemas:
     Pet:
       type: object
       properties:
         name:
           type: string`

	idx := func() *index.SpecIndex {
		var idxNode yaml.Node
		err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
		assert.NoError(t, err)
		return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
	}()

	const ymlSchema = `type: object
properties:
  name:
    type: string`

	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err := lowProxy.Build(context.Background(), nil, node.Content[0], idx)
	require.NoError(t, err)

	highProxy := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: node.Content[0],
	})

	// Pass wrong context type (not *InlineRenderContext)
	// Should fall back to creating fresh context
	wrongCtx := struct{ foo string }{foo: "bar"}
	result, err := highProxy.MarshalYAMLInlineWithContext(wrongCtx)

	assert.NoError(t, err)
	assert.NotNil(t, result)
}

func TestSchemaProxy_MarshalYAMLInlineWithContext_NilContext(t *testing.T) {
	// Test when nil is passed as context
	const ymlComponents = `components:
    schemas:
     Pet:
       type: object`

	idx := func() *index.SpecIndex {
		var idxNode yaml.Node
		err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
		assert.NoError(t, err)
		return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
	}()

	const ymlSchema = `type: string`

	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err := lowProxy.Build(context.Background(), nil, node.Content[0], idx)
	require.NoError(t, err)

	highProxy := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: node.Content[0],
	})

	// Pass nil - should fall back to creating fresh context
	result, err := highProxy.MarshalYAMLInlineWithContext(nil)

	assert.NoError(t, err)
	assert.NotNil(t, result)
}

// RenderingMode tests

func TestRenderingMode_Constants(t *testing.T) {
	// Verify the constants have expected values (iota order)
	assert.Equal(t, RenderingMode(0), RenderingModeBundle)
	assert.Equal(t, RenderingMode(1), RenderingModeValidation)
}

func TestNewInlineRenderContextForValidation(t *testing.T) {
	ctx := NewInlineRenderContextForValidation()
	assert.NotNil(t, ctx)
	assert.Equal(t, RenderingModeValidation, ctx.Mode)
}

func TestNewInlineRenderContext_DefaultMode(t *testing.T) {
	ctx := NewInlineRenderContext()
	assert.NotNil(t, ctx)
	assert.Equal(t, RenderingModeBundle, ctx.Mode)
}

func TestInlineRenderContext_ModePreservedDuringOperations(t *testing.T) {
	// Verify mode is preserved when using start/stop rendering
	ctx := NewInlineRenderContextForValidation()

	ctx.StartRendering("test-key")
	assert.Equal(t, RenderingModeValidation, ctx.Mode)

	ctx.StopRendering("test-key")
	assert.Equal(t, RenderingModeValidation, ctx.Mode)
}

func TestClearInlineRenderingTracker(t *testing.T) {
	// Store a value.
	inlineRenderingTracker.Store("test-key", true)

	// Verify it's there.
	_, ok := inlineRenderingTracker.Load("test-key")
	assert.True(t, ok)

	// Clear and verify it's gone.
	ClearInlineRenderingTracker()
	_, ok = inlineRenderingTracker.Load("test-key")
	assert.False(t, ok)

	// Idempotent: clearing an empty map should not panic.
	ClearInlineRenderingTracker()
}

// --- Tests for CreateSchemaProxyRefWithSchema ---

func TestCreateSchemaProxyRefWithSchema(t *testing.T) {
	schema := &Schema{Description: "A pet with extra context"}
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", schema)

	assert.True(t, sp.IsReference())
	assert.Equal(t, "#/components/schemas/Pet", sp.GetReference())
	assert.Equal(t, schema, sp.Schema())
	assert.NotNil(t, sp.GetReferenceNode())
	assert.Nil(t, sp.GoLow())
}

func TestCreateSchemaProxyRefWithSchema_Render(t *testing.T) {
	schema := &Schema{
		Description: "A pet with extra context",
		Type:        []string{"object"},
	}
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", schema)

	result, err := sp.MarshalYAML()
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, yaml.MappingNode, node.Kind)

	// $ref should be the first key
	require.GreaterOrEqual(t, len(node.Content), 2)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Pet", node.Content[1].Value)

	// Render to YAML bytes to verify full output
	rendered, err := sp.Render()
	require.NoError(t, err)
	renderedStr := string(rendered)
	assert.Contains(t, renderedStr, "$ref")
	assert.Contains(t, renderedStr, "#/components/schemas/Pet")
	assert.Contains(t, renderedStr, "description: A pet with extra context")
}

func TestCreateSchemaProxyRefWithSchema_NilSchema(t *testing.T) {
	// When schema is nil, behaves like CreateSchemaProxyRef
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", nil)

	assert.True(t, sp.IsReference())
	assert.Equal(t, "#/components/schemas/Pet", sp.GetReference())
	assert.Nil(t, sp.Schema())
	assert.False(t, sp.isRefWithSiblings())

	// MarshalYAML should produce ref-only output
	result, err := sp.MarshalYAML()
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, 2, len(node.Content))
	assert.Equal(t, "$ref", node.Content[0].Value)
}

func TestCreateSchemaProxyRefWithSchema_RoundTrip(t *testing.T) {
	schema := &Schema{
		Description: "Round trip test",
	}
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Example", schema)

	rendered, err := sp.Render()
	require.NoError(t, err)

	// Unmarshal and verify both $ref and sibling properties are present
	var result map[string]interface{}
	err = yaml.Unmarshal(rendered, &result)
	require.NoError(t, err)
	assert.Equal(t, "#/components/schemas/Example", result["$ref"])
	assert.Equal(t, "Round trip test", result["description"])
}

func TestCreateSchemaProxyRefWithSchema_InlineRender(t *testing.T) {
	schema := &Schema{
		Description: "Inline test",
	}
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", schema)

	result, err := sp.MarshalYAMLInline()
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, yaml.MappingNode, node.Kind)

	// Should have $ref as first key and description
	require.GreaterOrEqual(t, len(node.Content), 4)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Pet", node.Content[1].Value)
}

func TestCreateSchemaProxyRefWithSchema_InlinePreservedRef(t *testing.T) {
	schema := &Schema{
		Description: "Preserved ref test",
	}
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", schema)

	ctx := NewInlineRenderContext()
	ctx.MarkRefAsPreserved("#/components/schemas/Pet")

	result, err := sp.MarshalYAMLInlineWithContext(ctx)
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, yaml.MappingNode, node.Kind)

	// Even with preservation, siblings should still be present because
	// refNode() returns renderRefWithSiblings() for ref+siblings proxies
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Pet", node.Content[1].Value)
	require.GreaterOrEqual(t, len(node.Content), 4) // $ref key+val + description key+val
}

func TestSchemaProxy_MarshalYAMLInline_CircularReference_MatchesAbsoluteBasePath(t *testing.T) {
	const ymlComponents = `components:
  schemas:
    Ten:
      type: object`

	var idxNode yaml.Node
	err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
	require.NoError(t, err)

	idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
	idx.SetAbsolutePath(filepath.Join(t.TempDir(), "spec.yaml"))
	idx.SetCircularReferences([]*index.CircularReferenceResult{
		{
			LoopPoint: &index.Reference{
				Definition:     "#/components/schemas/NotTen",
				FullDefinition: idx.GetSpecAbsolutePath(),
			},
		},
	})

	refNode := utils.CreateRefNode("#/components/schemas/Ten")

	lowProxy := new(lowbase.SchemaProxy)
	err = lowProxy.Build(context.Background(), nil, refNode, idx)
	require.NoError(t, err)

	sp := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: refNode,
	})

	rendered, err := sp.MarshalYAMLInline()
	require.Error(t, err)
	require.NotNil(t, rendered)
	assert.Contains(t, err.Error(), "circular reference")

	node, ok := rendered.(*yaml.Node)
	require.True(t, ok)
	require.Len(t, node.Content, 2)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Ten", node.Content[1].Value)
}

func TestCreateSchemaProxyRefWithSchema_CircularRefSafe(t *testing.T) {
	// Verify inline rendering doesn't panic when rendered Schema has a non-nil low-level
	const ymlComponents = `components:
  schemas:
    Pet:
      type: object
      properties:
        name:
          type: string`

	var idxNode yaml.Node
	err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
	require.NoError(t, err)
	idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())

	// Build a real schema from the spec
	const ymlSchema = `type: object
properties:
  name:
    type: string`
	var node yaml.Node
	_ = yaml.Unmarshal([]byte(ymlSchema), &node)

	lowProxy := new(lowbase.SchemaProxy)
	err = lowProxy.Build(context.Background(), nil, node.Content[0], idx)
	require.NoError(t, err)

	parsedProxy := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: node.Content[0],
	})
	parsedSchema := parsedProxy.Schema()
	require.NotNil(t, parsedSchema)

	// Create ref+siblings proxy using the parsed schema (which has non-nil low)
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", parsedSchema)

	// MarshalYAML should not panic
	result, err := sp.MarshalYAML()
	require.NoError(t, err)
	yamlNode := result.(*yaml.Node)
	assert.Equal(t, "$ref", yamlNode.Content[0].Value)
}

func TestCreateSchemaProxyRefWithSchema_NilLowInline(t *testing.T) {
	// Plain high-level schema with low == nil must not panic during inline rendering
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Test", &Schema{Description: "test"})

	// MarshalYAMLInline must not panic (the schema.go:628 nil-deref path is avoided)
	result, err := sp.MarshalYAMLInline()
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, yaml.MappingNode, node.Kind)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Test", node.Content[1].Value)
}

func TestCreateSchemaProxyRefWithSchema_CycleDetection(t *testing.T) {
	// Verify the refNode() closure is used when a cycle is detected for a ref+siblings proxy
	schema := &Schema{Description: "cyclic sibling"}
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", schema)

	ctx := NewInlineRenderContext()
	// Pre-mark the ref as being rendered to simulate a cycle
	ctx.StartRendering("#/components/schemas/Pet")

	result, err := sp.MarshalYAMLInlineWithContext(ctx)
	require.Error(t, err)
	assert.Contains(t, err.Error(), "circular reference")

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	// Should include siblings in the ref node via refNode() -> renderRefWithSiblings()
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Pet", node.Content[1].Value)
	// Description sibling must be present
	rendered, renderErr := yaml.Marshal(node)
	require.NoError(t, renderErr)
	assert.Contains(t, string(rendered), "description: cyclic sibling")
}

func TestCreateSchemaProxyRefWithSchema_BundlingMode(t *testing.T) {
	// Verify ref+siblings proxy works in bundling mode without panic.
	// Since there is no low-level backing, the bundling mode check passes through.
	for IsBundlingMode() {
		SetBundlingMode(false)
	}
	SetBundlingMode(true)
	defer SetBundlingMode(false)

	schema := &Schema{Description: "bundled sibling"}
	sp := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", schema)

	result, err := sp.MarshalYAMLInline()
	require.NoError(t, err)

	node, ok := result.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Pet", node.Content[1].Value)
	rendered, renderErr := yaml.Marshal(node)
	require.NoError(t, renderErr)
	assert.Contains(t, string(rendered), "description: bundled sibling")
}

func TestSchemaProxy_RenderTransformedRefWithSiblings(t *testing.T) {
	deprecated := true
	lowProxy := &lowbase.SchemaProxy{
		TransformedRef: &yaml.Node{
			Kind: yaml.MappingNode,
			Tag:  "!!map",
			Content: []*yaml.Node{
				nil,
				utils.CreateStringNode("ignored"),
				utils.CreateStringNode("$ref"),
				nil,
				utils.CreateStringNode("description"),
				utils.CreateStringNode("original description"),
			},
		},
	}
	sp := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: lowProxy})
	schema := &Schema{
		AllOf: []*SchemaProxy{
			CreateSchemaProxy(&Schema{
				Description: "updated description",
				Title:       "added title",
				Deprecated:  &deprecated,
			}),
			CreateSchemaProxyRef("#/components/schemas/Thing"),
		},
	}

	node, ok, err := sp.renderTransformedRefWithSiblings(schema)
	require.NoError(t, err)
	require.True(t, ok)
	require.NotNil(t, node)
	require.Len(t, node.Content, 8)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Thing", node.Content[1].Value)
	assert.Equal(t, "description", node.Content[2].Value)
	assert.Equal(t, "updated description", node.Content[3].Value)
	assert.Equal(t, "title", node.Content[4].Value)
	assert.Equal(t, "added title", node.Content[5].Value)
	assert.Equal(t, "deprecated", node.Content[6].Value)
	assert.Equal(t, "true", node.Content[7].Value)
}

func TestSchemaProxy_MarshalYAML_TransformedRefWithSiblings(t *testing.T) {
	lowProxy := &lowbase.SchemaProxy{
		TransformedRef: &yaml.Node{
			Kind: yaml.MappingNode,
			Tag:  "!!map",
			Content: []*yaml.Node{
				utils.CreateStringNode("$ref"),
				utils.CreateStringNode("#/components/schemas/Thing"),
				utils.CreateStringNode("description"),
				utils.CreateStringNode("original description"),
			},
		},
	}
	sp := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: lowProxy})
	schema := &Schema{
		ParentProxy: sp,
		AllOf: []*SchemaProxy{
			CreateSchemaProxy(&Schema{Description: "updated description"}),
			CreateSchemaProxyRef("#/components/schemas/Thing"),
		},
	}
	sp.rendered = schema

	rendered, err := sp.MarshalYAML()
	require.NoError(t, err)
	node, ok := rendered.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Thing", node.Content[1].Value)
	assert.Equal(t, "description", node.Content[2].Value)
	assert.Equal(t, "updated description", node.Content[3].Value)

	rendered, err = schema.MarshalYAML()
	require.NoError(t, err)
	node, ok = rendered.(*yaml.Node)
	require.True(t, ok)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Thing", node.Content[1].Value)
	assert.Equal(t, "description", node.Content[2].Value)
	assert.Equal(t, "updated description", node.Content[3].Value)
}

func TestSchemaProxy_ParsedTransformedRefWithSiblingsRestoresReferenceContract(t *testing.T) {
	sp := buildParsedSiblingRefProxy(t, 3.1)

	assert.True(t, sp.isParsedRefWithSiblings())
	assert.True(t, sp.IsReference())
	assert.Equal(t, "#/components/schemas/Thing", sp.GetReference())
	require.NotNil(t, sp.GetReferenceNode())
	assert.Equal(t, "$ref", sp.GetReferenceNode().Content[0].Value)

	schema := sp.Schema()
	require.NotNil(t, schema)
	assert.Equal(t, "original description", schema.Description)
	assert.Equal(t, "Original title", schema.Title)
	assert.Empty(t, schema.AllOf)
	assert.Equal(t, sp, schema.ParentProxy)

	schema.Description = "updated description"
	rendered, err := sp.MarshalYAML()
	require.NoError(t, err)
	node, ok := rendered.(*yaml.Node)
	require.True(t, ok)
	require.Len(t, node.Content, 6)
	assert.Equal(t, "$ref", node.Content[0].Value)
	assert.Equal(t, "#/components/schemas/Thing", node.Content[1].Value)
	assert.Equal(t, "description", node.Content[2].Value)
	assert.Equal(t, "updated description", node.Content[3].Value)
	assert.Equal(t, "title", node.Content[4].Value)
	assert.Equal(t, "Original title", node.Content[5].Value)

	sp.schema.ValueNode = &yaml.Node{Kind: yaml.MappingNode}
	assert.Contains(t, sp.getInlineRenderKey(), ":inline:")
}

func TestSchemaProxy_ParsedTransformedRefWithSiblingsInlinePreservesSemantics(t *testing.T) {
	sp := buildParsedSiblingRefProxy(t, 3.1)

	siblingSchema := sp.Schema()
	require.NotNil(t, siblingSchema)
	siblingSchema.Description = "mutated description"

	semanticSchema, err := sp.BuildTransformedRefSemanticSchema(siblingSchema)
	require.NoError(t, err)
	require.NotNil(t, semanticSchema)
	require.Len(t, semanticSchema.AllOf, 2)
	assert.Equal(t, "mutated description", semanticSchema.AllOf[0].Schema().Description)
	assert.Equal(t, "string", semanticSchema.AllOf[1].Schema().Type[0])

	semanticSchema, err = sp.BuildTransformedRefSemanticSchema(nil)
	require.NoError(t, err)
	require.NotNil(t, semanticSchema)
	require.Len(t, semanticSchema.AllOf, 2)
	assert.Equal(t, "mutated description", semanticSchema.AllOf[0].Schema().Description)

	rendered, err := sp.MarshalYAMLInline()
	require.NoError(t, err)
	node, ok := rendered.(*yaml.Node)
	require.True(t, ok)

	out, err := yaml.Marshal(node)
	require.NoError(t, err)
	assert.Contains(t, string(out), "allOf:")
	assert.Contains(t, string(out), "description: mutated description")
	assert.NotContains(t, string(out), "description: original description")
	assert.Contains(t, string(out), "type: string")

	inlineFromSchema, err := siblingSchema.MarshalYAMLInline()
	require.NoError(t, err)
	inlineNode, ok := inlineFromSchema.(*yaml.Node)
	require.True(t, ok)
	inlineOut, err := yaml.Marshal(inlineNode)
	require.NoError(t, err)
	assert.Contains(t, string(inlineOut), "allOf:")
	assert.Contains(t, string(inlineOut), "description: mutated description")
	assert.Contains(t, string(inlineOut), "type: string")
}

func TestSchemaProxy_ParsedTransformedRefWithSiblingsJSONPreservesReference(t *testing.T) {
	sp := buildParsedSiblingRefProxy(t, 3.1)
	schema := sp.Schema()
	require.NotNil(t, schema)
	schema.Description = "json description"

	jsonBytes, err := schema.MarshalJSON()
	require.NoError(t, err)
	assert.JSONEq(t, `{
		"$ref": "#/components/schemas/Thing",
		"description": "json description",
		"title": "Original title"
	}`, string(jsonBytes))

	inlineBytes, err := schema.MarshalJSONInline()
	require.NoError(t, err)
	assert.Contains(t, string(inlineBytes), `"allOf"`)
	assert.Contains(t, string(inlineBytes), `"description":"json description"`)
	assert.Contains(t, string(inlineBytes), `"type":"string"`)
}

func TestSchemaProxy_ParsedTransformedRefWithSiblingsJSONReturnsErrors(t *testing.T) {
	sp := buildParsedSiblingRefProxy(t, 3.1)
	semanticSchema, err := sp.BuildTransformedRefSemanticSchema(sp.Schema())
	require.NoError(t, err)
	require.NotNil(t, semanticSchema)
	require.Len(t, semanticSchema.AllOf, 2)
	semanticSchema.ParentProxy = sp
	semanticSchema.AllOf[0] = &SchemaProxy{buildError: errors.New("boom")}

	jsonBytes, err := semanticSchema.MarshalJSON()
	require.Error(t, err)
	assert.Nil(t, jsonBytes)

	inlineBytes, err := semanticSchema.MarshalJSONInline()
	require.Error(t, err)
	assert.Nil(t, inlineBytes)
}

func TestSchemaProxy_ParsedTransformedRefWithSiblingsOpenAPI30KeepsAllOfContract(t *testing.T) {
	sp := buildParsedSiblingRefProxy(t, 3.0)

	assert.False(t, sp.isParsedRefWithSiblings())
	assert.False(t, sp.IsReference())
	assert.Empty(t, sp.GetReference())

	schema := sp.Schema()
	require.NotNil(t, schema)
	require.Len(t, schema.AllOf, 2)
	assert.Equal(t, "original description", schema.AllOf[0].Schema().Description)
	assert.True(t, schema.AllOf[1].IsReference())
	assert.Equal(t, "#/components/schemas/Thing", schema.AllOf[1].GetReference())
}

func TestSchemaProxy_ParsedTransformedRefWithSiblingsFallbacks(t *testing.T) {
	assert.False(t, (*SchemaProxy)(nil).isParsedRefWithSiblings())
	assert.Nil(t, (*SchemaProxy)(nil).buildSiblingOnlySchemaView())
	assert.False(t, (*SchemaProxy)(nil).schemaIsTransformedSiblingView(&Schema{}))
	synthetic, err := (*SchemaProxy)(nil).buildSemanticAllOfSchemaView(nil)
	require.NoError(t, err)
	assert.Nil(t, synthetic)

	empty := &SchemaProxy{}
	assert.False(t, empty.isParsedRefWithSiblings())
	assert.Nil(t, empty.GetReferenceNode())
	assert.Empty(t, empty.GetReference())
	_, err = empty.marshalParsedRefWithSiblingsInline(NewInlineRenderContext(), nil)
	require.Error(t, err)

	lowProxyWithoutSiblingNode := &lowbase.SchemaProxy{TransformedRef: utils.CreateRefNode("#/components/schemas/Thing")}
	withoutSiblingNode := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: lowProxyWithoutSiblingNode})
	assert.Nil(t, withoutSiblingNode.buildSiblingOnlySchemaView())

	emptyLowProxy := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: &lowbase.SchemaProxy{}})
	synthetic, err = emptyLowProxy.buildSemanticAllOfSchemaView(nil)
	require.Error(t, err)
	assert.Nil(t, synthetic)
	_, err = emptyLowProxy.marshalParsedRefWithSiblingsInline(NewInlineRenderContext(), nil)
	require.Error(t, err)

	invalid := buildInvalidParsedSiblingRefProxy(t)
	assert.Nil(t, invalid.Schema())
	require.Error(t, invalid.GetBuildError())
	node, err := invalid.referenceYAMLNode()
	require.Error(t, err)
	assert.Nil(t, node)
}

func TestSchemaProxy_RenderTransformedRefWithSiblingsFallbacks(t *testing.T) {
	deprecated := true
	lowProxy := &lowbase.SchemaProxy{
		TransformedRef: &yaml.Node{
			Kind: yaml.MappingNode,
			Tag:  "!!map",
			Content: []*yaml.Node{
				utils.CreateStringNode("$ref"),
				utils.CreateStringNode("#/components/schemas/Thing"),
				utils.CreateStringNode("deprecated"),
				utils.CreateBoolNode("true"),
			},
		},
	}
	sp := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: lowProxy})

	node, ok, err := (*SchemaProxy)(nil).renderTransformedRefWithSiblings(&Schema{})
	require.NoError(t, err)
	assert.False(t, ok)
	assert.Nil(t, node)

	node, ok, err = sp.renderTransformedRefWithSiblings(&Schema{})
	require.NoError(t, err)
	assert.False(t, ok)
	assert.Nil(t, node)

	node, ok, err = sp.renderTransformedRefWithSiblings(&Schema{
		Description: "outer mutation",
		AllOf: []*SchemaProxy{
			CreateSchemaProxy(&Schema{Deprecated: &deprecated}),
			CreateSchemaProxyRef("#/components/schemas/Thing"),
		},
	})
	require.NoError(t, err)
	assert.False(t, ok)
	assert.Nil(t, node)

	badSibling := &SchemaProxy{buildError: errors.New("boom")}
	node, ok, err = sp.renderTransformedRefWithSiblings(&Schema{
		AllOf: []*SchemaProxy{
			badSibling,
			CreateSchemaProxyRef("#/components/schemas/Thing"),
		},
	})
	require.Error(t, err)
	assert.True(t, ok)
	assert.Nil(t, node)

	scalarRefLowProxy := &lowbase.SchemaProxy{}
	scalarRefLowProxy.SetReference("#/components/schemas/Scalar", utils.CreateStringNode("#/components/schemas/Scalar"))
	node, ok, err = sp.renderTransformedRefWithSiblings(&Schema{
		AllOf: []*SchemaProxy{
			NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: scalarRefLowProxy}),
			CreateSchemaProxyRef("#/components/schemas/Thing"),
		},
	})
	require.NoError(t, err)
	assert.False(t, ok)
	assert.Nil(t, node)
}

func buildParsedSiblingRefProxy(t *testing.T, version float32) *SchemaProxy {
	t.Helper()

	spec := `openapi: 3.1.0
paths: {}
components:
  schemas:
    Thing:
      type: string
    Holder:
      type: object
      properties:
        thing:
          $ref: '#/components/schemas/Thing'
          description: original description
          title: Original title
`

	var root yaml.Node
	require.NoError(t, yaml.Unmarshal([]byte(spec), &root))
	rootNode := root.Content[0]

	cfg := index.CreateOpenAPIIndexConfig()
	cfg.SpecInfo = &datamodel.SpecInfo{VersionNumeric: version}
	cfg.TransformSiblingRefs = true
	idx := index.NewSpecIndexWithConfig(rootNode, cfg)

	refNode := findHighSchemaTestNode(t, rootNode, "components", "schemas", "Holder", "properties", "thing")
	lowProxy := new(lowbase.SchemaProxy)
	require.NoError(t, lowProxy.Build(context.Background(), nil, refNode, idx))
	require.NotNil(t, lowProxy.TransformedRef)

	return NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: refNode,
	})
}

func buildInvalidParsedSiblingRefProxy(t *testing.T) *SchemaProxy {
	t.Helper()

	spec := `openapi: 3.1.0
paths: {}
components:
  schemas:
    Thing:
      type: string
    Holder:
      type: object
      properties:
        thing:
          $ref: '#/components/schemas/Thing'
          dependentRequired:
            bad: nope
`

	var root yaml.Node
	require.NoError(t, yaml.Unmarshal([]byte(spec), &root))
	rootNode := root.Content[0]

	cfg := index.CreateOpenAPIIndexConfig()
	cfg.SpecInfo = &datamodel.SpecInfo{VersionNumeric: 3.1}
	cfg.TransformSiblingRefs = true
	idx := index.NewSpecIndexWithConfig(rootNode, cfg)

	refNode := findHighSchemaTestNode(t, rootNode, "components", "schemas", "Holder", "properties", "thing")
	lowProxy := new(lowbase.SchemaProxy)
	require.NoError(t, lowProxy.Build(context.Background(), nil, refNode, idx))

	return NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{
		Value:     lowProxy,
		ValueNode: refNode,
	})
}

func findHighSchemaTestNode(t *testing.T, node *yaml.Node, path ...string) *yaml.Node {
	t.Helper()

	current := node
	for _, key := range path {
		require.NotNil(t, current)
		require.Equal(t, yaml.MappingNode, current.Kind)
		var next *yaml.Node
		for i := 0; i+1 < len(current.Content); i += 2 {
			if current.Content[i].Value == key {
				next = current.Content[i+1]
				break
			}
		}
		require.NotNilf(t, next, "missing path key %q", key)
		current = next
	}
	return current
}

func TestSchemaProxy_RenderTransformedRefWithSiblings_OpenAPI30Fallback(t *testing.T) {
	var root yaml.Node
	require.NoError(t, yaml.Unmarshal([]byte("$ref: '#/components/schemas/Thing'\nminLength: 2"), &root))

	cfg := index.CreateOpenAPIIndexConfig()
	cfg.SpecInfo = &datamodel.SpecInfo{VersionNumeric: 3.0}
	cfg.TransformSiblingRefs = true
	idx := index.NewSpecIndexWithConfig(&root, cfg)

	lowProxy := new(lowbase.SchemaProxy)
	require.NoError(t, lowProxy.Build(context.Background(), nil, root.Content[0], idx))
	require.NotNil(t, lowProxy.TransformedRef)

	minLength := int64(2)
	sp := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: lowProxy})
	schema := &Schema{
		AllOf: []*SchemaProxy{
			CreateSchemaProxy(&Schema{MinLength: &minLength}),
			CreateSchemaProxyRef("#/components/schemas/Thing"),
		},
	}

	node, ok, err := sp.renderTransformedRefWithSiblings(schema)
	require.NoError(t, err)
	assert.False(t, ok)
	assert.Nil(t, node)
	assert.False(t, sp.shouldCollapseTransformedRefWithSiblings())
}

func TestSchemaProxy_ShouldCollapseTransformedRefWithSiblings(t *testing.T) {
	require.False(t, (*SchemaProxy)(nil).shouldCollapseTransformedRefWithSiblings())

	lowProxy := &lowbase.SchemaProxy{}
	noIndex := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: lowProxy})
	require.True(t, noIndex.shouldCollapseTransformedRefWithSiblings())

	var root yaml.Node
	require.NoError(t, yaml.Unmarshal([]byte("type: string"), &root))

	cfg30 := index.CreateOpenAPIIndexConfig()
	cfg30.SpecInfo = &datamodel.SpecInfo{VersionNumeric: 3.0}
	idx30 := index.NewSpecIndexWithConfig(&root, cfg30)
	low30 := new(lowbase.SchemaProxy)
	require.NoError(t, low30.Build(context.Background(), nil, root.Content[0], idx30))
	assert.False(t, NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: low30}).shouldCollapseTransformedRefWithSiblings())

	cfg31 := index.CreateOpenAPIIndexConfig()
	cfg31.SpecInfo = &datamodel.SpecInfo{VersionNumeric: 3.1}
	idx31 := index.NewSpecIndexWithConfig(&root, cfg31)
	low31 := new(lowbase.SchemaProxy)
	require.NoError(t, low31.Build(context.Background(), nil, root.Content[0], idx31))
	assert.True(t, NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{Value: low31}).shouldCollapseTransformedRefWithSiblings())
}

func TestTransformedRefRenderHelpers(t *testing.T) {
	node := utils.CreateStringNode("value")
	ptrNode, ok := yamlNodeFromRender(node)
	require.True(t, ok)
	assert.Equal(t, node, ptrNode)

	valueNode, ok := yamlNodeFromRender(*node)
	require.True(t, ok)
	assert.Equal(t, "value", valueNode.Value)

	noNode, ok := yamlNodeFromRender("nope")
	assert.False(t, ok)
	assert.Nil(t, noNode)

	key, value := findYAMLPair(nil, "description")
	assert.Nil(t, key)
	assert.Nil(t, value)

	key, value = findYAMLPair(utils.CreateStringNode("scalar"), "description")
	assert.Nil(t, key)
	assert.Nil(t, value)

	mapping := &yaml.Node{
		Kind: yaml.MappingNode,
		Tag:  "!!map",
		Content: []*yaml.Node{
			utils.CreateStringNode("description"),
			utils.CreateStringNode("hello"),
		},
	}
	key, value = findYAMLPair(mapping, "description")
	require.NotNil(t, key)
	require.NotNil(t, value)
	assert.Equal(t, "hello", value.Value)

	key, value = findYAMLPair(mapping, "title")
	assert.Nil(t, key)
	assert.Nil(t, value)

	assert.Nil(t, cloneYAMLNode(nil))
	cloned := cloneYAMLNode(mapping)
	require.NotNil(t, cloned)
	assert.Equal(t, mapping.Content[0].Value, cloned.Content[0].Value)
	cloned.Content[0].Value = "changed"
	assert.Equal(t, "description", mapping.Content[0].Value)
}

func TestCreateSchemaProxyRefWithSchema_IsRefWithSiblings(t *testing.T) {
	// Verify isRefWithSiblings() returns correct values for all factory types
	refOnly := CreateSchemaProxyRef("#/components/schemas/Pet")
	assert.False(t, refOnly.isRefWithSiblings())

	schemaOnly := CreateSchemaProxy(&Schema{Description: "test"})
	assert.False(t, schemaOnly.isRefWithSiblings())

	refWithSchema := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", &Schema{Description: "test"})
	assert.True(t, refWithSchema.isRefWithSiblings())

	refWithNilSchema := CreateSchemaProxyRefWithSchema("#/components/schemas/Pet", nil)
	assert.False(t, refWithNilSchema.isRefWithSiblings())
}
