package gcb

import (
	"context"

	serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
	"github.com/slsa-framework/slsa-verifier/v2/options"
	register "github.com/slsa-framework/slsa-verifier/v2/register"
	_ "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/keys"
	"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
)

const VerifierName = "GCB"

//nolint:gochecknoinits
func init() {
	register.RegisterVerifier(VerifierName, GCBVerifierNew())
}

type GCBVerifier struct{}

func GCBVerifierNew() *GCBVerifier {
	return &GCBVerifier{}
}

// IsAuthoritativeFor returns true of the verifier can verify provenance
// generated by the builderID.
func (v *GCBVerifier) IsAuthoritativeFor(builderIDName string) bool {
	// This verifier only supports the GCB builders.
	return builderIDName == "https://cloudbuild.googleapis.com/GoogleHostedWorker"
}

// VerifyArtifact verifies provenance for an artifact.
func (v *GCBVerifier) VerifyArtifact(ctx context.Context,
	provenance []byte, artifactHash string,
	provenanceOpts *options.ProvenanceOpts,
	builderOpts *options.BuilderOpts,
) ([]byte, *utils.TrustedBuilderID, error) {
	return nil, nil, serrors.ErrorNotSupported
}

// VerifyImage verifies provenance for an OCI image.
func (v *GCBVerifier) VerifyImage(ctx context.Context,
	provenance []byte, artifactImage string,
	provenanceOpts *options.ProvenanceOpts,
	builderOpts *options.BuilderOpts,
) ([]byte, *utils.TrustedBuilderID, error) {
	prov, err := ProvenanceFromBytes(provenance)
	if err != nil {
		return nil, nil, err
	}

	// Verify signature on the intoto attestation.
	if err = prov.VerifySignature(); err != nil {
		return nil, nil, err
	}

	// Verify intoto header.
	if err = prov.VerifyIntotoHeaders(); err != nil {
		return nil, nil, err
	}

	// Verify the builder.
	builderID, err := prov.VerifyBuilder(builderOpts)
	if err != nil {
		return nil, nil, err
	}

	// Verify subject digest.
	if err = prov.VerifySubjectDigest(provenanceOpts.ExpectedDigest); err != nil {
		return nil, nil, err
	}

	// Verify source.
	if err = prov.VerifySourceURI(provenanceOpts.ExpectedSourceURI, *builderID); err != nil {
		return nil, nil, err
	}

	// Verify metadata.
	// This is metadata that GCB appends to the DSSE content.
	if err = prov.VerifyMetadata(provenanceOpts); err != nil {
		return nil, nil, err
	}

	// Verify the summary.
	// This is an additional structure that GCB prepends to the provenance.
	if err = prov.VerifySummary(provenanceOpts); err != nil {
		return nil, nil, err
	}

	// Verify the text provenance.
	// This is an additional structure that GCB prepends to the provenance,
	// intended for humans. It reflect the DSSE payload.
	if err = prov.VerifyTextProvenance(); err != nil {
		return nil, nil, err
	}

	// Verify branch.
	if provenanceOpts.ExpectedBranch != nil {
		if err = prov.VerifyBranch(*provenanceOpts.ExpectedBranch); err != nil {
			return nil, nil, err
		}
	}

	// Verify the tag.
	if provenanceOpts.ExpectedTag != nil {
		if err := prov.VerifyTag(*provenanceOpts.ExpectedTag); err != nil {
			return nil, nil, err
		}
	}

	// Verify the versioned tag.
	if provenanceOpts.ExpectedVersionedTag != nil {
		if err := prov.VerifyVersionedTag(*provenanceOpts.ExpectedVersionedTag); err != nil {
			return nil, nil, err
		}
	}

	content, err := prov.GetVerifiedIntotoStatement()
	if err != nil {
		return nil, nil, err
	}
	return content, builderID, nil
}