package kafui

import (
	"context"
	"testing"

	"github.com/Benny93/kafui/pkg/api"
	"github.com/gdamore/tcell/v2"
	"github.com/rivo/tview"
)

// TestCreatePropertyInfo tests the CreatePropertyInfo function
func TestCreatePropertyInfo(t *testing.T) {
	tests := []struct {
		name          string
		propertyName  string
		propertyValue string
		expectedLabel string
		expectedText  string
		expectedDisabled bool
	}{
		{
			name:             "basic property",
			propertyName:     "Topic",
			propertyValue:    "user-events",
			expectedLabel:    "Topic: ",
			expectedText:     "user-events",
			expectedDisabled: true,
		},
		{
			name:             "empty property name",
			propertyName:     "",
			propertyValue:    "value",
			expectedLabel:    ": ",
			expectedText:     "value",
			expectedDisabled: true,
		},
		{
			name:             "empty property value",
			propertyName:     "Status",
			propertyValue:    "",
			expectedLabel:    "Status: ",
			expectedText:     "",
			expectedDisabled: true,
		},
		{
			name:             "property with special characters",
			propertyName:     "Config-Key",
			propertyValue:    "value with spaces & symbols!",
			expectedLabel:    "Config-Key: ",
			expectedText:     "value with spaces & symbols!",
			expectedDisabled: true,
		},
		{
			name:             "numeric property",
			propertyName:     "Partitions",
			propertyValue:    "5",
			expectedLabel:    "Partitions: ",
			expectedText:     "5",
			expectedDisabled: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			inputField := CreatePropertyInfo(tt.propertyName, tt.propertyValue)

			if inputField == nil {
				t.Fatal("CreatePropertyInfo returned nil")
			}

			// Test label
			if inputField.GetLabel() != tt.expectedLabel {
				t.Errorf("Label = %v, want %v", inputField.GetLabel(), tt.expectedLabel)
			}

			// Test text
			if inputField.GetText() != tt.expectedText {
				t.Errorf("Text = %v, want %v", inputField.GetText(), tt.expectedText)
			}

			// Test disabled state
			if !tt.expectedDisabled {
				t.Error("InputField should be disabled")
			}

			// Test field width (should be 0 for full width)
			// Note: We can't directly test GetFieldWidth() as it's not exposed,
			// but we can verify the field was created successfully
		})
	}
}

// TestCreateRunInfo tests the CreateRunInfo function
func TestCreateRunInfo(t *testing.T) {
	tests := []struct {
		name         string
		runeName     string
		info         string
		expectedLabel string
		expectedText string
	}{
		{
			name:          "basic rune info",
			runeName:      "Enter",
			info:          "Select item",
			expectedLabel: "<Enter>: ",
			expectedText:  "Select item",
		},
		{
			name:          "single character rune",
			runeName:      "q",
			info:          "Quit",
			expectedLabel: "<q>: ",
			expectedText:  "Quit",
		},
		{
			name:          "arrow key",
			runeName:      "↑",
			info:          "Move up",
			expectedLabel: "<↑>: ",
			expectedText:  "Move up",
		},
		{
			name:          "empty rune name",
			runeName:      "",
			info:          "No action",
			expectedLabel: "<>: ",
			expectedText:  "No action",
		},
		{
			name:          "empty info",
			runeName:      "Esc",
			info:          "",
			expectedLabel: "<Esc>: ",
			expectedText:  "",
		},
		{
			name:          "complex key combination",
			runeName:      "Ctrl+C",
			info:          "Copy to clipboard",
			expectedLabel: "<Ctrl+C>: ",
			expectedText:  "Copy to clipboard",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			inputField := CreateRunInfo(tt.runeName, tt.info)

			if inputField == nil {
				t.Fatal("CreateRunInfo returned nil")
			}

			// Test label
			if inputField.GetLabel() != tt.expectedLabel {
				t.Errorf("Label = %v, want %v", inputField.GetLabel(), tt.expectedLabel)
			}

			// Test text
			if inputField.GetText() != tt.expectedText {
				t.Errorf("Text = %v, want %v", inputField.GetText(), tt.expectedText)
			}

			// Test that the field is disabled
			// Note: We can't directly test IsDisabled() but we can verify creation
		})
	}
}

// TestUIThemeConfiguration tests that the UI theme is properly configured
func TestUIThemeConfiguration(t *testing.T) {
	// Save original theme
	originalTheme := tview.Styles

	// Test theme configuration (simulating what happens in OpenUI)
	testTheme := tview.Theme{
		PrimitiveBackgroundColor:    tcell.ColorBlack.TrueColor(),
		ContrastBackgroundColor:     tcell.ColorBlack.TrueColor(),
		MoreContrastBackgroundColor: tcell.ColorGreen.TrueColor(),
		BorderColor:                 tcell.ColorWhite.TrueColor(),
		TitleColor:                  tcell.ColorWhite.TrueColor(),
		GraphicsColor:               tcell.ColorBlack.TrueColor(),
		PrimaryTextColor:            tcell.ColorDarkCyan.TrueColor(),
		SecondaryTextColor:          tcell.ColorWhite.TrueColor(),
		TertiaryTextColor:           tcell.ColorGreen.TrueColor(),
		InverseTextColor:            tcell.ColorGreen.TrueColor(),
		ContrastSecondaryTextColor:  tcell.ColorWhite.TrueColor(),
	}

	// Apply test theme
	tview.Styles = testTheme

	// Verify theme colors
	if tview.Styles.PrimitiveBackgroundColor != tcell.ColorBlack.TrueColor() {
		t.Error("PrimitiveBackgroundColor not set correctly")
	}

	if tview.Styles.BorderColor != tcell.ColorWhite.TrueColor() {
		t.Error("BorderColor not set correctly")
	}

	if tview.Styles.PrimaryTextColor != tcell.ColorDarkCyan.TrueColor() {
		t.Error("PrimaryTextColor not set correctly")
	}

	// Restore original theme
	tview.Styles = originalTheme
}

// TestUIEventHandling tests UI event constants and handling
func TestUIEventHandling(t *testing.T) {
	// Test that UI events are properly defined
	events := []UIEvent{
		OnModalClose,
		OnFocusSearch,
		OnStartTableSearch,
		OnPageChange,
	}

	expectedEvents := []string{
		"ModalClose",
		"FocusSearch",
		"OnStartTableSearch",
		"PageChange",
	}

	for i, event := range events {
		if string(event) != expectedEvents[i] {
			t.Errorf("Event %d = %v, want %v", i, string(event), expectedEvents[i])
		}
	}
}

// TestInputFieldCreation tests the creation of input fields with various configurations
func TestInputFieldCreation(t *testing.T) {
	tests := []struct {
		name     string
		function func() *tview.InputField
		testFunc func(*testing.T, *tview.InputField)
	}{
		{
			name: "property info field",
			function: func() *tview.InputField {
				return CreatePropertyInfo("TestProp", "TestValue")
			},
			testFunc: func(t *testing.T, field *tview.InputField) {
				if field.GetLabel() != "TestProp: " {
					t.Error("Property info label incorrect")
				}
				if field.GetText() != "TestValue" {
					t.Error("Property info text incorrect")
				}
			},
		},
		{
			name: "run info field",
			function: func() *tview.InputField {
				return CreateRunInfo("TestKey", "TestAction")
			},
			testFunc: func(t *testing.T, field *tview.InputField) {
				if field.GetLabel() != "<TestKey>: " {
					t.Error("Run info label incorrect")
				}
				if field.GetText() != "TestAction" {
					t.Error("Run info text incorrect")
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			field := tt.function()
			if field == nil {
				t.Fatal("Input field creation returned nil")
			}
			tt.testFunc(t, field)
		})
	}
}

// TestUIComponentIntegration tests basic UI component integration
func TestUIComponentIntegration(t *testing.T) {
	// Test that UI components can be created and configured together
	propertyField := CreatePropertyInfo("Topic", "test-topic")
	runeField := CreateRunInfo("Enter", "Select")

	if propertyField == nil || runeField == nil {
		t.Fatal("Failed to create UI components")
	}

	// Test that components have different configurations
	if propertyField.GetLabel() == runeField.GetLabel() {
		t.Error("Property and rune fields should have different labels")
	}

	// Test that both components are properly configured
	if propertyField.GetText() == "" {
		t.Error("Property field should have text")
	}

	if runeField.GetText() == "" {
		t.Error("Rune field should have text")
	}
}

// TestUIGlobalVariables tests the global UI variables
func TestUIGlobalVariables(t *testing.T) {
	// Test that global variables can be accessed
	// Note: These are package-level variables that get set during UI initialization
	
	// We can't easily test the actual values without running the full UI,
	// but we can test that the variables exist and can be assigned
	var testTopic api.Topic
	testTopic = api.Topic{
		NumPartitions:     3,
		ReplicationFactor: 2,
		MessageCount:      100,
	}

	// Simulate setting the global variable
	originalTopic := currentTopic
	currentTopic = testTopic

	// Verify the assignment worked
	if currentTopic.NumPartitions != 3 {
		t.Error("Global topic variable not set correctly")
	}

	// Restore original value
	currentTopic = originalTopic
}

// Benchmark tests for UI component creation
func BenchmarkCreatePropertyInfo(b *testing.B) {
	for i := 0; i < b.N; i++ {
		field := CreatePropertyInfo("BenchmarkProp", "BenchmarkValue")
		_ = field
	}
}

func BenchmarkCreateRunInfo(b *testing.B) {
	for i := 0; i < b.N; i++ {
		field := CreateRunInfo("BenchmarkKey", "BenchmarkAction")
		_ = field
	}
}

// TestOpenUISetup tests the OpenUI function setup without running the blocking UI
func TestOpenUISetup(t *testing.T) {
	// We can't easily test the full OpenUI function since it's blocking,
	// but we can test the components it creates and configures
	
	// Test with mock data source
	mockDS := &mockDataSourceForUI{}
	
	// Test that we can create the components that OpenUI creates
	t.Run("tview_application_creation", func(t *testing.T) {
		app := tview.NewApplication()
		if app == nil {
			t.Fatal("Failed to create tview application")
		}
	})
	
	t.Run("pages_creation", func(t *testing.T) {
		pages := tview.NewPages()
		if pages == nil {
			t.Fatal("Failed to create tview pages")
		}
	})
	
	t.Run("modal_creation", func(t *testing.T) {
		modal := tview.NewModal().
			SetText("Resource Not Found").
			AddButtons([]string{"OK"})
		if modal == nil {
			t.Fatal("Failed to create modal")
		}
		
		// Test modal configuration - we can't easily test GetText() 
		// but we can verify the modal was created successfully
		if modal == nil {
			t.Error("Modal not created correctly")
		}
	})
	
	t.Run("message_channel_creation", func(t *testing.T) {
		msgChannel := make(chan UIEvent, 10)
		if msgChannel == nil {
			t.Fatal("Failed to create message channel")
		}
		
		// Test channel functionality
		testEvent := OnModalClose
		select {
		case msgChannel <- testEvent:
			// Successfully sent
		default:
			t.Error("Failed to send event to channel")
		}
		
		select {
		case receivedEvent := <-msgChannel:
			if receivedEvent != testEvent {
				t.Errorf("Received event %v, expected %v", receivedEvent, testEvent)
			}
		default:
			t.Error("Failed to receive event from channel")
		}
	})
	
	t.Run("main_page_creation", func(t *testing.T) {
		mainPage := NewMainPage()
		if mainPage == nil {
			t.Fatal("Failed to create main page")
		}
	})
	
	// Test theme configuration (simulating what OpenUI does)
	t.Run("theme_configuration", func(t *testing.T) {
		originalTheme := tview.Styles
		
		// Apply the same theme configuration as OpenUI
		tview.Styles = tview.Theme{
			PrimitiveBackgroundColor:    tcell.ColorBlack.TrueColor(),
			ContrastBackgroundColor:     tcell.ColorBlack.TrueColor(),
			MoreContrastBackgroundColor: tcell.ColorGreen.TrueColor(),
			BorderColor:                 tcell.ColorWhite.TrueColor(),
			TitleColor:                  tcell.ColorWhite.TrueColor(),
			GraphicsColor:               tcell.ColorBlack.TrueColor(),
			PrimaryTextColor:            tcell.ColorDarkCyan.TrueColor(),
			SecondaryTextColor:          tcell.ColorWhite.TrueColor(),
			TertiaryTextColor:           tcell.ColorGreen.TrueColor(),
			InverseTextColor:            tcell.ColorGreen.TrueColor(),
			ContrastSecondaryTextColor:  tcell.ColorWhite.TrueColor(),
		}
		
		// Verify all theme colors are set correctly
		if tview.Styles.PrimitiveBackgroundColor != tcell.ColorBlack.TrueColor() {
			t.Error("PrimitiveBackgroundColor not set correctly")
		}
		if tview.Styles.MoreContrastBackgroundColor != tcell.ColorGreen.TrueColor() {
			t.Error("MoreContrastBackgroundColor not set correctly")
		}
		if tview.Styles.BorderColor != tcell.ColorWhite.TrueColor() {
			t.Error("BorderColor not set correctly")
		}
		if tview.Styles.TitleColor != tcell.ColorWhite.TrueColor() {
			t.Error("TitleColor not set correctly")
		}
		if tview.Styles.PrimaryTextColor != tcell.ColorDarkCyan.TrueColor() {
			t.Error("PrimaryTextColor not set correctly")
		}
		if tview.Styles.SecondaryTextColor != tcell.ColorWhite.TrueColor() {
			t.Error("SecondaryTextColor not set correctly")
		}
		if tview.Styles.TertiaryTextColor != tcell.ColorGreen.TrueColor() {
			t.Error("TertiaryTextColor not set correctly")
		}
		if tview.Styles.InverseTextColor != tcell.ColorGreen.TrueColor() {
			t.Error("InverseTextColor not set correctly")
		}
		if tview.Styles.ContrastSecondaryTextColor != tcell.ColorWhite.TrueColor() {
			t.Error("ContrastSecondaryTextColor not set correctly")
		}
		
		// Restore original theme
		tview.Styles = originalTheme
	})
	
	_ = mockDS // Use the mock data source
}

// TestModalFunctionality tests modal setup and event handling
func TestModalFunctionality(t *testing.T) {
	t.Run("modal_done_function", func(t *testing.T) {
		pages := tview.NewPages()
		msgChannel := make(chan UIEvent, 10)
		
		modal := tview.NewModal().
			SetText("Test Modal").
			AddButtons([]string{"OK", "Cancel"})
		
		// Set up the done function like OpenUI does
		modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
			pages.HidePage("modal")
			msgChannel <- OnModalClose
		})
		
		// We can't easily trigger the done function without user interaction,
		// but we can verify the modal was configured
		if modal == nil {
			t.Error("Modal not configured correctly")
		}
	})
	
	t.Run("modal_input_capture", func(t *testing.T) {
		pages := tview.NewPages()
		modal := tview.NewModal()
		
		// Set up input capture like OpenUI does
		modal.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
			pages.HidePage("modal")
			return event
		})
		
		// Verify modal was configured (we can't easily test the actual capture)
		if modal == nil {
			t.Error("Modal input capture setup failed")
		}
	})
}

// TestKeyboardInputCapture tests the main application input capture functionality
func TestKeyboardInputCapture(t *testing.T) {
	tests := []struct {
		name        string
		keyRune     rune
		keyCode     tcell.Key
		frontPage   string
		expectedEvent UIEvent
		shouldHandle bool
	}{
		{
			name:          "colon_key_triggers_search",
			keyRune:       ':',
			frontPage:     "main",
			expectedEvent: OnFocusSearch,
			shouldHandle:  true,
		},
		{
			name:          "slash_key_on_main_page",
			keyRune:       '/',
			frontPage:     "main",
			expectedEvent: OnStartTableSearch,
			shouldHandle:  true,
		},
		{
			name:        "slash_key_on_other_page",
			keyRune:     '/',
			frontPage:   "topicPage",
			shouldHandle: false,
		},
		{
			name:        "escape_key",
			keyCode:     tcell.KeyEsc,
			frontPage:   "topicPage",
			shouldHandle: true,
		},
		{
			name:        "other_key",
			keyRune:     'a',
			frontPage:   "main",
			shouldHandle: false,
		},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			msgChannel := make(chan UIEvent, 10)
			pages := tview.NewPages()
			
			// Create a mock event
			var event *tcell.EventKey
			if tt.keyCode != 0 {
				event = tcell.NewEventKey(tt.keyCode, 0, tcell.ModNone)
			} else {
				event = tcell.NewEventKey(tcell.KeyRune, tt.keyRune, tcell.ModNone)
			}
			
			// Simulate the input capture function from OpenUI
			inputCapture := func(event *tcell.EventKey) *tcell.EventKey {
				frontPage := tt.frontPage // Simulate getting front page
				
				if event.Rune() == ':' {
					msgChannel <- OnFocusSearch
					return nil
				}
				
				if event.Rune() == '/' && frontPage == "main" {
					msgChannel <- OnStartTableSearch
					return nil
				}
				
				if event.Key() == tcell.KeyEsc {
					if frontPage == "topicPage" {
						// Simulate topicPage.CloseTopicPage()
					}
					if frontPage == "DetailPage" {
						// Simulate messageDetailPage.Hide()
						return event
					}
					if frontPage != "main" {
						pages.SwitchToPage("main")
					}
				}
				
				return event
			}
			
			// Test the input capture
			result := inputCapture(event)
			
			if tt.shouldHandle && tt.expectedEvent != "" {
				// Check if the expected event was sent
				select {
				case receivedEvent := <-msgChannel:
					if receivedEvent != tt.expectedEvent {
						t.Errorf("Expected event %v, got %v", tt.expectedEvent, receivedEvent)
					}
				default:
					t.Error("Expected event was not sent to channel")
				}
				
				// For handled events that return nil
				if tt.keyRune == ':' || (tt.keyRune == '/' && tt.frontPage == "main") {
					if result != nil {
						t.Error("Expected nil return for handled event")
					}
				}
			} else if !tt.shouldHandle {
				// Check that no event was sent
				select {
				case <-msgChannel:
					t.Error("Unexpected event sent to channel")
				default:
					// Expected - no event sent
				}
			}
		})
	}
}

// TestPageSetup tests the page setup functionality
func TestPageSetup(t *testing.T) {
	t.Run("pages_configuration", func(t *testing.T) {
		pages := tview.NewPages()
		
		// Create mock components
		mainFlex := tview.NewFlex()
		modal := tview.NewModal()
		topicFlex := tview.NewFlex()
		
		// Add pages like OpenUI does
		pages.
			AddPage("main", mainFlex, true, true).
			AddPage("modal", modal, true, false).
			AddPage("topicPage", topicFlex, true, false)
		
		// Test that pages were added
		if pages == nil {
			t.Fatal("Pages configuration failed")
		}
		
		// We can't easily test GetFrontPage without running the UI,
		// but we can verify the pages object was created successfully
	})
	
	t.Run("pages_changed_function", func(t *testing.T) {
		pages := tview.NewPages()
		msgChannel := make(chan UIEvent, 10)
		
		// Set up the changed function like OpenUI does
		pages.SetChangedFunc(func() {
			msgChannel <- OnPageChange
		})
		
		// We can't easily trigger the changed function without UI interaction,
		// but we can verify the setup completed successfully
		if pages == nil {
			t.Error("Pages changed function setup failed")
		}
	})
}

// TestRecoverAndExitFunction tests the RecoverAndExit functionality
func TestRecoverAndExitFunction(t *testing.T) {
	t.Run("recover_function_exists", func(t *testing.T) {
		// Test that RecoverAndExit can be called without panicking
		defer func() {
			if r := recover(); r != nil {
				// This is expected if RecoverAndExit is working
				t.Logf("RecoverAndExit caught panic: %v", r)
			}
		}()
		
		// We can't easily test the actual recovery without creating a real panic,
		// but we can verify the function exists and can be called
		app := tview.NewApplication()
		if app == nil {
			t.Fatal("Failed to create test application")
		}
		
		// The RecoverAndExit function should exist and be callable
		// We'll test this indirectly by ensuring the test doesn't panic
	})
}

// Mock data source for UI testing
type mockDataSourceForUI struct{}

func (m *mockDataSourceForUI) Init(cfgOption string) {}
func (m *mockDataSourceForUI) GetTopics() (map[string]api.Topic, error) {
	return map[string]api.Topic{
		"test-topic": {
			NumPartitions:     1,
			ReplicationFactor: 1,
			MessageCount:      0,
		},
	}, nil
}
func (m *mockDataSourceForUI) GetContexts() ([]string, error) {
	return []string{"test-context"}, nil
}
func (m *mockDataSourceForUI) GetContext() string {
	return "test-context"
}
func (m *mockDataSourceForUI) SetContext(contextName string) error {
	return nil
}
func (m *mockDataSourceForUI) GetConsumerGroups() ([]api.ConsumerGroup, error) {
	return []api.ConsumerGroup{}, nil
}
func (m *mockDataSourceForUI) ConsumeTopic(ctx context.Context, topicName string, flags api.ConsumeFlags, handleMessage api.MessageHandlerFunc, onError func(err any)) error {
	return nil
}