package lib

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"os"

	"github.com/sigstore/cosign/cmd/cosign/cli/rekor"
	"github.com/slsa-framework/slsa-verifier/pkg"
)

var defaultRekorAddr = "https://rekor.sigstore.dev"

// Verify SLSA provenance of the downloaded binary
// Copied from https://github.com/slsa-framework/slsa-verifier/blob/aee753f/main.go
// Once the slsa-verifier supports being used as a library, this can be removed
func verify(provenance []byte, artifactHash, source, branch, versionTag string) error {
	rClient, err := rekor.NewClient(defaultRekorAddr)
	if err != nil {
		return err
	}

	// Get Rekor entries corresponding to the binary artifact in the provenance.
	uuids, err := pkg.GetRekorEntries(rClient, artifactHash)
	if err != nil {
		return err
	}

	env, err := pkg.EnvelopeFromBytes(provenance)
	if err != nil {
		return err
	}

	// Verify the provenance and return the signing certificate.
	cert, err := pkg.FindSigningCertificate(context.Background(), uuids, *env, rClient)
	if err != nil {
		return err
	}

	// Get the workflow info given the certificate information.
	workflowInfo, err := pkg.GetWorkflowInfoFromCertificate(cert)
	if err != nil {
		return err
	}

	// Unpack and verify info in the provenance, including the Subject Digest.
	if err := pkg.VerifyProvenance(env, artifactHash); err != nil {
		return err
	}

	// Verify the workflow identity.
	if err := pkg.VerifyWorkflowIdentity(workflowInfo, source); err != nil {
		return err
	}

	// Verify the branch.
	// TODO: This started failing for some reason? base_ref was null
	// if err := pkg.VerifyBranch(env, branch); err != nil {
	// 	return err
	// }

	// Verify the tag.
	if err := pkg.VerifyTag(env, versionTag); err != nil {
		return err
	}

	return nil
}

func verifyBinary(binaryPath, attestationPath, versionTag string) error {
	// TODO: Also verify that the version is newer and this isn't a downgrade
	attestation, err := os.ReadFile(attestationPath)
	if err != nil {
		return fmt.Errorf("failed to read attestation file: %v", err)
	}

	binaryFile, err := os.Open(binaryPath)
	if err != nil {
		return fmt.Errorf("failed to read binary for verification purposes: %v", err)
	}
	defer binaryFile.Close()

	hasher := sha256.New()
	if _, err := io.Copy(hasher, binaryFile); err != nil {
		return fmt.Errorf("failed to hash binary: %v", err)
	}
	hash := hex.EncodeToString(hasher.Sum(nil))

	return verify(attestation, hash, "github.com/ddworken/hishtory", "master", versionTag)
}
