package project_test

import (
	"context"
	"strings"
	"testing"

	"efprojects.com/kitten-ipc/kitcom/internal/tsgo/bundled"
	"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ls"
	"efprojects.com/kitten-ipc/kitcom/internal/tsgo/lsp/lsproto"
	"efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/projecttestutil"
	"gotest.tools/v3/assert"
)

func TestUntitledReferences(t *testing.T) {
	t.Parallel()
	if !bundled.Embedded {
		t.Skip("bundled files are not embedded")
	}

	// First test the URI conversion functions to understand the issue
	untitledURI := lsproto.DocumentUri("untitled:Untitled-2")
	convertedFileName := untitledURI.FileName()
	t.Logf("URI '%s' converts to filename '%s'", untitledURI, convertedFileName)

	backToURI := ls.FileNameToDocumentURI(convertedFileName)
	t.Logf("Filename '%s' converts back to URI '%s'", convertedFileName, backToURI)

	if string(backToURI) != string(untitledURI) {
		t.Errorf("Round-trip conversion failed: '%s' -> '%s' -> '%s'", untitledURI, convertedFileName, backToURI)
	}

	// Create a test case that simulates how untitled files should work
	testContent := `let x = 42;

x

x++;`

	// Use the converted filename that DocumentURIToFileName would produce
	untitledFileName := convertedFileName // "^/untitled/ts-nul-authority/Untitled-2"
	t.Logf("Would use untitled filename: %s", untitledFileName)

	// Set up the file system with an untitled file -
	// But use a regular file first to see the current behavior
	files := map[string]any{
		"/Untitled-2.ts": testContent,
	}

	session, _ := projecttestutil.Setup(files)

	ctx := projecttestutil.WithRequestID(context.Background())
	session.DidOpenFile(ctx, "file:///Untitled-2.ts", 1, testContent, lsproto.LanguageKindTypeScript)

	// Get language service
	languageService, err := session.GetLanguageService(ctx, "file:///Untitled-2.ts")
	assert.NilError(t, err)

	// Test the filename that the source file reports
	program := languageService.GetProgram()
	sourceFile := program.GetSourceFile("/Untitled-2.ts")
	t.Logf("SourceFile.FileName() returns: '%s'", sourceFile.FileName())

	// Call ProvideReferences using the LSP method
	uri := lsproto.DocumentUri("file:///Untitled-2.ts")
	lspPosition := lsproto.Position{Line: 2, Character: 0} // Line 3, character 1 (0-indexed)

	refParams := &lsproto.ReferenceParams{
		TextDocument: lsproto.TextDocumentIdentifier{Uri: uri},
		Position:     lspPosition,
		Context:      &lsproto.ReferenceContext{IncludeDeclaration: true},
	}

	resp, err := languageService.ProvideReferences(ctx, refParams)
	assert.NilError(t, err)

	refs := *resp.Locations

	// Log the results
	t.Logf("Input file URI: %s", uri)
	t.Logf("Number of references found: %d", len(refs))
	for i, ref := range refs {
		t.Logf("Reference %d: URI=%s, Range=%+v", i+1, ref.Uri, ref.Range)
	}

	// We expect to find 3 references
	assert.Assert(t, len(refs) == 3, "Expected 3 references, got %d", len(refs))

	// Also test definition using ProvideDefinition
	definition, err := languageService.ProvideDefinition(ctx, uri, lspPosition)
	assert.NilError(t, err)
	if definition.Locations != nil {
		t.Logf("Definition found: %d locations", len(*definition.Locations))
		for i, loc := range *definition.Locations {
			t.Logf("Definition %d: URI=%s, Range=%+v", i+1, loc.Uri, loc.Range)
		}
	}
}

func TestUntitledFileInInferredProject(t *testing.T) {
	t.Parallel()
	if !bundled.Embedded {
		t.Skip("bundled files are not embedded")
	}

	// Test that untitled files are properly handled in inferred projects
	testContent := `let x = 42;

x

x++;`

	session, _ := projecttestutil.Setup(map[string]any{})

	ctx := projecttestutil.WithRequestID(context.Background())

	// Open untitled files - these should create an inferred project
	session.DidOpenFile(ctx, "untitled:Untitled-1", 1, "x\n\n", lsproto.LanguageKindTypeScript)
	session.DidOpenFile(ctx, "untitled:Untitled-2", 1, testContent, lsproto.LanguageKindTypeScript)

	snapshot, release := session.Snapshot()
	defer release()

	// Should have an inferred project
	assert.Assert(t, snapshot.ProjectCollection.InferredProject() != nil)

	// Get language service for the untitled file
	languageService, err := session.GetLanguageService(ctx, "untitled:Untitled-2")
	assert.NilError(t, err)

	program := languageService.GetProgram()
	untitledFileName := lsproto.DocumentUri("untitled:Untitled-2").FileName()
	sourceFile := program.GetSourceFile(untitledFileName)
	assert.Assert(t, sourceFile != nil)
	assert.Equal(t, sourceFile.Text(), testContent)

	// Test references on 'x' at position 13 (line 3, after "let x = 42;\n\n")
	uri := lsproto.DocumentUri("untitled:Untitled-2")
	lspPosition := lsproto.Position{Line: 2, Character: 0} // Line 3, character 1 (0-indexed)

	refParams := &lsproto.ReferenceParams{
		TextDocument: lsproto.TextDocumentIdentifier{Uri: uri},
		Position:     lspPosition,
		Context:      &lsproto.ReferenceContext{IncludeDeclaration: true},
	}

	resp, err := languageService.ProvideReferences(ctx, refParams)
	assert.NilError(t, err)

	refs := *resp.Locations
	t.Logf("Number of references found: %d", len(refs))
	for i, ref := range refs {
		t.Logf("Reference %d: URI=%s, Range=%+v", i+1, ref.Uri, ref.Range)
		// All URIs should be untitled: URIs, not file: URIs
		assert.Assert(t, strings.HasPrefix(string(ref.Uri), "untitled:"),
			"Expected untitled: URI, got %s", ref.Uri)
	}

	// We expect to find 4 references
	assert.Assert(t, len(refs) == 4, "Expected 4 references, got %d", len(refs))
}
