//go:build linux

package bridge

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"

	"tailscale.com/client/local"
)

func shouldIgnoreLoginCheckPrefsError(err error) bool {
	return runtime.GOOS == "linux" && local.IsAccessDeniedError(err)
}

func cliExecutablePath(bridgeProgram string) string {
	return cliExecutablePathWithBridge(os.Getenv("MIRAGE_CLI_PROGRAM"), bridgeProgram, exec.LookPath)
}

func cliExecutablePathWithBridge(explicit, bridgeProgram string, lookPath func(string) (string, error)) string {
	sibling := ""
	if bridgeProgram != "" {
		sibling = filepath.Join(filepath.Dir(bridgeProgram), "mirage")
	}
	for _, candidate := range []string{
		strings.TrimSpace(explicit),
		sibling,
		"/usr/bin/mirage",
		"mirage",
	} {
		candidate = strings.TrimSpace(candidate)
		if candidate == "" {
			continue
		}
		if strings.Contains(candidate, "/") {
			if _, err := os.Stat(candidate); err == nil {
				return candidate
			}
			continue
		}
		if path, err := lookPath(candidate); err == nil {
			return path
		}
	}
	return ""
}

func desktopUserName() (string, error) {
	for _, key := range []string{"SUDO_USER", "PKEXEC_UID", "USER", "LOGNAME"} {
		if value := strings.TrimSpace(os.Getenv(key)); value != "" {
			if key == "PKEXEC_UID" {
				u, err := user.LookupId(value)
				if err != nil {
					return "", err
				}
				return u.Username, nil
			}
			return value, nil
		}
	}
	u, err := user.Current()
	if err != nil {
		return "", err
	}
	if strings.TrimSpace(u.Username) == "" {
		return "", errors.New("current username is empty")
	}
	return u.Username, nil
}

func operatorGrantCommand(cliPath, username string) (string, []string, error) {
	if strings.TrimSpace(cliPath) == "" {
		return "", nil, errors.New("mirage CLI executable not found")
	}
	if strings.TrimSpace(username) == "" {
		return "", nil, errors.New("desktop username is empty")
	}
	return "pkexec", []string{cliPath, "set", "--operator=" + username}, nil
}

func runElevatedCommand(ctx context.Context, name string, args ...string) error {
	cmd := exec.CommandContext(ctx, name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func (a *App) grantOperatorAccess() error {
	bridgeProgram, err := os.Executable()
	if err != nil {
		bridgeProgram = os.Args[0]
	}
	cliPath := cliExecutablePath(bridgeProgram)
	username, err := desktopUserName()
	if err != nil {
		return fmt.Errorf("resolve desktop user failed: %w", err)
	}
	name, args, err := operatorGrantCommand(cliPath, username)
	if err != nil {
		return err
	}
	ctx, cancel := context.WithTimeout(context.Background(), 3*apiTimeout)
	defer cancel()
	if err := runElevatedCommand(ctx, name, args...); err != nil {
		return fmt.Errorf("grant operator access failed: %w", err)
	}
	return nil
}
