package output

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"sync"

	"github.com/trufflesecurity/trufflehog/v3/pkg/context"
	"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
	"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)

var dedupeCache = make(map[string]struct{})

// GitHubActionsPrinter is a printer that prints results in GitHub Actions format.
type GitHubActionsPrinter struct{ mu sync.Mutex }

func (p *GitHubActionsPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata) error {
	out := gitHubActionsOutputFormat{
		DetectorType: r.Result.DetectorType.String(),
		DecoderType:  r.Result.DecoderType.String(),
		Verified:     r.Result.Verified,
	}

	meta, err := structToMap(r.SourceMetadata.Data)
	if err != nil {
		return fmt.Errorf("could not marshal result: %w", err)
	}

	for _, data := range meta {
		for k, v := range data {
			if k == "line" {
				if line, ok := v.(float64); ok {
					out.StartLine = int64(line)
				}
			}
			if k == "file" {
				if filename, ok := v.(string); ok {
					out.Filename = filename
				}
			}
		}
	}

	verifiedStatus := "unverified"
	if out.Verified {
		verifiedStatus = "verified"
	}

	key := fmt.Sprintf("%s:%s:%s:%s:%d", out.DecoderType, out.DetectorType, verifiedStatus, out.Filename, out.StartLine)
	h := sha256.New()
	h.Write([]byte(key))
	key = hex.EncodeToString(h.Sum(nil))
	p.mu.Lock()
	defer p.mu.Unlock()
	if _, ok := dedupeCache[key]; ok {
		return nil
	}
	dedupeCache[key] = struct{}{}

	message := fmt.Sprintf("Found %s %s result 🐷🔑\n", verifiedStatus, out.DetectorType)
	if r.Result.DecoderType != detectorspb.DecoderType_PLAIN {
		message = fmt.Sprintf("Found %s %s result with %s encoding 🐷🔑\n", verifiedStatus, out.DetectorType, out.DecoderType)
	}

	fmt.Printf("::warning file=%s,line=%d,endLine=%d::%s",
		out.Filename, out.StartLine, out.StartLine, message)

	return nil
}

type gitHubActionsOutputFormat struct {
	DetectorType,
	DecoderType string
	Verified  bool
	StartLine int64
	Filename  string
}
