//go:build linux

package proc

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/pranshuparmar/witr/pkg/model"
)

// GetSocketStateForPort returns the socket state for a port
// Linux implementation using /proc/net/tcp and /proc/net/tcp6
func GetSocketStateForPort(port int) *model.SocketInfo {
	// Check both IPv4 and IPv6
	files := []string{"/proc/net/tcp", "/proc/net/tcp6"}

	var states []model.SocketInfo

	for _, file := range files {
		isIPv6 := strings.HasSuffix(file, "tcp6")

		func() {
			f, err := os.Open(file)
			if err != nil {
				return
			}
			defer f.Close()

			scanner := bufio.NewScanner(f)
			scanner.Scan()

			for scanner.Scan() {
				fields := strings.Fields(scanner.Text())
				if len(fields) < 10 {
					continue
				}

				localAddrHex := fields[1]
				localIP, localPort := parseAddr(localAddrHex, isIPv6)

				if localPort != port {
					continue
				}

				remoteAddrHex := fields[2]
				remoteIP, _ := parseAddr(remoteAddrHex, isIPv6)

				stateHex := fields[3]
				stateVal, _ := strconv.ParseInt(stateHex, 16, 0)
				stateStr := mapTCPState(int(stateVal))

				info := model.SocketInfo{
					Port:       port,
					State:      stateStr,
					LocalAddr:  localIP,
					RemoteAddr: remoteIP,
				}

				addStateExplanation(&info)
				states = append(states, info)
			}
		}()
	}

	if len(states) == 0 {
		return nil
	}

	// Prioritize problematic states just like the Darwin implementation
	for _, s := range states {
		if isProblematicState(s.State) {
			return &s
		}
	}

	// Then prioritize LISTEN
	for _, s := range states {
		if s.State == "LISTEN" {
			return &s
		}
	}

	// Default to first found
	return &states[0]
}

// mapTCPState maps Linux kernel TCP states (from include/net/tcp_states.h) to strings
func mapTCPState(state int) string {
	switch state {
	case 1:
		return "ESTABLISHED"
	case 2:
		return "SYN_SENT"
	case 3:
		return "SYN_RECV"
	case 4:
		return "FIN_WAIT_1"
	case 5:
		return "FIN_WAIT_2"
	case 6:
		return "TIME_WAIT"
	case 7:
		return "CLOSE"
	case 8:
		return "CLOSE_WAIT"
	case 9:
		return "LAST_ACK"
	case 10:
		return "LISTEN"
	case 11:
		return "CLOSING"
	default:
		return fmt.Sprintf("UNKNOWN (%02X)", state)
	}
}

func isProblematicState(state string) bool {
	switch state {
	case "TIME_WAIT", "CLOSE_WAIT", "FIN_WAIT_1", "FIN_WAIT_2":
		return true
	}
	return false
}

func addStateExplanation(info *model.SocketInfo) {
	switch info.State {
	case "LISTEN":
		info.Explanation = "Actively listening for connections"
	case "TIME_WAIT":
		info.Explanation = "Connection closed, waiting for delayed packets"
		info.Workaround = "Wait for timeout (usually 60s) or use SO_REUSEADDR"
	case "CLOSE_WAIT":
		info.Explanation = "Remote side closed connection, local side has not closed yet"
		info.Workaround = "The application should call close() on the socket"
	case "FIN_WAIT_1":
		info.Explanation = "Local side initiated close, waiting for acknowledgment"
	case "FIN_WAIT_2":
		info.Explanation = "Local close acknowledged, waiting for remote close"
	case "ESTABLISHED":
		info.Explanation = "Active connection"
	case "SYN_SENT":
		info.Explanation = "Connection request sent, waiting for response"
	case "SYN_RECEIVED":
		info.Explanation = "Connection request received, sending acknowledgment"
	case "CLOSING":
		info.Explanation = "Both sides initiated close simultaneously"
	case "LAST_ACK":
		info.Explanation = "Waiting for final acknowledgment of close"
	default:
		info.Explanation = "Socket in " + info.State + " state"
	}
}
