// Copyright 2025.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ui

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

func TestValidateHost(t *testing.T) {
	tests := []struct {
		name    string
		host    string
		wantErr bool
	}{
		{"Valid IP", "192.168.1.1", false},
		{"Valid hostname", "example.com", false},
		{"Valid subdomain", "api.example.com", false},
		{"Empty host", "", true},
		{"Host with spaces", "example .com", true},
		{"Host with invalid chars", "example@com", true},
		{"Host starting with dot", ".example.com", true},
		{"Host ending with dot", "example.com.", true},
		{"Host with empty label", "example..com", true},
		{"Label starting with hyphen", "-example.com", true},
		{"Label ending with hyphen", "example-.com", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateHost(tt.host)
			if (err != nil) != tt.wantErr {
				t.Errorf("validateHost(%s) error = %v, wantErr %v", tt.host, err, tt.wantErr)
			}
		})
	}
}

func TestValidatePortForward(t *testing.T) {
	tests := []struct {
		name    string
		forward string
		wantErr bool
	}{
		{"Valid simple forward", "8080:localhost:80", false},
		{"Valid with bind address", "127.0.0.1:8080:localhost:80", false},
		{"Multiple forwards", "8080:localhost:80, 3000:localhost:3000", false},
		{"Empty forward", "", false},
		{"Invalid format - too few parts", "8080:localhost", true},
		{"Invalid format - too many parts", "127.0.0.1:8080:localhost:80:extra", true},
		{"Invalid port number", "abc:localhost:80", true},
		{"Port out of range", "70000:localhost:80", true},
		{"Invalid bind address - malformed IP", "127.0.0.0.0.0.1:8080:localhost:80", true},
		{"Invalid bind address - IP out of range", "192.168.1.256:8080:localhost:80", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validatePortForward(tt.forward)
			if (err != nil) != tt.wantErr {
				t.Errorf("validatePortForward(%s) error = %v, wantErr %v", tt.forward, err, tt.wantErr)
			}
		})
	}
}

func TestValidateDynamicForward(t *testing.T) {
	tests := []struct {
		name    string
		forward string
		wantErr bool
	}{
		{"Valid port only", "1080", false},
		{"Valid with bind address", "127.0.0.1:1080", false},
		{"Multiple forwards", "1080, 1081", false},
		{"Empty forward", "", false},
		{"Invalid format - too many parts", "127.0.0.1:1080:extra", true},
		{"Invalid port number", "abc", true},
		{"Port out of range", "70000", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateDynamicForward(tt.forward)
			if (err != nil) != tt.wantErr {
				t.Errorf("validateDynamicForward(%s) error = %v, wantErr %v", tt.forward, err, tt.wantErr)
			}
		})
	}
}

func TestValidateBindAddress(t *testing.T) {
	tests := []struct {
		name    string
		address string
		wantErr bool
	}{
		{"Valid IP", "192.168.1.1", false},
		{"Valid IPv6", "::1", false},
		{"Valid hostname", "example.com", false},
		{"Wildcard", "*", false},
		{"Localhost", "localhost", false},
		{"Empty address", "", false},
		{"Address with spaces", "example .com", true},
		{"Address with invalid chars", "example@com", true},
		{"Address starting with dot", ".example.com", true},
		{"Address ending with dot", "example.com.", true},
		{"Address starting with hyphen", "-example.com", true},
		{"Address ending with hyphen", "example-.com", true},
		{"Invalid IP-like address", "127.0.0.0.0.0.1", true},
		{"Invalid numeric hostname", "192.168.1.256", true},
		{"Multiple dots", "example..com", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateBindAddress(tt.address)
			if (err != nil) != tt.wantErr {
				t.Errorf("validateBindAddress(%s) error = %v, wantErr %v", tt.address, err, tt.wantErr)
			}
		})
	}
}

func TestValidateKeyPaths(t *testing.T) {
	// Prepare an isolated HOME with a mock .ssh folder and key files
	oldHome := os.Getenv("HOME")
	t.Cleanup(func() {
		_ = os.Setenv("HOME", oldHome)
	})

	tempHome := t.TempDir()
	sshDir := filepath.Join(tempHome, ".ssh")
	if err := os.MkdirAll(sshDir, 0o755); err != nil {
		t.Fatalf("failed to create temp .ssh dir: %v", err)
	}

	shouldExistFiles := []string{"id_rsa", "id_ed25519"}
	for _, name := range shouldExistFiles {
		p := filepath.Join(sshDir, name)
		if err := os.WriteFile(p, []byte("test"), 0o644); err != nil {
			t.Fatalf("failed to create mock key file %s: %v", p, err)
		}
	}
	if err := os.Setenv("HOME", tempHome); err != nil {
		t.Fatalf("failed to set HOME: %v", err)
	}

	tests := []struct {
		name    string
		keys    string
		wantErr bool
	}{
		{"Valid single path", "~/.ssh/id_rsa", false},
		{"Valid multiple paths", "~/.ssh/id_rsa, ~/.ssh/id_ed25519", false},
		{"Empty keys", "", false},
		{"Path with newline", "~/.ssh/id_rsa\n", true},
		{"Path with tab", "~/.ssh/id_rsa\t", true},
		{"Path with carriage return", "~/.ssh/id_rsa\r", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateKeyPaths(tt.keys)
			if (err != nil) != tt.wantErr {
				t.Errorf("validateKeyPaths(%s) error = %v, wantErr %v", tt.keys, err, tt.wantErr)
			}
		})
	}
}

func TestFieldValidatorPatterns(t *testing.T) {
	fieldValidators := GetFieldValidators()

	tests := []struct {
		field   string
		value   string
		wantErr bool
	}{
		// Alias field
		{"Alias", "server-01", false},
		{"Alias", "server_01", false},
		{"Alias", "server.01", false},
		{"Alias", "server@01", true},
		{"Alias", "", true}, // Required field

		// Port field
		{"Port", "22", false},
		{"Port", "65535", false},
		{"Port", "0", true},
		{"Port", "65536", true},
		{"Port", "abc", true},

		// User field
		{"User", "root", false},
		{"User", "user_name", false},
		{"User", "user-name", false},
		{"User", "1user", true}, // Can't start with number

		// ConnectTimeout field
		{"ConnectTimeout", "none", false},
		{"ConnectTimeout", "30", false},
		{"ConnectTimeout", "0", true},
		{"ConnectTimeout", "-10", true},

		// IPQoS field
		{"IPQoS", "af21 cs1", false},
		{"IPQoS", "ef", false},
		{"IPQoS", "lowdelay", false},
		{"IPQoS", "invalid", true},

		// EscapeChar field
		{"EscapeChar", "~", false},
		{"EscapeChar", "none", false},
		{"EscapeChar", "^A", false},
		{"EscapeChar", "^z", false},
		{"EscapeChar", "invalid", true},
	}

	for _, tt := range tests {
		t.Run(tt.field+"_"+tt.value, func(t *testing.T) {
			validator, exists := fieldValidators[tt.field]
			if !exists {
				if tt.wantErr {
					t.Errorf("Expected validator for field %s but none found", tt.field)
				}
				return
			}

			var err error
			// Check required fields
			switch {
			case validator.Required && tt.value == "":
				err = &testError{msg: "required field is empty"}
			case validator.Pattern != nil && !validator.Pattern.MatchString(tt.value):
				err = &testError{msg: "pattern mismatch"}
			case validator.Validate != nil:
				err = validator.Validate(tt.value)
			}

			if (err != nil) != tt.wantErr {
				t.Errorf("validateField(%s, %s) error = %v, wantErr %v", tt.field, tt.value, err, tt.wantErr)
			}
		})
	}
}

// testError is a helper type for testing
type testError struct {
	msg string
}

func (e *testError) Error() string {
	return e.msg
}

func TestValidationState_MultipleErrors(t *testing.T) {
	state := NewValidationState()

	// Set multiple errors
	state.SetError("Alias", "Alias is required")
	state.SetError("Host", "Host is required")
	state.SetError("Port", "Port must be between 1 and 65535")
	state.SetError("User", "Invalid username")

	// Check that we have errors
	if !state.HasErrors() {
		t.Error("Expected HasErrors to return true")
	}

	// Get all errors
	errors := state.GetAllErrors()

	// Should have 4 errors
	if len(errors) != 4 {
		t.Errorf("Expected 4 errors, got %d", len(errors))
	}

	// Print errors for debugging
	t.Logf("Found %d errors:", len(errors))
	for i, err := range errors {
		t.Logf("  %d. %s", i+1, err)
	}

	// Check that errors are in the expected order
	expectedOrder := []string{"Alias", "Host", "Port", "User"}
	for i, expectedField := range expectedOrder {
		if i >= len(errors) {
			break
		}
		// Check if the error message starts with the expected field name
		if len(errors[i]) < len(expectedField) || errors[i][:len(expectedField)] != expectedField {
			t.Errorf("Expected error %d to be for field %s, but got: %s", i, expectedField, errors[i])
		}
	}
}

func TestValidationState_Clear(t *testing.T) {
	state := NewValidationState()

	// Add some errors
	state.SetError("Alias", "Error 1")
	state.SetError("Host", "Error 2")

	// Clear all errors
	state.Clear()

	// Should have no errors
	if state.HasErrors() {
		t.Error("Expected no errors after Clear()")
	}

	if state.GetErrorCount() != 0 {
		t.Errorf("Expected error count to be 0, got %d", state.GetErrorCount())
	}
}
