package nftables

import (
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	"github.com/google/nftables"
)

// IsSupported returns true if nftables is supported on the system, false otherwise.
func IsSupported() bool {
	conn, err := nftables.New()
	if err != nil {
		return false
	}
	_, err = conn.ListTable("filter")
	return err == nil
}

// Version obtains the version of the installed nftables.
func (f *Firewall) Version(ctx context.Context) (string, error) {
	const emptyVersionError = "nft version string is empty"
	cmd := exec.CommandContext(ctx, "nft", "-v")
	output, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("running nft -v: %w", err)
	}
	outputStr := strings.TrimSpace(string(output))
	words := strings.Fields(outputStr)
	if len(words) == 0 {
		return "", errors.New(emptyVersionError) //nolint:err113
	}
	return words[0], nil
}

// RunUserPostRules reads and executes custom nft commands from a file.
// Only lines starting with "nft" are executed. Lines starting with iptables
// commands are rejected with an error.
func (f *Firewall) RunUserPostRules(ctx context.Context, filepath string) error {
	file, err := os.OpenFile(filepath, os.O_RDONLY, 0)
	if os.IsNotExist(err) {
		return nil
	} else if err != nil {
		return fmt.Errorf("opening user rules file: %w", err)
	}
	content, err := io.ReadAll(file)
	if err != nil {
		_ = file.Close()
		return fmt.Errorf("reading user rules file: %w", err)
	}
	if err := file.Close(); err != nil {
		return fmt.Errorf("closing user rules file: %w", err)
	}
	lines := strings.Split(string(content), "\n")

	for lineNum, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		// Only allow nft commands
		if !strings.HasPrefix(line, "nft") {
			f.logger.Warnf("line %d: skipping unrecognized command (expected nft): %s", lineNum+1, line)
			continue
		}

		// Ensure we're matching "nft" as a complete command prefix (not "nftables", "nftrace", etc.)
		if len(line) > 3 && line[3] != ' ' {
			f.logger.Warnf("line %d: skipping unrecognized command (expected nft): %s", lineNum+1, line)
			continue
		}

		// Extract nft arguments
		nftArgs := strings.TrimSpace(line[3:]) // Trim "nft" and leading space
		if nftArgs == "" {
			continue
		}

		args := strings.Fields(nftArgs)
		if len(args) == 0 {
			continue
		}

		cmd := exec.CommandContext(ctx, "nft", args...)
		output, err := cmd.CombinedOutput()
		if err != nil {
			outputStr := strings.TrimSpace(string(output))
			return fmt.Errorf("running user rule on line %d (nft %s): %w: %s",
				lineNum+1, nftArgs, err, outputStr)
		}
	}

	return nil
}
