package inspector

import (
	"bytes"
	"encoding/json"
	"strings"
	"testing"
	"time"
)

func createTestReport() *InspectorReport {
	return &InspectorReport{
		Summary: ReportSummary{
			TotalRules:   10,
			RulesChecked: 8,
			RulesSkipped: 2,
			ErrorCount:   3,
			WarningCount: 5,
			PassedCount:  12,
		},
		Violations: []ValidationResult{
			{
				RuleName: "primary_key_naming",
				Level:    "error",
				Message:  "Primary key should start with 'id_'",
				Location: "public.users.user_id",
				Passed:   false,
				Context: map[string]interface{}{
					"schema":  "public",
					"table":   "users",
					"column":  "user_id",
					"pattern": "^id_",
				},
			},
			{
				RuleName: "table_name_length",
				Level:    "warning",
				Message:  "Table name too long",
				Location: "public.very_long_table_name_that_exceeds_limits",
				Passed:   false,
				Context: map[string]interface{}{
					"schema":     "public",
					"table":      "very_long_table_name_that_exceeds_limits",
					"length":     44,
					"max_length": 32,
				},
			},
		},
		GeneratedAt:  time.Now(),
		Database:     "testdb",
		SourceFormat: "postgresql",
	}
}

func TestNewMarkdownFormatter(t *testing.T) {
	var buf bytes.Buffer
	formatter := NewMarkdownFormatter(&buf)

	if formatter == nil {
		t.Fatal("NewMarkdownFormatter() returned nil")
	}

	// Buffer is not a terminal, so colors should be disabled
	if formatter.UseColors {
		t.Error("NewMarkdownFormatter() UseColors should be false for non-terminal")
	}
}

func TestNewJSONFormatter(t *testing.T) {
	formatter := NewJSONFormatter()

	if formatter == nil {
		t.Fatal("NewJSONFormatter() returned nil")
	}
}

func TestMarkdownFormatter_Format(t *testing.T) {
	report := createTestReport()
	var buf bytes.Buffer
	formatter := NewMarkdownFormatter(&buf)

	output, err := formatter.Format(report)
	if err != nil {
		t.Fatalf("MarkdownFormatter.Format() returned error: %v", err)
	}

	// Check that output contains expected sections
	if !strings.Contains(output, "# RelSpec Inspector Report") {
		t.Error("Markdown output missing header")
	}

	if !strings.Contains(output, "Database:") {
		t.Error("Markdown output missing database field")
	}

	if !strings.Contains(output, "testdb") {
		t.Error("Markdown output missing database name")
	}

	if !strings.Contains(output, "Summary") {
		t.Error("Markdown output missing summary section")
	}

	if !strings.Contains(output, "Rules Checked: 8") {
		t.Error("Markdown output missing rules checked count")
	}

	if !strings.Contains(output, "Errors: 3") {
		t.Error("Markdown output missing error count")
	}

	if !strings.Contains(output, "Warnings: 5") {
		t.Error("Markdown output missing warning count")
	}

	if !strings.Contains(output, "Violations") {
		t.Error("Markdown output missing violations section")
	}

	if !strings.Contains(output, "primary_key_naming") {
		t.Error("Markdown output missing rule name")
	}

	if !strings.Contains(output, "public.users.user_id") {
		t.Error("Markdown output missing location")
	}
}

func TestMarkdownFormatter_FormatNoViolations(t *testing.T) {
	report := &InspectorReport{
		Summary: ReportSummary{
			TotalRules:   10,
			RulesChecked: 10,
			RulesSkipped: 0,
			ErrorCount:   0,
			WarningCount: 0,
			PassedCount:  50,
		},
		Violations:   []ValidationResult{},
		GeneratedAt:  time.Now(),
		Database:     "testdb",
		SourceFormat: "postgresql",
	}

	var buf bytes.Buffer
	formatter := NewMarkdownFormatter(&buf)

	output, err := formatter.Format(report)
	if err != nil {
		t.Fatalf("MarkdownFormatter.Format() returned error: %v", err)
	}

	if !strings.Contains(output, "No violations found") {
		t.Error("Markdown output should indicate no violations")
	}
}

func TestJSONFormatter_Format(t *testing.T) {
	report := createTestReport()
	formatter := NewJSONFormatter()

	output, err := formatter.Format(report)
	if err != nil {
		t.Fatalf("JSONFormatter.Format() returned error: %v", err)
	}

	// Verify it's valid JSON
	var decoded InspectorReport
	if err := json.Unmarshal([]byte(output), &decoded); err != nil {
		t.Fatalf("JSONFormatter.Format() produced invalid JSON: %v", err)
	}

	// Check key fields
	if decoded.Database != "testdb" {
		t.Errorf("JSON decoded Database = %q, want \"testdb\"", decoded.Database)
	}

	if decoded.Summary.ErrorCount != 3 {
		t.Errorf("JSON decoded ErrorCount = %d, want 3", decoded.Summary.ErrorCount)
	}

	if len(decoded.Violations) != 2 {
		t.Errorf("JSON decoded Violations length = %d, want 2", len(decoded.Violations))
	}
}

func TestMarkdownFormatter_FormatHeader(t *testing.T) {
	var buf bytes.Buffer
	formatter := NewMarkdownFormatter(&buf)

	header := formatter.formatHeader("Test Header")

	if !strings.Contains(header, "# Test Header") {
		t.Errorf("formatHeader() = %q, want to contain \"# Test Header\"", header)
	}
}

func TestMarkdownFormatter_FormatBold(t *testing.T) {
	tests := []struct {
		name         string
		useColors    bool
		text         string
		wantContains string
	}{
		{
			name:         "without colors",
			useColors:    false,
			text:         "Bold Text",
			wantContains: "**Bold Text**",
		},
		{
			name:         "with colors",
			useColors:    true,
			text:         "Bold Text",
			wantContains: "Bold Text",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			formatter := &MarkdownFormatter{UseColors: tt.useColors}
			result := formatter.formatBold(tt.text)

			if !strings.Contains(result, tt.wantContains) {
				t.Errorf("formatBold() = %q, want to contain %q", result, tt.wantContains)
			}
		})
	}
}

func TestMarkdownFormatter_Colorize(t *testing.T) {
	tests := []struct {
		name      string
		useColors bool
		text      string
		color     string
		wantColor bool
	}{
		{
			name:      "without colors",
			useColors: false,
			text:      "Test",
			color:     colorRed,
			wantColor: false,
		},
		{
			name:      "with colors",
			useColors: true,
			text:      "Test",
			color:     colorRed,
			wantColor: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			formatter := &MarkdownFormatter{UseColors: tt.useColors}
			result := formatter.colorize(tt.text, tt.color)

			hasColor := strings.Contains(result, tt.color)
			if hasColor != tt.wantColor {
				t.Errorf("colorize() has color codes = %v, want %v", hasColor, tt.wantColor)
			}

			if !strings.Contains(result, tt.text) {
				t.Errorf("colorize() doesn't contain original text %q", tt.text)
			}
		})
	}
}

func TestMarkdownFormatter_FormatContext(t *testing.T) {
	formatter := &MarkdownFormatter{UseColors: false}

	context := map[string]interface{}{
		"schema":     "public",
		"table":      "users",
		"column":     "id",
		"pattern":    "^id_",
		"max_length": 64,
	}

	result := formatter.formatContext(context)

	// Should not include schema, table, column (they're in location)
	if strings.Contains(result, "schema") {
		t.Error("formatContext() should skip schema field")
	}

	if strings.Contains(result, "table=") {
		t.Error("formatContext() should skip table field")
	}

	if strings.Contains(result, "column=") {
		t.Error("formatContext() should skip column field")
	}

	// Should include other fields
	if !strings.Contains(result, "pattern") {
		t.Error("formatContext() should include pattern field")
	}

	if !strings.Contains(result, "max_length") {
		t.Error("formatContext() should include max_length field")
	}
}

func TestMarkdownFormatter_FormatViolation(t *testing.T) {
	formatter := &MarkdownFormatter{UseColors: false}

	violation := ValidationResult{
		RuleName: "test_rule",
		Level:    "error",
		Message:  "Test violation message",
		Location: "public.users.id",
		Passed:   false,
		Context: map[string]interface{}{
			"pattern": "^id_",
		},
	}

	result := formatter.formatViolation(violation, colorRed)

	if !strings.Contains(result, "test_rule") {
		t.Error("formatViolation() should include rule name")
	}

	if !strings.Contains(result, "Test violation message") {
		t.Error("formatViolation() should include message")
	}

	if !strings.Contains(result, "public.users.id") {
		t.Error("formatViolation() should include location")
	}

	if !strings.Contains(result, "Location:") {
		t.Error("formatViolation() should include Location label")
	}

	if !strings.Contains(result, "Message:") {
		t.Error("formatViolation() should include Message label")
	}
}

func TestReportFormatConstants(t *testing.T) {
	// Test that color constants are defined
	if colorReset == "" {
		t.Error("colorReset is not defined")
	}

	if colorRed == "" {
		t.Error("colorRed is not defined")
	}

	if colorYellow == "" {
		t.Error("colorYellow is not defined")
	}

	if colorGreen == "" {
		t.Error("colorGreen is not defined")
	}

	if colorBold == "" {
		t.Error("colorBold is not defined")
	}
}
