package config

import (
	"testing"
	"time"
)

// validConfigForNode returns a valid config
func validConfigForNode() *Config {
	validPeer := "/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWHbcFcrGPXKUrHcxvd8MXEeUzRYyvY8fQcpEBxncSUwhj"
	cfg := &Config{
		Node: NodeConfig{
			ID:              "test-node-id",
			ListenAddresses: []string{"/ip4/0.0.0.0/tcp/4001"},
			DataDir:         ".",
			MaxConnections:  50,
		},
		Database: DatabaseConfig{
			DataDir:           ".",
			ReplicationFactor: 3,
			ShardCount:        16,
			MaxDatabaseSize:   1024,
			BackupInterval:    1 * time.Hour,
			RQLitePort:        5001,
			RQLiteRaftPort:    7001,
			MinClusterSize:    1,
			RQLiteJoinAddress: "", // Optional - first node creates cluster, others join
		},
		Discovery: DiscoveryConfig{
			BootstrapPeers:    []string{validPeer},
			DiscoveryInterval: 15 * time.Second,
			BootstrapPort:     4001,
			HttpAdvAddress:    "localhost:5001",
			RaftAdvAddress:    "localhost:7001",
			NodeNamespace:     "default",
		},
		Logging: LoggingConfig{
			Level:  "info",
			Format: "console",
		},
	}

	return cfg
}

func TestValidateListenAddresses(t *testing.T) {
	tests := []struct {
		name        string
		addresses   []string
		shouldError bool
	}{
		{"valid single", []string{"/ip4/0.0.0.0/tcp/4001"}, false},
		{"valid ipv6", []string{"/ip6/::/tcp/4001"}, false},
		{"invalid port", []string{"/ip4/0.0.0.0/tcp/99999"}, true},
		{"invalid port zero", []string{"/ip4/0.0.0.0/tcp/0"}, true},
		{"invalid multiaddr", []string{"invalid"}, true},
		{"empty", []string{}, true},
		{"duplicate", []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/tcp/4001"}, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Node.ListenAddresses = tt.addresses
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidateReplicationFactor(t *testing.T) {
	tests := []struct {
		name        string
		replication int
		shouldError bool
	}{
		{"valid 1", 1, false},
		{"valid 3", 3, false},
		{"even replication factor", 2, true}, // even numbers are invalid for Raft quorum
		{"invalid zero", 0, true},
		{"invalid negative", -1, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Database.ReplicationFactor = tt.replication
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidateRQLitePorts(t *testing.T) {
	tests := []struct {
		name        string
		httpPort    int
		raftPort    int
		shouldError bool
	}{
		{"valid different", 5001, 7001, false},
		{"invalid same", 5001, 5001, true},
		{"invalid http port zero", 0, 7001, true},
		{"invalid raft port zero", 5001, 0, true},
		{"invalid http port too high", 99999, 7001, true},
		{"invalid raft port too high", 5001, 99999, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Database.RQLitePort = tt.httpPort
			cfg.Database.RQLiteRaftPort = tt.raftPort
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidateRQLiteJoinAddress(t *testing.T) {
	tests := []struct {
		name        string
		joinAddr    string
		shouldError bool
	}{
		{"node with join", "localhost:5001", false},
		{"node without join", "", false}, // Join address is optional (first node creates cluster)
		{"invalid join format", "localhost", true},
		{"invalid join port", "localhost:99999", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Database.RQLiteJoinAddress = tt.joinAddr
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidatePeerAddresses(t *testing.T) {
	validPeer := "/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWHbcFcrGPXKUrHcxvd8MXEeUzRYyvY8fQcpEBxncSUwhj"
	tests := []struct {
		name        string
		peers       []string
		shouldError bool
	}{
		{"node with peer", []string{validPeer}, false},
		{"node without peer", []string{}, false}, // All nodes are unified peers - bootstrap peers optional
		{"invalid multiaddr", []string{"invalid"}, true},
		{"missing p2p", []string{"/ip4/127.0.0.1/tcp/4001"}, true},
		{"duplicate peer", []string{validPeer, validPeer}, true},
		{"invalid port", []string{"/ip4/127.0.0.1/tcp/99999/p2p/12D3KooWHbcFcrGPXKUrHcxvd8MXEeUzRYyvY8fQcpEBxncSUwhj"}, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Discovery.BootstrapPeers = tt.peers
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidateLoggingLevel(t *testing.T) {
	tests := []struct {
		name        string
		level       string
		shouldError bool
	}{
		{"debug", "debug", false},
		{"info", "info", false},
		{"warn", "warn", false},
		{"error", "error", false},
		{"invalid", "verbose", true},
		{"empty", "", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Logging.Level = tt.level
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidateLoggingFormat(t *testing.T) {
	tests := []struct {
		name        string
		format      string
		shouldError bool
	}{
		{"json", "json", false},
		{"console", "console", false},
		{"invalid", "text", true},
		{"empty", "", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Logging.Format = tt.format
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidateMaxConnections(t *testing.T) {
	tests := []struct {
		name        string
		maxConn     int
		shouldError bool
	}{
		{"valid 50", 50, false},
		{"valid 1", 1, false},
		{"invalid zero", 0, true},
		{"invalid negative", -1, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Node.MaxConnections = tt.maxConn
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidateDiscoveryInterval(t *testing.T) {
	tests := []struct {
		name        string
		interval    time.Duration
		shouldError bool
	}{
		{"valid 15s", 15 * time.Second, false},
		{"valid 1s", 1 * time.Second, false},
		{"invalid zero", 0, true},
		{"invalid negative", -5 * time.Second, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Discovery.DiscoveryInterval = tt.interval
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidatePeerDiscoveryPort(t *testing.T) {
	tests := []struct {
		name        string
		port        int
		shouldError bool
	}{
		{"valid 4001", 4001, false},
		{"valid 4002", 4002, false},
		{"invalid zero", 0, true},
		{"invalid too high", 99999, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := validConfigForNode()
			cfg.Discovery.BootstrapPort = tt.port
			errs := cfg.Validate()
			if tt.shouldError && len(errs) == 0 {
				t.Errorf("expected error, got none")
			}
			if !tt.shouldError && len(errs) > 0 {
				t.Errorf("unexpected errors: %v", errs)
			}
		})
	}
}

func TestValidateCompleteConfig(t *testing.T) {
	// Test a complete valid config
	validCfg := &Config{
		Node: NodeConfig{
			ID:              "node1",
			ListenAddresses: []string{"/ip4/0.0.0.0/tcp/4002"},
			DataDir:         ".",
			MaxConnections:  50,
		},
		Database: DatabaseConfig{
			DataDir:           ".",
			ReplicationFactor: 3,
			ShardCount:        16,
			MaxDatabaseSize:   1073741824,
			BackupInterval:    24 * time.Hour,
			RQLitePort:        5002,
			RQLiteRaftPort:    7002,
			RQLiteJoinAddress: "localhost:7001",
			MinClusterSize:    1,
		},
		Discovery: DiscoveryConfig{
			BootstrapPeers: []string{
				"/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWHbcFcrGPXKUrHcxvd8MXEeUzRYyvY8fQcpEBxncSUwhj",
			},
			DiscoveryInterval: 15 * time.Second,
			BootstrapPort:     4001,
			HttpAdvAddress:    "localhost:5001",
			RaftAdvAddress:    "localhost:7001",
			NodeNamespace:     "default",
		},
		Security: SecurityConfig{
			EnableTLS: false,
		},
		Logging: LoggingConfig{
			Level:  "info",
			Format: "console",
		},
	}

	errs := validCfg.Validate()
	if len(errs) > 0 {
		t.Errorf("valid config should not have errors: %v", errs)
	}
}
