package report

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

func TestGenerateToken_NotEmpty(t *testing.T) {
	token, err := GenerateToken()
	if err != nil {
		t.Fatalf("GenerateToken failed: %v", err)
	}
	if token == "" {
		t.Fatal("expected non-empty token")
	}
}

func TestGenerateToken_Format(t *testing.T) {
	token, err := GenerateToken()
	if err != nil {
		t.Fatalf("GenerateToken failed: %v", err)
	}

	// UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
	parts := strings.Split(token, "-")
	if len(parts) != 5 {
		t.Errorf("expected 5 UUID parts, got %d: %s", len(parts), token)
	}

	// Check version nibble (should be 4 in the third group)
	if len(parts[2]) > 0 && parts[2][0] != '4' {
		t.Errorf("expected version 4 UUID, got version '%c'", parts[2][0])
	}

	// Check variant nibble (should be 8, 9, a, or b in the fourth group)
	if len(parts[3]) > 0 {
		v := parts[3][0]
		if v != '8' && v != '9' && v != 'a' && v != 'b' {
			t.Errorf("expected variant 8/9/a/b, got '%c'", v)
		}
	}
}

func TestGenerateToken_Unique(t *testing.T) {
	seen := make(map[string]bool)
	for i := 0; i < 100; i++ {
		token, err := GenerateToken()
		if err != nil {
			t.Fatal(err)
		}
		if seen[token] {
			t.Errorf("duplicate token generated: %s", token)
		}
		seen[token] = true
	}
}

func TestGenerateToken_Length(t *testing.T) {
	token, err := GenerateToken()
	if err != nil {
		t.Fatal(err)
	}
	// UUID v4 is 36 characters (32 hex + 4 dashes)
	if len(token) != 36 {
		t.Errorf("expected UUID length 36, got %d: %s", len(token), token)
	}
}

func TestGenerateFallbackHTML_WithSubdomains(t *testing.T) {
	subdomains := []string{"example.com", "www.example.com", "api.example.com"}
	html := GenerateFallbackHTML(subdomains)

	if html == "" {
		t.Fatal("expected non-empty HTML")
	}

	// Should contain the primary domain
	if !strings.Contains(html, "example.com") {
		t.Error("expected HTML to contain 'example.com'")
	}

	// Should contain subdomain list items
	if !strings.Contains(html, "www.example.com") {
		t.Error("expected HTML to contain 'www.example.com'")
	}
	if !strings.Contains(html, "api.example.com") {
		t.Error("expected HTML to contain 'api.example.com'")
	}

	// Should contain resilience narrative heading
	if !strings.Contains(html, "Resilience Narrative") {
		t.Error("expected 'Resilience Narrative' heading in fallback HTML")
	}

	// Should contain the resilience score
	if !strings.Contains(html, "B+") {
		t.Error("expected 'B+' score in fallback HTML")
	}
}

func TestGenerateFallbackHTML_SingleSubdomain(t *testing.T) {
	subdomains := []string{"example.com"}
	html := GenerateFallbackHTML(subdomains)

	if html == "" {
		t.Fatal("expected non-empty HTML")
	}

	if !strings.Contains(html, "example.com") {
		t.Error("expected HTML to contain the domain")
	}
}

func TestGenerateFallbackHTML_ContainsSubdomainCount(t *testing.T) {
	subdomains := []string{"a.com", "b.com", "c.com"}
	html := GenerateFallbackHTML(subdomains)
	// The count "3" should appear
	if !strings.Contains(html, "3 subdomains") {
		t.Error("expected HTML to mention the subdomain count (3)")
	}
}

func TestNewGenerator(t *testing.T) {
	g := New("/tmp/reports", "https://test.local")
	if g == nil {
		t.Fatal("expected non-nil generator")
	}
}

func TestBuildReport_CreatesFile(t *testing.T) {
	dir := t.TempDir()
	reportsDir := filepath.Join(dir, "reports")
	g := New(reportsDir, "https://test.local")

	token, err := GenerateToken()
	if err != nil {
		t.Fatal(err)
	}

	aiHTML := "<p>AI generated content</p>"
	path, err := g.BuildReport(token, "example.com", aiHTML)
	if err != nil {
		t.Fatalf("BuildReport failed: %v", err)
	}

	if path == "" {
		t.Fatal("expected non-empty report path")
	}

	// Check the file exists
	if _, err := os.Stat(path); os.IsNotExist(err) {
		t.Fatalf("report file not created at %s", path)
	}

	// Verify filename
	expectedName := "visitor_" + token + ".html"
	if !strings.HasSuffix(path, expectedName) {
		t.Errorf("expected filename to end with '%s', got '%s'", expectedName, path)
	}
}

func TestBuildReport_ContainsDomain(t *testing.T) {
	dir := t.TempDir()
	reportsDir := filepath.Join(dir, "reports")
	g := New(reportsDir, "https://test.local")

	token, err := GenerateToken()
	if err != nil {
		t.Fatal(err)
	}

	aiHTML := "<p>Security analysis results</p>"
	path, err := g.BuildReport(token, "mycompany.com", aiHTML)
	if err != nil {
		t.Fatal(err)
	}

	data, err := os.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}

	content := string(data)
	if !strings.Contains(content, "mycompany.com") {
		t.Error("expected report to contain the domain name")
	}
	if !strings.Contains(content, aiHTML) {
		t.Error("expected report to contain the AI narrative HTML")
	}
}

func TestBuildReport_ContainsToken(t *testing.T) {
	dir := t.TempDir()
	reportsDir := filepath.Join(dir, "reports")
	g := New(reportsDir, "")

	token, _ := GenerateToken()
	_, err := g.BuildReport(token, "example.com", "<p>content</p>")
	if err != nil {
		t.Fatal(err)
	}

	path := filepath.Join(reportsDir, "visitor_"+token+".html")
	data, _ := os.ReadFile(path)
	content := string(data)

	if !strings.Contains(content, token) {
		t.Error("expected report to contain the token")
	}
}

func TestBuildReport_QRCodeLink(t *testing.T) {
	dir := t.TempDir()
	reportsDir := filepath.Join(dir, "reports")
	g := New(reportsDir, "https://test.local")

	token, _ := GenerateToken()
	_, err := g.BuildReport(token, "example.com", "<p>content</p>")
	if err != nil {
		t.Fatal(err)
	}

	path := filepath.Join(reportsDir, "visitor_"+token+".html")
	data, _ := os.ReadFile(path)
	content := string(data)

	qrURL := "https://test.local/reports/visitor_" + token + ".html"
	if !strings.Contains(content, qrURL) {
		t.Errorf("expected QR URL '%s' in report", qrURL)
	}
}

func TestBuildReport_QRCodeLinkRelative(t *testing.T) {
	// When baseURL is empty, QR should use relative path
	dir := t.TempDir()
	reportsDir := filepath.Join(dir, "reports")
	g := New(reportsDir, "")

	token, _ := GenerateToken()
	_, err := g.BuildReport(token, "example.com", "<p>content</p>")
	if err != nil {
		t.Fatal(err)
	}

	path := filepath.Join(reportsDir, "visitor_"+token+".html")
	data, _ := os.ReadFile(path)
	content := string(data)

	// Should contain a relative QR path, not absolute
	if strings.Contains(content, "https://") {
		t.Log("QR URL is absolute (baseURL was empty but QR may still use relative paths)")
	}
}

func TestBuildReport_StartNewScanButton(t *testing.T) {
	dir := t.TempDir()
	reportsDir := filepath.Join(dir, "reports")
	g := New(reportsDir, "")

	token, _ := GenerateToken()
	_, err := g.BuildReport(token, "example.com", "<p>content</p>")
	if err != nil {
		t.Fatal(err)
	}

	path := filepath.Join(reportsDir, "visitor_"+token+".html")
	data, _ := os.ReadFile(path)
	content := string(data)

	if !strings.Contains(content, "Start New Scan") {
		t.Error("expected 'Start New Scan' button in report")
	}
	if !strings.Contains(content, "AASD") {
		t.Error("expected 'AASD' nav bar title in report")
	}
}

func TestBuildReport_ReportsDirCreated(t *testing.T) {
	dir := t.TempDir()
	// Don't create reports subdirectory — BuildReport should create it
	reportsDir := filepath.Join(dir, "reports")

	g := New(reportsDir, "")
	token, _ := GenerateToken()

	_, err := g.BuildReport(token, "example.com", "<p>content</p>")
	if err != nil {
		t.Fatalf("BuildReport should create the reports directory: %v", err)
	}

	if _, err := os.Stat(reportsDir); os.IsNotExist(err) {
		t.Error("reports directory should have been created")
	}
}

func TestGenerateFallbackHTML_ValidHTML(t *testing.T) {
	subdomains := []string{"test.example.com"}
	html := GenerateFallbackHTML(subdomains)

	// Basic HTML structure checks
	if !strings.HasPrefix(strings.TrimSpace(html), "<div") {
		t.Error("expected HTML to start with a div")
	}
	if !strings.Contains(html, "</div>") {
		t.Error("expected closing div tag")
	}
}
