package node

import (
	"io"
	"testing"

	"github.com/go-gost/core/logger"
	"github.com/go-gost/x/config"
	"github.com/go-gost/x/config/parsing"
	xlogger "github.com/go-gost/x/logger"

	// Register connector and dialer implementations needed for node parsing.
	_ "github.com/go-gost/x/connector/http"
	_ "github.com/go-gost/x/dialer/tcp"
)

func TestMain(m *testing.M) {
	logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
	m.Run()
}

func testLogger() logger.Logger {
	return xlogger.NewLogger(xlogger.OutputOption(io.Discard))
}

func TestParseNode_Nil(t *testing.T) {
	n, err := ParseNode("test-hop", nil, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n != nil {
		t.Fatal("expected nil for nil config")
	}
}

func TestParseNode_Defaults(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "node1",
		Addr: "example.com:8080",
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_DefaultsEmptyName(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "",
		Addr: "example.com:8080",
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_DefaultsEmptyAddr(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "no-addr",
		Addr: "",
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_ExplicitConnector(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "explicit-conn",
		Addr: "example.com:8080",
		Connector: &config.ConnectorConfig{
			Type: "http",
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_UnregisteredConnector(t *testing.T) {
	_, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "bad-conn",
		Addr: "example.com:8080",
		Connector: &config.ConnectorConfig{
			Type: "nonexistent-connector-type",
		},
	}, testLogger())
	if err == nil {
		t.Fatal("expected error for unregistered connector type")
	}
}

func TestParseNode_UnregisteredDialer(t *testing.T) {
	_, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "bad-dial",
		Addr: "example.com:8080",
		Dialer: &config.DialerConfig{
			Type: "nonexistent-dialer-type",
		},
	}, testLogger())
	if err == nil {
		t.Fatal("expected error for unregistered dialer type")
	}
}

func TestParseNode_ExplicitDialer(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "explicit-dial",
		Addr: "example.com:8080",
		Dialer: &config.DialerConfig{
			Type: "tcp",
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithBypass(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name:    "bypass-node",
		Addr:    "example.com:8080",
		Bypass:  "bp1",
		Bypasses: []string{"bp2", "bp3"},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithResolver(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name:     "resolver-node",
		Addr:     "example.com:8080",
		Resolver: "my-resolver",
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithHosts(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name:  "hosts-node",
		Addr:  "example.com:8080",
		Hosts: "my-hosts",
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithNetwork(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name:    "network-node",
		Addr:    "example.com:8080",
		Network: "tcp",
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithMetadata(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "meta-node",
		Addr: "example.com:8080",
		Metadata: map[string]any{
			parsing.MDKeySoMark:    1234,
			parsing.MDKeyInterface: "eth0",
			parsing.MDKeyNetns:     "myns",
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithSockOpts(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "sockopts-node",
		Addr: "example.com:8080",
		SockOpts: &config.SockOptsConfig{
			Mark: 9999,
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithFilter(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "filter-node",
		Addr: "example.com:8080",
		Filter: &config.NodeFilterConfig{
			Protocol: "http",
			Host:     "*.example.com",
			Path:     "/api",
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithFilterStarBare(t *testing.T) {
	// "*example.com" should become ".example.com"
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "star-bare-node",
		Addr: "example.com:8080",
		Filter: &config.NodeFilterConfig{
			Host: "*example.com",
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithMatcher(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "matcher-node",
		Addr: "example.com:8080",
		Matcher: &config.NodeMatcherConfig{
			Rule:     "dstport==80",
			Priority: 10,
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithMatcherAutoPriority(t *testing.T) {
	// Priority auto-set to len(rule) when zero
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "auto-prio-node",
		Addr: "example.com:8080",
		Matcher: &config.NodeMatcherConfig{
			Rule:     "dstport==443",
			Priority: 0,
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithHTTPNode(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "http-node",
		Addr: "example.com:8080",
		HTTP: &config.HTTPNodeConfig{
			Host:           "custom.example.com",
			RequestHeader:  map[string]string{"X-Custom": "value"},
			ResponseHeader: map[string]string{"X-Response": "value"},
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithHTTPAuth(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "http-auth-node",
		Addr: "example.com:8080",
		HTTP: &config.HTTPNodeConfig{
			Auth: &config.AuthConfig{
				Username: "user",
				Password: "pass",
			},
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithHTTPDeprecatedHeader(t *testing.T) {
	// Deprecated Header field should populate RequestHeader
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "http-deprecated-node",
		Addr: "example.com:8080",
		HTTP: &config.HTTPNodeConfig{
			Header: map[string]string{"X-Old": "value"},
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_WithTLSNode(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "tls-node",
		Addr: "example.com:8080",
		TLS: &config.TLSNodeConfig{
			ServerName: "secure.example.com",
			Secure:     true,
			Options: &config.TLSOptions{
				MinVersion:   "1.2",
				MaxVersion:   "1.3",
				CipherSuites: []string{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"},
				ALPN:         []string{"h2", "http/1.1"},
			},
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_TLSServerNameFromAddr(t *testing.T) {
	// When connector/dialer TLS ServerName is empty, it defaults from addr hostname.
	// The server name is set on the connector's TLS config (internal), so the best
	// verification is that ParseNode does not error and returns a node.
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "tls-servername-node",
		Addr: "tls.example.com:443",
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_ConnectorAuth(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "conn-auth-node",
		Addr: "example.com:8080",
		Connector: &config.ConnectorConfig{
			Type: "http",
			Auth: &config.AuthConfig{
				Username: "connuser",
				Password: "connpass",
			},
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_DialerAuth(t *testing.T) {
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "dial-auth-node",
		Addr: "example.com:8080",
		Dialer: &config.DialerConfig{
			Type: "tcp",
			Auth: &config.AuthConfig{
				Username: "dialuser",
				Password: "dialpass",
			},
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}

func TestParseNode_InvalidMatcherRule(t *testing.T) {
	// An invalid matcher rule sets priority to -1
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "invalid-matcher-node",
		Addr: "example.com:8080",
		Matcher: &config.NodeMatcherConfig{
			Rule: "",
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node even with empty matcher rule")
	}
}

func TestParseNode_DeprecatedFilterFields(t *testing.T) {
	// Deprecated filter fields: Protocol/Host/Path via Filter
	n, err := ParseNode("test-hop", &config.NodeConfig{
		Name: "deprecated-node",
		Addr: "example.com:8080",
		Filter: &config.NodeFilterConfig{
			Protocol: "socks5",
			Host:     "*.example.com",
			Path:     "/proxy",
		},
	}, testLogger())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n == nil {
		t.Fatal("expected non-nil node")
	}
}
