/*
Copyright © 2025 Benny Powers <web@bennypowers.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lsp_test

import (
	"os"
	"path/filepath"
	"testing"

	"bennypowers.dev/cem/lsp/document"
)

func TestDocument_ScriptTagParsing_ModuleStaticImports(t *testing.T) {
	dm, err := document.NewDocumentManager()
	if err != nil {
		t.Fatalf("Failed to create document manager: %v", err)
	}
	defer dm.Close()

	// Load test fixture
	fixtureDir := "test/fixtures/script-tag-parsing"
	contentBytes, err := os.ReadFile(filepath.Join(fixtureDir, "module-static-imports.html"))
	if err != nil {
		t.Fatalf("Failed to read test fixture: %v", err)
	}
	content := string(contentBytes)

	// Open the document
	doc := dm.OpenDocument("test://test.html", content, 1)

	// Get script tags
	scriptTags := doc.ScriptTags()

	// Should find one script tag
	if len(scriptTags) != 1 {
		t.Fatalf("Expected 1 script tag, got %d", len(scriptTags))
	}

	scriptTag := scriptTags[0]

	// Verify script tag properties
	if scriptTag.Type != "module" {
		t.Errorf("Expected script type 'module', got '%s'", scriptTag.Type)
	}

	if !scriptTag.IsModule {
		t.Error("Expected IsModule to be true")
	}

	// Verify static imports were parsed
	if len(scriptTag.Imports) < 3 {
		t.Errorf("Expected at least 3 static imports, got %d", len(scriptTag.Imports))
		for i, imp := range scriptTag.Imports {
			t.Logf("Import %d: %s", i, imp.ImportPath)
		}
	}

	// Check for expected import paths
	expectedImports := []string{
		"@scope/package/my-card.js",
		"lit",
		"./components/my-button.js",
	}

	foundImports := make(map[string]bool)
	for _, imp := range scriptTag.Imports {
		foundImports[imp.ImportPath] = true
	}

	for _, expected := range expectedImports {
		if !foundImports[expected] {
			t.Errorf("Expected import '%s' not found", expected)
		}
	}

	// Test FindModuleScript functionality
	_, found := doc.FindModuleScript()
	if !found {
		t.Error("Expected to find module script insertion point")
	}

}

func TestDocument_ScriptTagParsing_ModuleDynamicImports(t *testing.T) {
	dm, err := document.NewDocumentManager()
	if err != nil {
		t.Fatalf("Failed to create document manager: %v", err)
	}
	defer dm.Close()

	// Load test fixture
	fixtureDir := "test/fixtures/script-tag-parsing"
	contentBytes, err := os.ReadFile(filepath.Join(fixtureDir, "module-dynamic-imports.html"))
	if err != nil {
		t.Fatalf("Failed to read test fixture: %v", err)
	}
	content := string(contentBytes)

	// Open the document
	doc := dm.OpenDocument("test://test.html", content, 1)

	// Get script tags
	scriptTags := doc.ScriptTags()

	// Should find one script tag
	if len(scriptTags) != 1 {
		t.Fatalf("Expected 1 script tag, got %d", len(scriptTags))
	}

	scriptTag := scriptTags[0]

	// Verify script tag properties
	if scriptTag.Type != "module" {
		t.Errorf("Expected script type 'module', got '%s'", scriptTag.Type)
	}

	if !scriptTag.IsModule {
		t.Error("Expected IsModule to be true")
	}

	// Verify dynamic imports were parsed
	if len(scriptTag.Imports) < 3 {
		t.Errorf("Expected at least 3 dynamic imports, got %d", len(scriptTag.Imports))
		for i, imp := range scriptTag.Imports {
			t.Logf("Import %d: %s", i, imp.ImportPath)
		}
	}

	// Check for expected import paths
	expectedImports := []string{
		"@scope/package/my-card.js",
		"./components/my-button.js",
		"@scope/package/my-icon.js",
	}

	foundImports := make(map[string]bool)
	for _, imp := range scriptTag.Imports {
		foundImports[imp.ImportPath] = true
	}

	for _, expected := range expectedImports {
		if !foundImports[expected] {
			t.Errorf("Expected import '%s' not found", expected)
		}
	}
}

func TestDocument_ScriptTagParsing_NonModuleDynamicImports(t *testing.T) {
	dm, err := document.NewDocumentManager()
	if err != nil {
		t.Fatalf("Failed to create document manager: %v", err)
	}
	defer dm.Close()

	// Load test fixture
	fixtureDir := "test/fixtures/script-tag-parsing"
	contentBytes, err := os.ReadFile(filepath.Join(fixtureDir, "non-module-dynamic-imports.html"))
	if err != nil {
		t.Fatalf("Failed to read test fixture: %v", err)
	}
	content := string(contentBytes)

	// Open the document
	doc := dm.OpenDocument("test://test.html", content, 1)

	// Get script tags
	scriptTags := doc.ScriptTags()

	// Should find one script tag
	if len(scriptTags) != 1 {
		t.Fatalf("Expected 1 script tag, got %d", len(scriptTags))
	}

	scriptTag := scriptTags[0]

	// Verify script tag properties
	if scriptTag.Type != "" {
		t.Errorf("Expected script type '', got '%s'", scriptTag.Type)
	}

	if scriptTag.IsModule {
		t.Error("Expected IsModule to be false for non-module script")
	}

	// Non-module scripts can still have dynamic imports parsed
	if len(scriptTag.Imports) < 2 {
		t.Errorf("Expected at least 2 dynamic imports, got %d", len(scriptTag.Imports))
		for i, imp := range scriptTag.Imports {
			t.Logf("Import %d: %s", i, imp.ImportPath)
		}
	}

	// Check for expected import paths
	expectedImports := []string{
		"@scope/package/my-card.js",
		"./components/my-button.js",
	}

	foundImports := make(map[string]bool)
	for _, imp := range scriptTag.Imports {
		foundImports[imp.ImportPath] = true
	}

	for _, expected := range expectedImports {
		if !foundImports[expected] {
			t.Errorf("Expected import '%s' not found", expected)
		}
	}

	// Test FindModuleScript functionality - should not find any
	_, found := doc.FindModuleScript()
	if found {
		t.Error("Expected NOT to find module script insertion point when no module scripts exist")
	}
}

func TestDocument_ScriptTagParsing_SimpleModuleScript(t *testing.T) {
	dm, err := document.NewDocumentManager()
	if err != nil {
		t.Fatalf("Failed to create document manager: %v", err)
	}
	defer dm.Close()

	// Load test fixture
	fixtureDir := "test/fixtures/script-tag-parsing"
	contentBytes, err := os.ReadFile(filepath.Join(fixtureDir, "simple-module-script.html"))
	if err != nil {
		t.Fatalf("Failed to read test fixture: %v", err)
	}
	content := string(contentBytes)

	// Open the document
	doc := dm.OpenDocument("test://test.html", content, 1)

	// Get script tags
	scriptTags := doc.ScriptTags()

	// Should find one script tag
	if len(scriptTags) != 1 {
		t.Fatalf("Expected 1 script tag, got %d", len(scriptTags))
	}

	scriptTag := scriptTags[0]

	// Verify script tag properties
	if scriptTag.Type != "module" {
		t.Errorf("Expected script type 'module', got '%s'", scriptTag.Type)
	}

	if !scriptTag.IsModule {
		t.Error("Expected IsModule to be true")
	}

	// Verify single import was parsed
	if len(scriptTag.Imports) != 1 {
		t.Errorf("Expected exactly 1 import, got %d", len(scriptTag.Imports))
		for i, imp := range scriptTag.Imports {
			t.Logf("Import %d: %s", i, imp.ImportPath)
		}
	}

	// Check for expected import path
	expectedImport := "@scope/package/my-element.js"
	if len(scriptTag.Imports) > 0 && scriptTag.Imports[0].ImportPath != expectedImport {
		t.Errorf("Expected import '%s', got '%s'", expectedImport, scriptTag.Imports[0].ImportPath)
	}

	// Test FindModuleScript functionality
	_, found := doc.FindModuleScript()
	if !found {
		t.Error("Expected to find module script insertion point")
	}

}

func TestDocument_ScriptTagParsing_FullHtmlWithModule(t *testing.T) {
	dm, err := document.NewDocumentManager()
	if err != nil {
		t.Fatalf("Failed to create document manager: %v", err)
	}
	defer dm.Close()

	// Load test fixture
	fixtureDir := "test/fixtures/script-tag-parsing"
	contentBytes, err := os.ReadFile(filepath.Join(fixtureDir, "full-html-with-module.html"))
	if err != nil {
		t.Fatalf("Failed to read test fixture: %v", err)
	}
	content := string(contentBytes)

	// Open the document
	doc := dm.OpenDocument("test://test.html", content, 1)

	// Get script tags
	scriptTags := doc.ScriptTags()

	// Should find one script tag
	if len(scriptTags) != 1 {
		t.Fatalf("Expected 1 script tag, got %d", len(scriptTags))
	}

	scriptTag := scriptTags[0]

	// Verify script tag properties
	if scriptTag.Type != "module" {
		t.Errorf("Expected script type 'module', got '%s'", scriptTag.Type)
	}

	if !scriptTag.IsModule {
		t.Error("Expected IsModule to be true")
	}

	// Verify imports were parsed
	if len(scriptTag.Imports) < 2 {
		t.Errorf("Expected at least 2 imports, got %d", len(scriptTag.Imports))
		for i, imp := range scriptTag.Imports {
			t.Logf("Import %d: %s", i, imp.ImportPath)
		}
	}

	// Check for expected import paths
	expectedImports := []string{
		"@scope/package/my-card.js",
		"lit",
	}

	foundImports := make(map[string]bool)
	for _, imp := range scriptTag.Imports {
		foundImports[imp.ImportPath] = true
	}

	for _, expected := range expectedImports {
		if !foundImports[expected] {
			t.Errorf("Expected import '%s' not found", expected)
		}
	}
}

func TestDocument_ScriptTagParsing_NoModuleScript(t *testing.T) {
	// Create a document manager for HTML parsing
	dm, err := document.NewDocumentManager()
	if err != nil {
		t.Fatalf("Failed to create document manager: %v", err)
	}
	defer dm.Close()

	// Load test fixture
	fixtureDir := "test/fixtures/script-tag-parsing"
	contentBytes, err := os.ReadFile(filepath.Join(fixtureDir, "non-module-script.html"))
	if err != nil {
		t.Fatalf("Failed to read test fixture: %v", err)
	}
	content := string(contentBytes)

	// Open the document
	doc := dm.OpenDocument("test://test.html", content, 1)

	// Get script tags
	scriptTags := doc.ScriptTags()

	// Should find one script tag but it's not a module
	if len(scriptTags) != 1 {
		t.Errorf("Expected 1 script tag, got %d", len(scriptTags))
		return
	}

	scriptTag := scriptTags[0]

	// Verify script tag properties
	if scriptTag.IsModule {
		t.Error("Expected IsModule to be false for non-module script")
	}

	if scriptTag.Src != "legacy.js" {
		t.Errorf("Expected src 'legacy.js', got '%s'", scriptTag.Src)
	}

	// Should have no imports since it's not a module script
	if len(scriptTag.Imports) != 0 {
		t.Errorf("Expected 0 imports for non-module script, got %d", len(scriptTag.Imports))
	}

	// Test FindModuleScript functionality - should not find any
	_, found := doc.FindModuleScript()
	if found {
		t.Error("Expected NOT to find module script insertion point when no module scripts exist")
	}
}
