package clihubtest

import (
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"os"

	"github.com/fatih/color"
	"github.com/spf13/cobra"

	"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/core/args"
	"github.com/crowdsecurity/crowdsec/pkg/hubtest"
)

// getCoverage returns the coverage and the percentage of tests that passed
func getCoverage(show bool, getCoverageFunc func(string) ([]hubtest.Coverage, error), hubDir string) ([]hubtest.Coverage, int, error) {
	if !show {
		return nil, 0, nil
	}

	coverage, err := getCoverageFunc(hubDir)
	if err != nil {
		return nil, 0, fmt.Errorf("while getting coverage: %w", err)
	}

	tested := 0

	for _, test := range coverage {
		if test.TestsCount > 0 {
			tested++
		}
	}

	// keep coverage 0 if there's no tests?
	percent := 0
	if len(coverage) > 0 {
		percent = int(math.Round((float64(tested) / float64(len(coverage)) * 100)))
	}

	return coverage, percent, nil
}

func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAppsecCov bool, showOnlyPercent bool) error {
	cfg := cli.cfg()

	// for this one we explicitly don't do for appsec
	if err := HubTest.LoadAllTests(); err != nil {
		return fmt.Errorf("unable to load all tests: %w", err)
	}

	var err error

	// if all are false (flag by default), show them
	if !showParserCov && !showScenarioCov && !showAppsecCov {
		showParserCov = true
		showScenarioCov = true
		showAppsecCov = true
	}

	parserCoverage, parserCoveragePercent, err := getCoverage(showParserCov, HubTest.GetParsersCoverage, HubTest.HubPath)
	if err != nil {
		return err
	}

	scenarioCoverage, scenarioCoveragePercent, err := getCoverage(showScenarioCov, HubTest.GetScenariosCoverage, HubTest.HubPath)
	if err != nil {
		return err
	}

	appsecRuleCoverage, appsecRuleCoveragePercent, err := getCoverage(showAppsecCov, HubTest.GetAppsecCoverage, HubTest.HubPath)
	if err != nil {
		return err
	}

	if showOnlyPercent {
		switch {
		case showParserCov:
			fmt.Fprintf(os.Stdout, "parsers=%d%%", parserCoveragePercent)
		case showScenarioCov:
			fmt.Fprintf(os.Stdout, "scenarios=%d%%", scenarioCoveragePercent)
		case showAppsecCov:
			fmt.Fprintf(os.Stdout, "appsec_rules=%d%%", appsecRuleCoveragePercent)
		}

		return nil
	}

	switch cfg.Cscli.Output {
	case "human":
		if showParserCov {
			hubTestCoverageTable(color.Output, cfg.Cscli.Color, []string{"Parser", "Status", "Number of tests"}, parserCoverage)
		}

		if showScenarioCov {
			hubTestCoverageTable(color.Output, cfg.Cscli.Color, []string{"Scenario", "Status", "Number of tests"}, parserCoverage)
		}

		if showAppsecCov {
			hubTestCoverageTable(color.Output, cfg.Cscli.Color, []string{"Appsec Rule", "Status", "Number of tests"}, parserCoverage)
		}

		fmt.Fprintln(os.Stdout)

		if showParserCov {
			fmt.Fprintf(os.Stdout, "PARSERS    : %d%% of coverage\n", parserCoveragePercent)
		}

		if showScenarioCov {
			fmt.Fprintf(os.Stdout, "SCENARIOS  : %d%% of coverage\n", scenarioCoveragePercent)
		}

		if showAppsecCov {
			fmt.Fprintf(os.Stdout, "APPSEC RULES  : %d%% of coverage\n", appsecRuleCoveragePercent)
		}
	case "json":
		dump, err := json.MarshalIndent(parserCoverage, "", " ")
		if err != nil {
			return err
		}

		fmt.Fprint(os.Stdout, string(dump))

		dump, err = json.MarshalIndent(scenarioCoverage, "", " ")
		if err != nil {
			return err
		}

		fmt.Fprint(os.Stdout, string(dump))

		dump, err = json.MarshalIndent(appsecRuleCoverage, "", " ")
		if err != nil {
			return err
		}

		fmt.Fprint(os.Stdout, string(dump))
	default:
		return errors.New("only human/json output modes are supported")
	}

	return nil
}

func (cli *cliHubTest) newCoverageCmd() *cobra.Command {
	var (
		showParserCov   bool
		showScenarioCov bool
		showOnlyPercent bool
		showAppsecCov   bool
	)

	cmd := &cobra.Command{
		Use:               "coverage",
		Short:             "coverage",
		Args:              args.NoArgs,
		DisableAutoGenTag: true,
		RunE: func(_ *cobra.Command, _ []string) error {
			return cli.coverage(showScenarioCov, showParserCov, showAppsecCov, showOnlyPercent)
		},
	}

	cmd.PersistentFlags().BoolVar(&showOnlyPercent, "percent", false, "Show only percentages of coverage")
	cmd.PersistentFlags().BoolVar(&showParserCov, "parsers", false, "Show only parsers coverage")
	cmd.PersistentFlags().BoolVar(&showScenarioCov, "scenarios", false, "Show only scenarios coverage")
	cmd.PersistentFlags().BoolVar(&showAppsecCov, "appsec", false, "Show only appsec coverage")

	return cmd
}
