package listener

import (
	"context"
	"fmt"
	"log"
	"net"
	"strconv"
	"time"

	"tps.local/labbridge-agent/internal/astm"
	syncpkg "tps.local/labbridge-agent/internal/sync"
)

// tcpListener wraps a net.Listener so the manager can Close() it.
type tcpListener struct{ ln net.Listener }

func (t *tcpListener) Close() error { return t.ln.Close() }

// startTCP opens a TCP listener for one device. The analyzer connects in,
// runs ASTM E1381, and the assembled record stream is handed to sink.
func startTCP(ctx context.Context, dev syncpkg.Device, sink Sink) (*tcpListener, error) {
	if dev.Port <= 0 {
		return nil, fmt.Errorf("port not set")
	}
	addr := ":" + strconv.Itoa(dev.Port)
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, fmt.Errorf("listen %s: %w", addr, err)
	}

	go func() {
		<-ctx.Done()
		_ = ln.Close()
	}()

	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				if ctx.Err() != nil {
					return // graceful shutdown
				}
				log.Printf("listener: accept on %s: %v", addr, err)
				continue
			}
			go handleConn(dev, conn, sink)
		}
	}()

	return &tcpListener{ln: ln}, nil
}

func handleConn(dev syncpkg.Device, conn net.Conn, sink Sink) {
	defer conn.Close()
	peer := conn.RemoteAddr().String()
	log.Printf("listener: %s connected on dev %d (%s)", peer, dev.ID, dev.Name)

	sess, err := astm.Receive(conn)
	if err != nil {
		log.Printf("listener: %s ASTM error: %v", peer, err)
		return
	}
	if sess.Records == "" {
		log.Printf("listener: %s empty session — ignoring", peer)
		return
	}
	deviceID := dev.ID
	sink(&deviceID, dev.Vendor, sess.Records, peer, time.Now())
}
