package collector

import (
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

type PFStates struct {
	CurrentEntries int     `json:"current_entries"`
	HardLimit      int     `json:"hard_limit"`
	SearchRate     float64 `json:"search_rate"`
	InsertRate     float64 `json:"insert_rate"`
	RemovalRate    float64 `json:"removal_rate"`
	MatchRate      float64 `json:"match_rate"`
}

func CollectPFStates() *PFStates {
	result := &PFStates{}

	// pfctl -si for state table info
	if out, err := exec.Command("/sbin/pfctl", "-si").Output(); err == nil {
		lines := strings.Split(string(out), "\n")
		rateRe := regexp.MustCompile(`([\d.]+)/s`)
		for _, line := range lines {
			lower := strings.TrimSpace(line)
			if strings.HasPrefix(lower, "current entries") {
				result.CurrentEntries = parseLastInt(lower)
			} else if strings.HasPrefix(lower, "searches") {
				if m := rateRe.FindStringSubmatch(line); len(m) > 1 {
					result.SearchRate, _ = strconv.ParseFloat(m[1], 64)
				}
			} else if strings.HasPrefix(lower, "inserts") {
				if m := rateRe.FindStringSubmatch(line); len(m) > 1 {
					result.InsertRate, _ = strconv.ParseFloat(m[1], 64)
				}
			} else if strings.HasPrefix(lower, "removals") {
				if m := rateRe.FindStringSubmatch(line); len(m) > 1 {
					result.RemovalRate, _ = strconv.ParseFloat(m[1], 64)
				}
			} else if strings.HasPrefix(lower, "match") {
				if m := rateRe.FindStringSubmatch(line); len(m) > 1 {
					result.MatchRate, _ = strconv.ParseFloat(m[1], 64)
				}
			}
		}
	}

	// pfctl -sm for hard limit
	if out, err := exec.Command("/sbin/pfctl", "-sm").Output(); err == nil {
		for _, line := range strings.Split(string(out), "\n") {
			if strings.Contains(line, "states") && strings.Contains(line, "hard limit") {
				result.HardLimit = parseLastInt(line)
				break
			}
		}
	}

	return result
}

func parseLastInt(line string) int {
	fields := strings.Fields(line)
	for i := len(fields) - 1; i >= 0; i-- {
		if v, err := strconv.Atoi(fields[i]); err == nil {
			return v
		}
	}
	return 0
}
