package infra

import (
	"bufio"
	"fmt"
	"net"
	"strings"
	"testing"
)

// mockSMTPServer starts a minimal SMTP server on a random port that greets
// and accepts any commands. Returns the listener address and a function to stop it.
func mockSMTPServer(t *testing.T) (addr string, stop func()) {
	t.Helper()
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("mock smtp listen: %v", err)
	}
	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				return
			}
			go handleMockSMTP(conn)
		}
	}()
	return ln.Addr().String(), func() { ln.Close() }
}

// handleMockSMTP is a minimal SMTP session handler (no TLS, no auth required).
func handleMockSMTP(conn net.Conn) {
	defer conn.Close()
	w := bufio.NewWriter(conn)
	r := bufio.NewReader(conn)
	fmt.Fprintf(w, "220 mock SMTP ready\r\n")
	w.Flush()
	for {
		line, err := r.ReadString('\n')
		if err != nil {
			return
		}
		line = strings.TrimSpace(line)
		upper := strings.ToUpper(line)
		switch {
		case strings.HasPrefix(upper, "EHLO"), strings.HasPrefix(upper, "HELO"):
			fmt.Fprintf(w, "250 mock\r\n")
		case strings.HasPrefix(upper, "MAIL FROM"):
			fmt.Fprintf(w, "250 OK\r\n")
		case strings.HasPrefix(upper, "RCPT TO"):
			fmt.Fprintf(w, "250 OK\r\n")
		case strings.HasPrefix(upper, "DATA"):
			fmt.Fprintf(w, "354 End with .\r\n")
			w.Flush()
			// read until ".\r\n"
			for {
				dl, derr := r.ReadString('\n')
				if derr != nil {
					return
				}
				if strings.TrimSpace(dl) == "." {
					break
				}
			}
			fmt.Fprintf(w, "250 OK\r\n")
		case strings.HasPrefix(upper, "QUIT"):
			fmt.Fprintf(w, "221 Bye\r\n")
			w.Flush()
			return
		default:
			fmt.Fprintf(w, "502 Command not recognized\r\n")
		}
		w.Flush()
	}
}

func TestSMTPConnect(t *testing.T) {
	t.Run("conecta sin cifrado a servidor mock", func(t *testing.T) {
		addr, stop := mockSMTPServer(t)
		defer stop()

		host, portStr, _ := net.SplitHostPort(addr)
		port := 0
		fmt.Sscanf(portStr, "%d", &port)

		cfg := SMTPConfig{
			Host:    host,
			Port:    port,
			TLSMode: "", // no encryption
		}
		client, err := SMTPConnect(cfg)
		if err != nil {
			t.Fatalf("SMTPConnect: %v", err)
		}
		client.Quit()
	})

	t.Run("error si el servidor no existe", func(t *testing.T) {
		// Use a port that is listening but immediately closes (RST) — we start
		// and immediately close a listener so the port is known-closed.
		ln2, err := net.Listen("tcp", "127.0.0.1:0")
		if err != nil {
			t.Fatalf("setup: %v", err)
		}
		h2, p2str, _ := net.SplitHostPort(ln2.Addr().String())
		ln2.Close() // now nothing listens — connections get refused
		p2 := 0
		fmt.Sscanf(p2str, "%d", &p2)

		cfg := SMTPConfig{
			Host:    h2,
			Port:    p2,
			TLSMode: "",
		}
		_, connErr := SMTPConnect(cfg)
		if connErr == nil {
			t.Error("expected error for refused connection, got nil")
		}
	})
}
