package gogen

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/zeromicro/go-zero/tools/goctl/internal/version"
)

// TestGenerationComments verifies that all generated files have appropriate generation comments
func TestGenerationComments(t *testing.T) {
	// Create a temporary directory for our test
	tempDir, err := os.MkdirTemp("", "goctl_test_")
	require.NoError(t, err)
	defer os.RemoveAll(tempDir)

	// Create a simple API spec for testing
	apiContent := `
syntax = "v1"

type HelloRequest {
	Name string ` + "`json:\"name\"`" + `
}

type HelloResponse {
	Message string ` + "`json:\"message\"`" + `
}

service hello-api {
	@handler helloHandler
	post /hello (HelloRequest) returns (HelloResponse)
}`

	// Write the API spec to a temporary file
	apiFile := filepath.Join(tempDir, "test.api")
	err = os.WriteFile(apiFile, []byte(apiContent), 0644)
	require.NoError(t, err)

	// Parse and generate the API files using the correct function signature
	err = DoGenProject(apiFile, tempDir, "gozero", false)
	require.NoError(t, err)

	// Define expected files and their comment types
	expectedFiles := map[string]string{
		// Files that should have "DO NOT EDIT" comments (regenerated files)
		"internal/types/types.go": "DO NOT EDIT",

		// Files that should have "Safe to edit" comments (scaffolded files)
		"internal/handler/hellohandler.go": "Safe to edit",
		"internal/config/config.go":        "Safe to edit",
		"hello.go":                         "Safe to edit", // main file
		"internal/svc/servicecontext.go":   "Safe to edit",
		"internal/logic/hellologic.go":     "Safe to edit",
	}

	// Check each file for the correct generation comment
	for filePath, expectedCommentType := range expectedFiles {
		fullPath := filepath.Join(tempDir, filePath)

		// Skip if file doesn't exist (some files might not be generated in all cases)
		if _, err := os.Stat(fullPath); os.IsNotExist(err) {
			t.Logf("File %s does not exist, skipping", filePath)
			continue
		}

		content, err := os.ReadFile(fullPath)
		require.NoError(t, err, "Failed to read file: %s", filePath)

		contentStr := string(content)
		lines := strings.Split(contentStr, "\n")

		// Check that the file starts with proper generation comments
		require.GreaterOrEqual(t, len(lines), 2, "File %s should have at least 2 lines", filePath)

		if expectedCommentType == "DO NOT EDIT" {
			assert.Contains(t, lines[0], "// Code generated by goctl. DO NOT EDIT.",
				"File %s should have 'DO NOT EDIT' comment as first line", filePath)
		} else if expectedCommentType == "Safe to edit" {
			assert.Contains(t, lines[0], "// Code scaffolded by goctl. Safe to edit.",
				"File %s should have 'Safe to edit' comment as first line", filePath)
		}

		// Check that the second line contains the version
		assert.Contains(t, lines[1], "// goctl",
			"File %s should have version comment as second line", filePath)
		assert.Contains(t, lines[1], version.BuildVersion,
			"File %s should contain version %s in second line", filePath, version.BuildVersion)
	}
}

// TestRoutesGenerationComment verifies routes files have "DO NOT EDIT" comment
func TestRoutesGenerationComment(t *testing.T) {
	// Create a temporary directory for our test
	tempDir, err := os.MkdirTemp("", "goctl_routes_test_")
	require.NoError(t, err)
	defer os.RemoveAll(tempDir)

	// Create an API spec with multiple handlers to ensure routes file is generated
	apiContent := `
syntax = "v1"

type HelloRequest {
	Name string ` + "`json:\"name\"`" + `
}

type HelloResponse {
	Message string ` + "`json:\"message\"`" + `
}

service hello-api {
	@handler helloHandler
	post /hello (HelloRequest) returns (HelloResponse)

	@handler worldHandler
	get /world returns (HelloResponse)
}`

	// Write the API spec to a temporary file
	apiFile := filepath.Join(tempDir, "test.api")
	err = os.WriteFile(apiFile, []byte(apiContent), 0644)
	require.NoError(t, err)

	// Generate the API files using the correct function signature
	err = DoGenProject(apiFile, tempDir, "gozero", false)
	require.NoError(t, err)

	// Check the routes file specifically
	routesFile := filepath.Join(tempDir, "internal/handler/routes.go")
	if _, err := os.Stat(routesFile); os.IsNotExist(err) {
		t.Skip("Routes file not generated, skipping test")
		return
	}

	content, err := os.ReadFile(routesFile)
	require.NoError(t, err, "Failed to read routes.go")

	contentStr := string(content)
	lines := strings.Split(contentStr, "\n")

	// Check that routes.go has "DO NOT EDIT" comment
	require.GreaterOrEqual(t, len(lines), 2, "Routes file should have at least 2 lines")
	assert.Contains(t, lines[0], "// Code generated by goctl. DO NOT EDIT.",
		"Routes file should have 'DO NOT EDIT' comment")
	assert.Contains(t, lines[1], "// goctl",
		"Routes file should have version comment")
	assert.Contains(t, lines[1], version.BuildVersion,
		"Routes file should contain version %s", version.BuildVersion)
}

// TestVersionInTemplateData verifies that version is correctly passed to templates
func TestVersionInTemplateData(t *testing.T) {
	// Test that BuildVersion is available
	assert.NotEmpty(t, version.BuildVersion, "BuildVersion should not be empty")
}

// TestCommentsFollowGoStandards verifies our comments follow Go community standards
func TestCommentsFollowGoStandards(t *testing.T) {
	// Test the format of our generation comments
	doNotEditComment := "// Code generated by goctl. DO NOT EDIT."
	safeToEditComment := "// Code scaffolded by goctl. Safe to edit."

	// Both should be valid Go comments
	assert.True(t, strings.HasPrefix(doNotEditComment, "//"),
		"DO NOT EDIT comment should start with //")
	assert.True(t, strings.HasPrefix(safeToEditComment, "//"),
		"Safe to edit comment should start with //")

	// Should contain key information
	assert.Contains(t, doNotEditComment, "goctl",
		"DO NOT EDIT comment should mention goctl")
	assert.Contains(t, safeToEditComment, "goctl",
		"Safe to edit comment should mention goctl")
	assert.Contains(t, doNotEditComment, "DO NOT EDIT",
		"Should clearly state DO NOT EDIT")
	assert.Contains(t, safeToEditComment, "Safe to edit",
		"Should clearly state Safe to edit")
}
