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/vex"
)

var (
	IgnoreUnfixedFlag = Flag[bool]{
		Name:       "ignore-unfixed",
		ConfigName: "vulnerability.ignore-unfixed",
		Usage:      "display only fixed vulnerabilities",
	}
	IgnoreStatusFlag = Flag[[]string]{
		Name:       "ignore-status",
		ConfigName: "vulnerability.ignore-status",
		Values:     dbTypes.Statuses,
		Usage:      "comma-separated list of vulnerability status to ignore",
	}
	VEXFlag = Flag[[]string]{
		Name:       "vex",
		ConfigName: "vulnerability.vex",
		Usage:      `[EXPERIMENTAL] VEX sources ("repo", "oci" or file path)`,
	}
	SkipVEXRepoUpdateFlag = Flag[bool]{
		Name:       "skip-vex-repo-update",
		ConfigName: "vulnerability.skip-vex-repo-update",
		Usage:      `[EXPERIMENTAL] Skip VEX Repository update`,
	}
)

type VulnerabilityFlagGroup struct {
	IgnoreUnfixed     *Flag[bool]
	IgnoreStatus      *Flag[[]string]
	VEX               *Flag[[]string]
	SkipVEXRepoUpdate *Flag[bool]
}

type VulnerabilityOptions struct {
	IgnoreStatuses    []dbTypes.Status
	VEXSources        []vex.Source
	SkipVEXRepoUpdate bool
}

func NewVulnerabilityFlagGroup() *VulnerabilityFlagGroup {
	return &VulnerabilityFlagGroup{
		IgnoreUnfixed:     IgnoreUnfixedFlag.Clone(),
		IgnoreStatus:      IgnoreStatusFlag.Clone(),
		VEX:               VEXFlag.Clone(),
		SkipVEXRepoUpdate: SkipVEXRepoUpdateFlag.Clone(),
	}
}

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

func (f *VulnerabilityFlagGroup) Flags() []Flagger {
	return []Flagger{
		f.IgnoreUnfixed,
		f.IgnoreStatus,
		f.VEX,
		f.SkipVEXRepoUpdate,
	}
}

func (f *VulnerabilityFlagGroup) ToOptions() (VulnerabilityOptions, error) {
	if err := parseFlags(f); err != nil {
		return VulnerabilityOptions{}, err
	}

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

	switch {
	case ignoreUnfixed && len(ignoreStatuses) > 0:
		log.Warn("'--ignore-unfixed' is ignored because '--ignore-status' is specified")
	case 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
		})
	case len(ignoreStatuses) == 0:
		ignoreStatuses = nil
	}
	log.Debug("Ignore statuses", log.Any("statuses", ignoreStatuses))

	return VulnerabilityOptions{
		IgnoreStatuses: ignoreStatuses,
		VEXSources: lo.Map(f.VEX.Value(), func(s string, _ int) vex.Source {
			return vex.NewSource(s)
		}),
		SkipVEXRepoUpdate: f.SkipVEXRepoUpdate.Value(),
	}, nil
}
