package scanners

import (
	"github.com/komodorio/helm-dashboard/pkg/dashboard/objects"
	"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
	"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
	log "github.com/sirupsen/logrus"
	"strconv"
	"strings"
)

type Trivy struct {
	Data *objects.DataLayer
}

func (c *Trivy) ManifestIsScannable() bool {
	return false
}

func (c *Trivy) SupportedResourceKinds() []string {
	// from https://github.com/aquasecurity/trivy-kubernetes/blob/main/pkg/k8s/k8s.go#L190
	return []string{
		"ReplicaSet",
		"ReplicationController",
		"StatefulSet",
		"Deployment",
		"CronJob",
		"DaemonSet",
		"Job",
	}
}

func (c *Trivy) Name() string {
	return "Trivy"
}

func (c *Trivy) Test() bool {
	utils.FailLogLevel = log.DebugLevel
	defer func() { utils.FailLogLevel = log.WarnLevel }()

	res, err := utils.RunCommand([]string{"trivy", "--version"}, nil)
	if err != nil {
		return false
	}
	parts := strings.Split(res, "\n")
	log.Infof("Discovered Trivy: %s", strings.TrimSpace(parts[0]))
	return true
}

func (c *Trivy) ScanManifests(_ string) (*subproc.ScanResults, error) {
	return nil, nil // Trivy is unable to scan manifests
}

func (c *Trivy) scanResource(ns string, kind string, name string) (string, error) {
	cmd := []string{"trivy", "kubernetes", "--quiet", "--format", "table", "--report", "all", "--no-progress",
		"--context", c.Data.KubeContext, "--namespace", ns, kind + "/" + name}
	out, err := utils.RunCommand(cmd, nil)
	if err != nil {
		return "", err
	}

	return out, nil
}

func (c *Trivy) ScanResource(ns string, kind string, name string) (*subproc.ScanResults, error) {
	res := subproc.ScanResults{}
	resource, err := c.scanResource(ns, kind, name)
	if err != nil {
		return nil, err
	}

	for _, line := range strings.Split(resource, "\n") {
		if strings.HasPrefix(line, "Tests:") {
			parts := strings.FieldsFunc(line, func(r rune) bool {
				return r == ':' || r == ',' || r == ')'
			})

			if cnt, err := strconv.Atoi(strings.TrimSpace(parts[2])); err == nil {
				res.PassedCount += cnt
			} else {
				log.Warnf("Failed to parse Trivy output: %s", err)
			}

			if cnt, err := strconv.Atoi(strings.TrimSpace(parts[4])); err == nil {
				res.FailedCount += cnt
			} else {
				log.Warnf("Failed to parse Trivy output: %s", err)
			}
		}

		if strings.HasPrefix(line, "Total:") {
			parts := strings.FieldsFunc(line, func(r rune) bool {
				return r == ':' || r == ',' || r == '('
			})

			if cnt, err := strconv.Atoi(strings.TrimSpace(parts[1])); err == nil {
				res.FailedCount += cnt
			} else {
				log.Warnf("Failed to parse Trivy output: %s", err)
			}
		}

	}

	res.OrigReport = resource

	return &res, nil
}
