package collector

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

type HAProxyInfo struct {
	Uptime       string `json:"uptime"`
	CurrentConns int    `json:"current_conns"`
	TotalConns   int    `json:"total_conns"`
	TotalReqs    int    `json:"total_requests"`
	CurrentSSL   int    `json:"current_ssl"`
	TotalSSL     int    `json:"total_ssl"`
	MaxConns     int    `json:"max_conns"`
}

type HAProxyEntry struct {
	ProxyName       string `json:"proxy_name"`
	ServerName      string `json:"server_name"`
	Type            string `json:"type"`
	Status          string `json:"status"`
	CurrentSessions int    `json:"current_sessions"`
	TotalSessions   int    `json:"total_sessions"`
	BytesIn         int64  `json:"bytes_in"`
	BytesOut        int64  `json:"bytes_out"`
	RequestsTotal   int    `json:"requests_total"`
	ErrorsReq       int    `json:"errors_req"`
	ErrorsConn      int    `json:"errors_conn"`
	ErrorsResp      int    `json:"errors_resp"`
}

type HAProxyData struct {
	Info    HAProxyInfo    `json:"info"`
	Entries []HAProxyEntry `json:"entries"`
}

const haproxySocket = "/var/run/haproxy.socket"

func CollectHAProxy() *HAProxyData {
	if _, err := os.Stat(haproxySocket); err != nil {
		return nil
	}

	data := &HAProxyData{}

	// show info
	if out, err := exec.Command("/bin/sh", "-c", `echo "show info" | /usr/bin/nc -U `+haproxySocket).Output(); err == nil {
		info := parseHAProxyInfo(string(out))
		data.Info = info
	}

	// show stat
	if out, err := exec.Command("/bin/sh", "-c", `echo "show stat" | /usr/bin/nc -U `+haproxySocket).Output(); err == nil {
		data.Entries = parseHAProxyStats(string(out))
	}

	return data
}

func parseHAProxyInfo(output string) HAProxyInfo {
	info := HAProxyInfo{}
	for _, line := range strings.Split(output, "\n") {
		parts := strings.SplitN(line, ":", 2)
		if len(parts) != 2 {
			continue
		}
		key := strings.TrimSpace(parts[0])
		val := strings.TrimSpace(parts[1])
		switch key {
		case "Uptime":
			info.Uptime = val
		case "CurrConns":
			info.CurrentConns, _ = strconv.Atoi(val)
		case "CumConns":
			info.TotalConns, _ = strconv.Atoi(val)
		case "CumReq":
			info.TotalReqs, _ = strconv.Atoi(val)
		case "CurrSslConns":
			info.CurrentSSL, _ = strconv.Atoi(val)
		case "CumSslConns":
			info.TotalSSL, _ = strconv.Atoi(val)
		case "Maxconn":
			info.MaxConns, _ = strconv.Atoi(val)
		}
	}
	return info
}

func parseHAProxyStats(output string) []HAProxyEntry {
	var entries []HAProxyEntry
	lines := strings.Split(output, "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" {
			continue
		}
		fields := strings.Split(line, ",")
		if len(fields) < 18 {
			continue
		}

		svname := fields[1]
		entryType := "server"
		if svname == "FRONTEND" {
			entryType = "frontend"
		} else if svname == "BACKEND" {
			entryType = "backend"
		}

		e := HAProxyEntry{
			ProxyName:  fields[0],
			ServerName: svname,
			Type:       entryType,
			Status:     fields[17],
		}
		e.CurrentSessions, _ = strconv.Atoi(fields[4])
		e.TotalSessions, _ = strconv.Atoi(fields[7])
		e.BytesIn, _ = strconv.ParseInt(fields[8], 10, 64)
		e.BytesOut, _ = strconv.ParseInt(fields[9], 10, 64)
		e.ErrorsReq, _ = strconv.Atoi(fields[12])
		e.ErrorsConn, _ = strconv.Atoi(fields[13])
		e.ErrorsResp, _ = strconv.Atoi(fields[14])
		if len(fields) > 48 {
			e.RequestsTotal, _ = strconv.Atoi(fields[48])
		}

		entries = append(entries, e)
	}
	return entries
}
