// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package githook

import (
	"fmt"
	"time"

	"github.com/harness/gitness/git"
	"github.com/harness/gitness/git/hook"

	"github.com/fatih/color"
)

var (
	colorScanHeader            = color.New(color.FgHiWhite, color.Underline)
	colorScanSummary           = color.New(color.FgHiRed, color.Bold)
	colorScanSummaryNoFindings = color.New(color.FgHiGreen, color.Bold)
)

func printScanSecretsFindings(
	output *hook.Output,
	findings []secretFinding,
	multipleRefs bool,
	duration time.Duration,
) {
	findingsCnt := len(findings)

	// no results? output success and continue
	if findingsCnt == 0 {
		output.Messages = append(
			output.Messages,
			colorScanSummaryNoFindings.Sprintf("No secrets found")+
				fmt.Sprintf(" in %s", duration.Round(time.Millisecond)),
			"", "", // add two empty lines for making it visually more consumable
		)
		return
	}

	output.Messages = append(
		output.Messages,
		colorScanHeader.Sprintf(
			"Push contains %s:",
			singularOrPlural("secret", findingsCnt > 1),
		),
		"", // add empty line for making it visually more consumable
	)

	for _, finding := range findings {
		headerTxt := fmt.Sprintf("%s in %s:%d", finding.RuleID, finding.File, finding.StartLine)
		if finding.StartLine != finding.EndLine {
			headerTxt += fmt.Sprintf("-%d", finding.EndLine)
		}
		if multipleRefs {
			headerTxt += fmt.Sprintf(" [%s]", finding.Ref)
		}

		output.Messages = append(
			output.Messages,
			fmt.Sprintf("  %s", headerTxt),
			fmt.Sprintf("      Secret:       %s", finding.Secret),
			fmt.Sprintf("      Commit:       %s", finding.Commit),
			fmt.Sprintf("      Details:      %s", finding.Description),
			fmt.Sprintf("      Fingerprint:  %s", finding.Fingerprint),
			"", // add empty line for making it visually more consumable
		)
	}

	output.Messages = append(
		output.Messages,
		colorScanSummary.Sprintf(
			"%d %s found",
			findingsCnt,
			singularOrPlural("secret", findingsCnt > 1),
		)+fmt.Sprintf(" in %s", FMTDuration(time.Millisecond)),
		"", "", // add two empty lines for making it visually more consumable
	)
}

func FMTDuration(d time.Duration) string {
	const secondsRounding = time.Second / time.Duration(10)
	switch {
	case d <= time.Millisecond:
	// keep anything under a millisecond untouched
	case d < time.Second:
		d = d.Round(time.Millisecond) // round under a second to millisecondss
	case d < time.Minute:
		d = d.Round(secondsRounding) // round under a minute to .1 precision
	default:
		d = d.Round(time.Second) // keep rest at second precision
	}
	return d.String()
}

func printOversizeFiles(
	output *hook.Output,
	oversizeFiles []git.FileInfo,
	sizeLimit int64,
) {
	output.Messages = append(
		output.Messages,
		colorScanHeader.Sprintf(
			"Push contains files exceeding the size limit:",
		),
		"", // add empty line for making it visually more consumable
	)

	for _, file := range oversizeFiles {
		output.Messages = append(
			output.Messages,
			fmt.Sprintf("  %s", file.SHA),
			fmt.Sprintf("      Size: %dB", file.Size),
			"", // add empty line for making it visually more consumable
		)
	}

	total := len(oversizeFiles)
	output.Messages = append(
		output.Messages,
		colorScanSummary.Sprintf(
			"%d %s found exceeding the size limit of %dB",
			total, singularOrPlural("file", total > 1), sizeLimit,
		),
		"", "", // add two empty lines for making it visually more consumable
	)
}

func singularOrPlural(noun string, plural bool) string {
	if plural {
		return noun + "s"
	}
	return noun
}
