package ls

import (
	"context"
	"testing"

	"github.com/microsoft/typescript-go/internal/ast"
	"github.com/microsoft/typescript-go/internal/core"
	"github.com/microsoft/typescript-go/internal/ls/lsutil"
	"github.com/microsoft/typescript-go/internal/parser"
)

// Test for issue: Panic Handling textDocument/onTypeFormatting
// This reproduces the panic when pressing enter in an empty file
func TestGetFormattingEditsAfterKeystroke_EmptyFile(t *testing.T) {
	t.Parallel()
	// Create an empty file
	text := ""
	sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
		FileName: "/index.ts",
		Path:     "/index.ts",
	}, text, core.ScriptKindTS)

	// Create language service with nil program (we're only testing the formatting function)
	langService := &LanguageService{}

	// Test formatting after keystroke with newline character at position 0
	ctx := context.Background()
	options := lsutil.GetDefaultFormatCodeSettings()

	// This should not panic
	edits := langService.getFormattingEditsAfterKeystroke(
		ctx,
		sourceFile,
		options,
		0, // position
		"\n",
	)

	// Should return nil or empty edits, not panic
	_ = edits
}

// Test with a simple statement
func TestGetFormattingEditsAfterKeystroke_SimpleStatement(t *testing.T) {
	t.Parallel()
	// Create a file with a simple statement
	text := "const x = 1"
	sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
		FileName: "/index.ts",
		Path:     "/index.ts",
	}, text, core.ScriptKindTS)

	// Create language service with nil program
	langService := &LanguageService{}

	// Test formatting after keystroke with newline character at end of statement
	ctx := context.Background()
	options := lsutil.GetDefaultFormatCodeSettings()

	// This should not panic
	edits := langService.getFormattingEditsAfterKeystroke(
		ctx,
		sourceFile,
		options,
		len(text), // position at end of file
		"\n",
	)

	// Should return nil or empty edits, not panic
	_ = edits
}

// Test for issue: Crash in range formatting when requested on a line that is different from the containing function
// This reproduces the panic when formatting a range inside a function body
func TestGetFormattingEditsForRange_FunctionBody(t *testing.T) {
	t.Parallel()

	testCases := []struct {
		name     string
		text     string
		startPos int
		endPos   int
	}{
		{
			name:     "return statement in function",
			text:     "function foo() {\n    return (1  + 2);\n}",
			startPos: 21, // Start of "return"
			endPos:   38, // End of ");"
		},
		{
			name:     "function with newline after keyword",
			text:     "function\nf() {\n}",
			startPos: 9,  // After "function\n"
			endPos:   13, // Inside or after function
		},
		{
			name:     "empty function body",
			text:     "function f() {\n  \n}",
			startPos: 15, // Inside body
			endPos:   17, // Inside body
		},
		{
			name:     "after function closing brace",
			text:     "function f() {\n}",
			startPos: 15, // After closing brace
			endPos:   15,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
				FileName: "/test.ts",
				Path:     "/test.ts",
			}, tc.text, core.ScriptKindTS)

			langService := &LanguageService{}
			ctx := context.Background()
			options := lsutil.GetDefaultFormatCodeSettings()

			// This should not panic
			edits := langService.getFormattingEditsForRange(
				ctx,
				sourceFile,
				options,
				core.NewTextRange(tc.startPos, tc.endPos),
			)

			// Should not panic
			_ = edits // Just ensuring no panic
		})
	}
}
