package integration

import (
	"io/ioutil"
	"strings"
	"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"
)

func parseAndAddToIndex(t *testing.T, idx *index.ProjectTree, filePath string) {
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		t.Fatalf("Failed to read %s: %v", filePath, err)
	}

	p := parser.NewParser(string(content))
	config, err := p.Parse()
	if err != nil {
		t.Fatalf("Parse failed for %s: %v", filePath, err)
	}

	idx.AddFile(filePath, config)
}

func TestMultiFileNodeValidation(t *testing.T) {
	idx := index.NewProjectTree()
	parseAndAddToIndex(t, idx, "integration/multifile_valid_1.marte")
	parseAndAddToIndex(t, idx, "integration/multifile_valid_2.marte")

	// Resolving references might be needed if the validator relies on it for merging implicitly
	// But primarily we want to check if the validator sees the merged node.
    // The current implementation of Validator likely iterates over the ProjectTree.
    // If the ProjectTree doesn't merge nodes automatically, the Validator needs to do it.
    // However, the spec says "The build tool, validator, and LSP must merge these definitions".
    // Let's assume the Validator or Index does the merging logic.
	
	v := validator.NewValidator(idx, ".")
	v.ValidateProject()

	// +MyNode is split. 
    // valid_1 has FieldA
    // valid_2 has Class and FieldB
    // If merging works, it should have a Class, so no error about missing Class.

	for _, diag := range v.Diagnostics {
		if strings.Contains(diag.Message, "must contain a 'Class' field") {
			t.Errorf("Unexpected 'Class' field error for +MyNode: %s", diag.Message)
		}
	}
}

func TestMultiFileDuplicateField(t *testing.T) {
	idx := index.NewProjectTree()
	parseAndAddToIndex(t, idx, "integration/multifile_dup_1.marte")
	parseAndAddToIndex(t, idx, "integration/multifile_dup_2.marte")

	v := validator.NewValidator(idx, ".")
	v.ValidateProject()

	foundError := false
	for _, diag := range v.Diagnostics {
		if strings.Contains(diag.Message, "Duplicate Field Definition") && strings.Contains(diag.Message, "FieldX") {
			foundError = true
			break
		}
	}

	if !foundError {
		t.Errorf("Expected duplicate field error for FieldX in +DupNode, but found none")
	}
}

func TestMultiFileReference(t *testing.T) {
	idx := index.NewProjectTree()
	parseAndAddToIndex(t, idx, "integration/multifile_ref_1.marte")
	parseAndAddToIndex(t, idx, "integration/multifile_ref_2.marte")

	idx.ResolveReferences()
    
    // Check if the reference in +SourceNode to TargetNode is resolved.
	v := validator.NewValidator(idx, ".")
	v.ValidateProject()
    
    if len(v.Diagnostics) > 0 {
        // Filter out irrelevant errors
    }
}

func TestHierarchicalPackageMerge(t *testing.T) {
	idx := index.NewProjectTree()
	parseAndAddToIndex(t, idx, "integration/hierarchical_pkg_1.marte")
	parseAndAddToIndex(t, idx, "integration/hierarchical_pkg_2.marte")

	v := validator.NewValidator(idx, ".")
	v.ValidateProject()

	// +MyObj should have Class (from file 1) and FieldX (from file 2).
	// If Class is missing, ValidateProject reports error.
	
	for _, diag := range v.Diagnostics {
		if strings.Contains(diag.Message, "must contain a 'Class' field") {
			t.Errorf("Unexpected 'Class' field error for +MyObj: %s", diag.Message)
		}
	}
	
	// We can also inspect the tree to verify FieldX is there (optional, but good for confidence)
	baseNode := idx.Root.Children["Base"]
	if baseNode == nil {
		t.Fatal("Base node not found")
	}
	objNode := baseNode.Children["MyObj"]
	if objNode == nil {
		t.Fatal("MyObj node not found in Base")
	}
	
	hasFieldX := false
	for _, frag := range objNode.Fragments {
		for _, def := range frag.Definitions {
			if f, ok := def.(*parser.Field); ok && f.Name == "FieldX" {
				hasFieldX = true
			}
		}
	}
	
	if !hasFieldX {
		t.Error("FieldX not found in +MyObj")
	}
}

func TestHierarchicalDuplicate(t *testing.T) {
	idx := index.NewProjectTree()
	parseAndAddToIndex(t, idx, "integration/hierarchical_dup_1.marte")
	parseAndAddToIndex(t, idx, "integration/hierarchical_dup_2.marte")

	v := validator.NewValidator(idx, ".")
	v.ValidateProject()

	foundError := false
	for _, diag := range v.Diagnostics {
		if strings.Contains(diag.Message, "Duplicate Field Definition") && strings.Contains(diag.Message, "FieldY") {
			foundError = true
			break
		}
	}

	if !foundError {
		t.Errorf("Expected duplicate field error for FieldY in +DupObj (hierarchical), but found none")
	}
}

func TestIsolatedFileValidation(t *testing.T) {
	idx := index.NewProjectTree()
    
    // File 1: Has package. Defines SharedClass.
    f1Content := `
#package Proj.Pkg
+SharedObj = { Class = SharedClass }
`
    p1 := parser.NewParser(f1Content)
    c1, _ := p1.Parse()
    idx.AddFile("shared.marte", c1)
    
    // File 2: No package. References SharedObj.
    // Should NOT resolve to SharedObj in shared.marte because iso.marte is isolated.
    f2Content := `
+IsoObj = {
    Class = "MyClass"
    Ref = SharedObj
}
`
    p2 := parser.NewParser(f2Content)
    c2, _ := p2.Parse()
    idx.AddFile("iso.marte", c2)
    
    idx.ResolveReferences()
    
    // Find reference
    var ref *index.Reference
    for i := range idx.References {
        if idx.References[i].File == "iso.marte" && idx.References[i].Name == "SharedObj" {
            ref = &idx.References[i]
            break
        }
    }
    
    if ref == nil {
        t.Fatal("Reference SharedObj not found in index")
    }
    
    if ref.Target != nil {
        t.Errorf("Expected reference in isolated file to be unresolved, but got target in %s", ref.Target.Fragments[0].File)
    }
}
