package health

import (
	"context"
	"net"
	"strconv"
	"strings"
	"testing"
	"time"
)

func TestSMTPChecker_Check_NoAuth(t *testing.T) {
	config := SMTPConfig{
		Host: "smtp.example.com",
		Port: 25,
	}

	checker := NewSMTPChecker(config)

	if checker.config.Username != "" {
		t.Error("expected empty username")
	}
	if checker.config.Password != "" {
		t.Error("expected empty password")
	}
}

func TestSMTPChecker_Check_InvalidPort(t *testing.T) {
	config := SMTPConfig{
		Host: "localhost",
		Port: 99999,
	}

	checker := NewSMTPChecker(config)
	ctx := context.Background()

	result := checker.Check(ctx)

	if result.Status != StatusUnhealthy {
		t.Errorf("expected unhealthy status for invalid port, got %s", result.Status)
	}
}

func TestSMTPChecker_Check_ConnectionRefused(t *testing.T) {
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("failed to create listener: %v", err)
	}
	port := listener.Addr().(*net.TCPAddr).Port
	listener.Close()

	config := SMTPConfig{
		Host: "127.0.0.1",
		Port: port,
	}

	checker := NewSMTPChecker(config)
	ctx := context.Background()

	result := checker.Check(ctx)

	if result.Status != StatusUnhealthy {
		t.Errorf("expected unhealthy status for connection refused, got %s", result.Status)
	}
}

func TestSMTPChecker_Check_WithMockServer(t *testing.T) {
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("failed to create listener: %v", err)
	}
	defer listener.Close()

	port := listener.Addr().(*net.TCPAddr).Port

	serverDone := make(chan bool)
	go func() {
		defer close(serverDone)
		conn, err := listener.Accept()
		if err != nil {
			return
		}
		defer conn.Close()

		conn.Write([]byte("220 test.example.com ESMTP ready\r\n"))

		buf := make([]byte, 1024)
		n, _ := conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("250-test.example.com\r\n250-STARTTLS\r\n250 AUTH PLAIN\r\n"))
		}

		n, _ = conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("220 2.0.0 Ready to start TLS\r\n"))
		}

		n, _ = conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("235 2.7.0 Authentication successful\r\n"))
		}

		n, _ = conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("221 2.0.0 Bye\r\n"))
		}
	}()

	config := SMTPConfig{
		Host:     "127.0.0.1",
		Port:     port,
		Username: "test@example.com",
		Password: "password",
	}

	checker := NewSMTPChecker(config)
	ctx := context.Background()

	result := checker.Check(ctx)

	select {
	case <-serverDone:
	case <-time.After(2 * time.Second):
		t.Error("server timeout")
	}

	if result.Status != StatusHealthy && result.Status != StatusDegraded {
		t.Errorf("unexpected status: %s", result.Status)
	}

	if result.Details["host"] == nil {
		t.Errorf("expected host in details, got %v", result.Details["host"])
	}
}

func TestSMTPChecker_Check_EHLOFailure(t *testing.T) {
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("failed to create listener: %v", err)
	}
	defer listener.Close()

	port := listener.Addr().(*net.TCPAddr).Port

	serverDone := make(chan bool)
	go func() {
		defer close(serverDone)
		conn, err := listener.Accept()
		if err != nil {
			return
		}
		defer conn.Close()

		conn.Write([]byte("220 test.example.com ESMTP ready\r\n"))

		buf := make([]byte, 1024)
		n, _ := conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("500 Syntax error, command unrecognized\r\n"))
		}
	}()

	config := SMTPConfig{
		Host: "127.0.0.1",
		Port: port,
	}

	checker := NewSMTPChecker(config)
	ctx := context.Background()

	result := checker.Check(ctx)

	select {
	case <-serverDone:
	case <-time.After(2 * time.Second):
		t.Error("server timeout")
	}

	if result.Status != StatusDegraded {
		t.Errorf("expected degraded status for EHLO failure, got %s", result.Status)
	}

	if !strings.Contains(result.Message, "EHLO") {
		t.Errorf("expected EHLO error in message, got: %s", result.Message)
	}
}

func TestSMTPChecker_Check_STARTTLSNotSupported(t *testing.T) {
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("failed to create listener: %v", err)
	}
	defer listener.Close()

	port := listener.Addr().(*net.TCPAddr).Port

	serverDone := make(chan bool)
	go func() {
		defer close(serverDone)
		conn, err := listener.Accept()
		if err != nil {
			return
		}
		defer conn.Close()

		conn.Write([]byte("220 test.example.com ESMTP ready\r\n"))

		buf := make([]byte, 1024)
		n, _ := conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("250-test.example.com\r\n250 AUTH PLAIN\r\n"))
		}

		n, _ = conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("221 2.0.0 Bye\r\n"))
		}
	}()

	config := SMTPConfig{
		Host: "127.0.0.1",
		Port: port,
	}

	checker := NewSMTPChecker(config)
	ctx := context.Background()

	result := checker.Check(ctx)

	select {
	case <-serverDone:
	case <-time.After(2 * time.Second):
		t.Error("server timeout")
	}

	if result.Details["starttls"] != "not supported" && result.Details["starttls"] != "failed" {
		t.Errorf("expected starttls not supported or failed, got: %v", result.Details["starttls"])
	}
}

func TestSMTPChecker_Check_AuthNotConfigured(t *testing.T) {
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("failed to create listener: %v", err)
	}
	defer listener.Close()

	port := listener.Addr().(*net.TCPAddr).Port

	serverDone := make(chan bool)
	go func() {
		defer close(serverDone)
		conn, err := listener.Accept()
		if err != nil {
			return
		}
		defer conn.Close()

		conn.Write([]byte("220 test.example.com ESMTP ready\r\n"))

		buf := make([]byte, 1024)
		n, _ := conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("250-test.example.com\r\n250-STARTTLS\r\n250 AUTH PLAIN\r\n"))
		}

		n, _ = conn.Read(buf)
		if n > 0 {
			conn.Write([]byte("221 2.0.0 Bye\r\n"))
		}
	}()

	config := SMTPConfig{
		Host: "127.0.0.1",
		Port: port,
	}

	checker := NewSMTPChecker(config)
	ctx := context.Background()

	result := checker.Check(ctx)

	select {
	case <-serverDone:
	case <-time.After(2 * time.Second):
		t.Error("server timeout")
	}

	if result.Details["auth"] != "not configured" {
		t.Errorf("expected auth not configured, got: %v", result.Details["auth"])
	}
}

func TestSMTPConfig_GetAddress(t *testing.T) {
	config := SMTPConfig{
		Host: "smtp.example.com",
		Port: 587,
	}

	address := getSMTPAddress(config)
	expected := "smtp.example.com:587"
	if address != expected {
		t.Errorf("expected address %s, got %s", expected, address)
	}
}

func getSMTPAddress(config SMTPConfig) string {
	return config.Host + ":" + strconv.Itoa(config.Port)
}
