//go:build linux

package tun

import (
	"os/exec"
	"strings"

	E "github.com/sagernet/sing/common/exceptions"
	F "github.com/sagernet/sing/common/format"
)

func (r *autoRedirect) setupIPTables() error {
	if r.enableIPv4 {
		err := r.setupIPTablesForFamily(r.iptablesPath)
		if err != nil {
			return E.Cause(err, "setup iptables")
		}
	}
	if r.enableIPv6 {
		err := r.setupIPTablesForFamily(r.ip6tablesPath)
		if err != nil {
			return E.Cause(err, "setup ip6tables")
		}
	}
	return nil
}

func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error {
	tableNameOutput := r.tableName + "-output"
	redirectPort := r.redirectPort()
	// OUTPUT
	err := r.runShell(iptablesPath, "-t nat -N", tableNameOutput)
	if err != nil {
		return err
	}
	err = r.runShell(iptablesPath, "-t nat -A", tableNameOutput,
		"-p tcp -o", r.tunOptions.Name,
		"-j REDIRECT --to-ports", redirectPort)
	if err != nil {
		return err
	}
	err = r.runShell(iptablesPath, "-t nat -I OUTPUT -j", tableNameOutput)
	if err != nil {
		return err
	}
	if r.redirectServer != nil {
		tableNameInput := r.tableName + "-input"
		err = r.runShell(iptablesPath, "-t filter -N", tableNameInput)
		if err != nil {
			return err
		}
		err = r.runShell(iptablesPath, "-t filter -A", tableNameInput,
			"-p tcp --dport", redirectPort,
			"-m conntrack ! --ctstate DNAT",
			"-j REJECT --reject-with tcp-reset")
		if err != nil {
			return err
		}
		err = r.runShell(iptablesPath, "-t filter -I INPUT -j", tableNameInput)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *autoRedirect) cleanupIPTables() {
	if r.enableIPv4 {
		r.cleanupIPTablesForFamily(r.iptablesPath)
	}
	if r.enableIPv6 {
		r.cleanupIPTablesForFamily(r.ip6tablesPath)
	}
}

func (r *autoRedirect) cleanupIPTablesForFamily(iptablesPath string) {
	tableNameOutput := r.tableName + "-output"
	tableNameInput := r.tableName + "-input"

	_ = r.runShell(iptablesPath, "-t nat -D OUTPUT -j", tableNameOutput)
	_ = r.runShell(iptablesPath, "-t nat -F", tableNameOutput)
	_ = r.runShell(iptablesPath, "-t nat -X", tableNameOutput)
	_ = r.runShell(iptablesPath, "-t filter -D INPUT -j", tableNameInput)
	_ = r.runShell(iptablesPath, "-t filter -F", tableNameInput)
	_ = r.runShell(iptablesPath, "-t filter -X", tableNameInput)
}

func (r *autoRedirect) runShell(commands ...any) error {
	commandStr := strings.Join(F.MapToString(commands), " ")
	var command *exec.Cmd
	if r.androidSu {
		command = exec.Command(r.suPath, "-c", commandStr)
	} else {
		commandArray := strings.Split(commandStr, " ")
		command = exec.Command(commandArray[0], commandArray[1:]...)
	}
	combinedOutput, err := command.CombinedOutput()
	if err != nil {
		return E.Extend(err, F.ToString(commandStr, ": ", string(combinedOutput)))
	}
	return nil
}
