package flag

import (
	"github.com/samber/lo"

	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
	"github.com/aquasecurity/trivy/pkg/log"
	"github.com/aquasecurity/trivy/pkg/types"
)

var (
	VulnTypeFlag = Flag{
		Name:       "vuln-type",
		ConfigName: "vulnerability.type",
		Default: []string{
			types.VulnTypeOS,
			types.VulnTypeLibrary,
		},
		Values: []string{
			types.VulnTypeOS,
			types.VulnTypeLibrary,
		},
		Usage: "comma-separated list of vulnerability types",
	}
	IgnoreUnfixedFlag = Flag{
		Name:       "ignore-unfixed",
		ConfigName: "vulnerability.ignore-unfixed",
		Default:    false,
		Usage:      "display only fixed vulnerabilities",
	}
	IgnoreStatusFlag = Flag{
		Name:       "ignore-status",
		ConfigName: "vulnerability.ignore-status",
		Default:    []string{},
		Values:     dbTypes.Statuses,
		Usage:      "comma-separated list of vulnerability status to ignore",
	}
)

type VulnerabilityFlagGroup struct {
	VulnType      *Flag
	IgnoreUnfixed *Flag
	IgnoreStatus  *Flag
}

type VulnerabilityOptions struct {
	VulnType       []string
	IgnoreStatuses []dbTypes.Status
}

func NewVulnerabilityFlagGroup() *VulnerabilityFlagGroup {
	return &VulnerabilityFlagGroup{
		VulnType:      &VulnTypeFlag,
		IgnoreUnfixed: &IgnoreUnfixedFlag,
		IgnoreStatus:  &IgnoreStatusFlag,
	}
}

func (f *VulnerabilityFlagGroup) Name() string {
	return "Vulnerability"
}

func (f *VulnerabilityFlagGroup) Flags() []*Flag {
	return []*Flag{
		f.VulnType,
		f.IgnoreUnfixed,
		f.IgnoreStatus,
	}
}

func (f *VulnerabilityFlagGroup) ToOptions() VulnerabilityOptions {
	// Just convert string to dbTypes.Status as the validated values are passed here.
	ignoreStatuses := lo.Map(getStringSlice(f.IgnoreStatus), func(s string, _ int) dbTypes.Status {
		return dbTypes.NewStatus(s)
	})
	ignoreUnfixed := getBool(f.IgnoreUnfixed)

	if ignoreUnfixed && len(ignoreStatuses) > 0 {
		log.Logger.Warn("'--ignore-unfixed' is ignored because '--ignore-status' is specified")
	} else if ignoreUnfixed {
		// '--ignore-unfixed' is a shorthand of '--ignore-status'.
		ignoreStatuses = lo.FilterMap(dbTypes.Statuses, func(s string, _ int) (dbTypes.Status, bool) {
			fixed := dbTypes.StatusFixed
			if s == fixed.String() {
				return 0, false
			}
			return dbTypes.NewStatus(s), true
		})
	} else if len(ignoreStatuses) == 0 {
		ignoreStatuses = nil
	}
	log.Logger.Debugw("Ignore statuses", "statuses", ignoreStatuses)

	return VulnerabilityOptions{
		VulnType:       getStringSlice(f.VulnType),
		IgnoreStatuses: ignoreStatuses,
	}
}
