package integration

import (
	"io/ioutil"
	"testing"

	"github.com/marte-dev/marte-dev-tools/internal/index"
	"github.com/marte-dev/marte-dev-tools/internal/parser"
	"github.com/marte-dev/marte-dev-tools/internal/validator"
)

// Helper to load and parse a file
func loadConfig(t *testing.T, filename string) *parser.Configuration {
	content, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatalf("Failed to read %s: %v", filename, err)
	}
	p := parser.NewParser(string(content))
	config, err := p.Parse()
	if err != nil {
		t.Fatalf("Parse failed: %v", err)
	}
	return config
}

func TestLSPDiagnostics(t *testing.T) {
	inputFile := "integration/check_dup.marte"
	config := loadConfig(t, inputFile)
	
	// Simulate LSP logic: Build Index -> Validate
	idx := index.NewProjectTree()
	idx.AddFile(inputFile, config)
	
	v := validator.NewValidator(idx, ".")
	v.ValidateProject()
	
	// Check for expected diagnostics
	found := false
	for _, d := range v.Diagnostics {
		if d.Message == "Duplicate Field Definition: 'Field' is already defined in integration/check_dup.marte" {
			found = true
			if d.Position.Line != 5 {
				t.Errorf("Expected diagnostic at line 5, got %d", d.Position.Line)
			}
			break
		}
	}
	if !found {
		t.Error("LSP Diagnostic for duplicate field not found")
	}
}

// For GoToDefinition and References, we need to test the Indexer's ability to resolve symbols.
// Currently, my Indexer (ProjectTree) stores structure but doesn't explicitly track 
// "references" in a way that maps a source position to a target symbol yet.
// The ProjectTree is built for structure merging.
// To support LSP "Go To Definition", we need to map usage -> definition.

// Let's verify what we have implemented.
// `internal/index/index.go`:
//   ProjectTree has Fragments.
//   It does NOT have a "Lookup(position)" method or a reference map.
//   Previously (before rewrite), `index.go` had `References []Reference`.
//   I removed it during the rewrite to ProjectTree!

// I need to re-implement reference tracking in `ProjectTree` or a parallel structure 
// to support LSP features.
func TestLSPDefinition(t *testing.T) {
	// Create a virtual file content with a definition and a reference
	content := `
+MyObject = {
    Class = Type
}
+RefObject = {
    Class = Type
    RefField = MyObject
}
`
	p := parser.NewParser(content)
	config, err := p.Parse()
	if err != nil {
		t.Fatalf("Parse failed: %v", err)
	}

	idx := index.NewProjectTree()
	idx.AddFile("memory.marte", config)
	idx.ResolveReferences()

	// Find the reference to "MyObject"
	var foundRef *index.Reference
	for _, ref := range idx.References {
		if ref.Name == "MyObject" {
			foundRef = &ref
			break
		}
	}
	
	if foundRef == nil {
		t.Fatal("Reference to MyObject not found in index")
	}
	
	if foundRef.Target == nil {
		t.Fatal("Reference to MyObject was not resolved to a target")
	}
	
	if foundRef.Target.RealName != "+MyObject" {
		t.Errorf("Expected target to be +MyObject, got %s", foundRef.Target.RealName)
	}
}

func TestLSPHover(t *testing.T) {
	content := `
+MyObject = {
    Class = Type
}
`
	p := parser.NewParser(content)
	config, err := p.Parse()
	if err != nil {
		t.Fatalf("Parse failed: %v", err)
	}

	idx := index.NewProjectTree()
	file := "hover.marte"
	idx.AddFile(file, config)
	
	// +MyObject is at line 2.
	// Query at line 2, col 2 (on 'M' of MyObject)
	res := idx.Query(file, 2, 2)
	
	if res == nil {
		t.Fatal("Query returned nil")
	}
	
	if res.Node == nil {
		t.Fatal("Expected Node result")
	}
	
	if res.Node.RealName != "+MyObject" {
		t.Errorf("Expected +MyObject, got %s", res.Node.RealName)
	}
}
