package clistat

import (
	"bufio"
	"bytes"
	"os"

	"github.com/spf13/afero"
	"golang.org/x/xerrors"
)

const (
	procMounts                           = "/proc/mounts"
	procOneCgroup                        = "/proc/1/cgroup"
	kubernetesDefaultServiceAccountToken = "/var/run/secrets/kubernetes.io/serviceaccount/token" //nolint:gosec
)

// IsContainerized returns whether the host is containerized.
// This is adapted from https://github.com/elastic/go-sysinfo/tree/main/providers/linux/container.go#L31
// with modifications to support Sysbox containers.
// On non-Linux platforms, it always returns false.
func IsContainerized(fs afero.Fs) (ok bool, err error) {
	cgData, err := afero.ReadFile(fs, procOneCgroup)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, xerrors.Errorf("read file %s: %w", procOneCgroup, err)
	}

	scn := bufio.NewScanner(bytes.NewReader(cgData))
	for scn.Scan() {
		line := scn.Bytes()
		if bytes.Contains(line, []byte("docker")) ||
			bytes.Contains(line, []byte(".slice")) ||
			bytes.Contains(line, []byte("lxc")) ||
			bytes.Contains(line, []byte("kubepods")) {
			return true, nil
		}
	}

	// Sometimes the above method of sniffing /proc/1/cgroup isn't reliable.
	// If a Kubernetes service account token is present, that's
	// also a good indication that we are in a container.
	_, err = afero.ReadFile(fs, kubernetesDefaultServiceAccountToken)
	if err == nil {
		return true, nil
	}

	// Last-ditch effort to detect Sysbox containers.
	// Check if we have anything mounted as type sysboxfs in /proc/mounts
	mountsData, err := afero.ReadFile(fs, procMounts)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, xerrors.Errorf("read file %s: %w", procMounts, err)
	}

	scn = bufio.NewScanner(bytes.NewReader(mountsData))
	for scn.Scan() {
		line := scn.Bytes()
		if bytes.Contains(line, []byte("sysboxfs")) {
			return true, nil
		}
	}

	// If we get here, we are _probably_ not running in a container.
	return false, nil
}
