// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause

// TODO(#8502): add support for more architectures
//go:build linux && (arm64 || amd64)

package linuxfw

import (
	"fmt"
	"os/exec"
	"strings"
	"unicode"

	"github.com/sagernet/tailscale/types/logger"
	"github.com/sagernet/tailscale/util/multierr"
)

// DebugNetfilter prints debug information about iptables rules to the
// provided log function.
func DebugIptables(logf logger.Logf) error {
	// unused.
	return nil
}

// detectIptables returns the number of iptables rules that are present in the
// system, ignoring the default "ACCEPT" rule present in the standard iptables
// chains.
//
// It only returns an error when there is no iptables binary, or when iptables -S
// fails. In all other cases, it returns the number of non-default rules.
//
// If the iptables binary is not found, it returns an underlying exec.ErrNotFound
// error.
func detectIptables() (int, error) {
	// run "iptables -S" to get the list of rules using iptables
	// exec.Command returns an error if the binary is not found
	cmd := exec.Command("iptables", "-S")
	output, err := cmd.Output()
	ip6cmd := exec.Command("ip6tables", "-S")
	ip6output, ip6err := ip6cmd.Output()
	var allLines []string
	outputStr := string(output)
	lines := strings.Split(outputStr, "\n")
	ip6outputStr := string(ip6output)
	ip6lines := strings.Split(ip6outputStr, "\n")
	switch {
	case err == nil && ip6err == nil:
		allLines = append(lines, ip6lines...)
	case err == nil && ip6err != nil:
		allLines = lines
	case err != nil && ip6err == nil:
		allLines = ip6lines
	default:
		return 0, FWModeNotSupportedError{
			Mode: FirewallModeIPTables,
			Err:  fmt.Errorf("iptables command run fail: %w", multierr.New(err, ip6err)),
		}
	}

	// count the number of non-default rules
	count := 0
	for _, line := range allLines {
		trimmedLine := strings.TrimLeftFunc(line, unicode.IsSpace)
		if line != "" && strings.HasPrefix(trimmedLine, "-A") {
			// if the line is not empty and starts with "-A", it is a rule appended not default
			count++
		}
	}

	// return the count of non-default rules
	return count, nil
}
