package gateway

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/sha256"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

const (
	signingPolicyPath = "../release/gateway-image-signing-policy.json"
	signingPublicKey  = "../release/gateway-image-signing-public-key.pem"
	signingRole       = "arn:aws:iam::604175033442:role/nexuscrm-api-host-gateway-publisher"
	signingKey        = "arn:aws:kms:us-west-2:604175033442:key/5d8648a4-770c-4383-8f7a-9f0780522293"
	imageName         = "ghcr.io/pingcap-inc/nexuscrm-api-host-gateway"
	imageDigest       = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
)

type ownerSigningPolicy struct {
	AWSRegion                   string `json:"aws_region"`
	AWSRoleARN                  string `json:"aws_role_arn"`
	ImageRepository             string `json:"image_repository"`
	KeySpec                     string `json:"key_spec"`
	KeyUsage                    string `json:"key_usage"`
	KMSKeyARN                   string `json:"kms_key_arn"`
	OIDCAudiencePrefix          string `json:"oidc_audience_prefix"`
	OIDCIssuer                  string `json:"oidc_issuer"`
	OIDCJWKSURI                 string `json:"oidc_jwks_uri"`
	OIDCMaxTokenLifetimeSeconds int64  `json:"oidc_max_token_lifetime_seconds"`
	OIDCSubject                 string `json:"oidc_subject"`
	OIDCTokenAnnotation         string `json:"oidc_token_annotation"`
	OwnerRepository             string `json:"owner_repository"`
	ProvenanceBuilderID         string `json:"provenance_builder_id"`
	ProvenanceBuildType         string `json:"provenance_build_type"`
	ProvenancePredicateType     string `json:"provenance_predicate_type"`
	PublicKeyPath               string `json:"public_key_path"`
	PublicKeySHA256             string `json:"public_key_sha256"`
	PublisherState              string `json:"publisher_state"`
	RepositoryID                string `json:"repository_id"`
	RepositoryVisibility        string `json:"repository_visibility"`
	RunnerEnvironment           string `json:"runner_environment"`
	SchemaVersion               string `json:"schema_version"`
	SigningAlgorithm            string `json:"signing_algorithm"`
	SourceRef                   string `json:"source_ref"`
	WorkflowName                string `json:"workflow_name"`
	WorkflowRef                 string `json:"workflow_ref"`
}

func fileSHA256(t *testing.T, path string) string {
	t.Helper()
	content, err := os.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	return fmt.Sprintf("sha256:%x", sha256.Sum256(content))
}

func readOwnerSigningPolicy(t *testing.T, path string) ownerSigningPolicy {
	t.Helper()
	content, err := os.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	var policy ownerSigningPolicy
	if err := json.Unmarshal(content, &policy); err != nil {
		t.Fatal(err)
	}
	return policy
}

func TestGatewayOwnerSigningFactsAreCompleteAndConsumerUsable(t *testing.T) {
	policy := readOwnerSigningPolicy(t, signingPolicyPath)
	expected := ownerSigningPolicy{
		AWSRegion:                   "us-west-2",
		AWSRoleARN:                  signingRole,
		ImageRepository:             imageName,
		KeySpec:                     "ECC_NIST_P256",
		KeyUsage:                    "SIGN_VERIFY",
		KMSKeyARN:                   signingKey,
		OIDCAudiencePrefix:          "urn:nexuscrm:gateway-image:",
		OIDCIssuer:                  "https://token.actions.githubusercontent.com",
		OIDCJWKSURI:                 "https://token.actions.githubusercontent.com/.well-known/jwks",
		OIDCMaxTokenLifetimeSeconds: 600,
		OIDCSubject:                 "repo:pingcap-inc/nexuscrm-api-host:ref:refs/heads/main",
		OIDCTokenAnnotation:         "io.nexuscrm.github-oidc-token",
		OwnerRepository:             "pingcap-inc/nexuscrm-api-host",
		ProvenanceBuilderID:         "https://github.com/pingcap-inc/nexuscrm-api-host/.github/workflows/trusted-publish-gateway-image.yml@c2cedc3d50ef39e79e93b9dd0d0f34c22592d1d6",
		ProvenanceBuildType:         "https://github.com/pingcap-inc/nexuscrm-api-host/blob/main/docs/gateway-image-build-type-v1.md",
		ProvenancePredicateType:     "https://slsa.dev/provenance/v1",
		PublicKeyPath:               "release/gateway-image-signing-public-key.pem",
		PublicKeySHA256:             "sha256:875cb4f1609a01267d1e7aaa11d85f6fac659d287d4a1ac0d94829f32aaf5aa7",
		PublisherState:              "active",
		RepositoryID:                "1279604404",
		RepositoryVisibility:        "private",
		RunnerEnvironment:           "github-hosted",
		SchemaVersion:               "nexuscrm.oci-owner-signing-policy/v4",
		SigningAlgorithm:            "ECDSA_SHA_256",
		SourceRef:                   "refs/heads/main",
		WorkflowName:                "publish-gateway-image",
		WorkflowRef:                 "pingcap-inc/nexuscrm-api-host/.github/workflows/publish-gateway-image.yml@refs/heads/main",
	}
	if policy != expected {
		t.Fatalf("owner signing policy drifted:\n got %#v\nwant %#v", policy, expected)
	}
	if got := fileSHA256(t, signingPublicKey); got != policy.PublicKeySHA256 {
		t.Fatalf("public key digest=%s, policy=%s", got, policy.PublicKeySHA256)
	}
	publicKeyPEM, err := os.ReadFile(signingPublicKey)
	if err != nil {
		t.Fatal(err)
	}
	block, rest := pem.Decode(publicKeyPEM)
	if block == nil || block.Type != "PUBLIC KEY" || len(rest) != 0 {
		t.Fatal("owner verifier input must be exactly one PKIX PUBLIC KEY PEM block")
	}
	parsed, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		t.Fatal(err)
	}
	publicKey, ok := parsed.(*ecdsa.PublicKey)
	if !ok || publicKey.Curve != elliptic.P256() {
		t.Fatalf("owner verifier key is not ECDSA P-256: %T", parsed)
	}
}

func TestGatewaySourceScriptIsVerificationOnly(t *testing.T) {
	script, err := os.ReadFile("../scripts/gateway-image-signing-policy.sh")
	if err != nil {
		t.Fatal(err)
	}
	content := string(script)
	for _, required := range []string{
		"verify-release <source-commit> <image-digest>",
		"cosign verify",
		"cosign verify-attestation",
		"--cosign-evidence",
		"--provenance-evidence",
		"GATEWAY_OIDC_JWKS_FILE",
		"inactive publisher must not claim a builder",
		"active publisher must pin the exact trusted workflow SHA",
	} {
		if !strings.Contains(content, required) {
			t.Fatalf("downstream verifier script missing %q", required)
		}
	}
	for _, forbidden := range []string{
		"cosign sign", "cosign attest", "awskms:///", "ACTIONS_ID_TOKEN_REQUEST_",
		"aws sts", "aws kms", "--token", "--write-provenance", "preflight)", "sign)", "attest)",
		"EXPECTED_PUBLISHER_STATE", "EXPECTED_PROVENANCE_BUILDER_ID",
	} {
		if strings.Contains(content, forbidden) {
			t.Fatalf("downstream verifier retained release authority %q", forbidden)
		}
	}
}

func TestGatewayReleaseVerificationRejectsInvalidIdentityBeforeCosign(t *testing.T) {
	fakeDir := t.TempDir()
	logPath := filepath.Join(fakeDir, "cosign.args")
	cosignPath := filepath.Join(fakeDir, "cosign")
	if err := os.WriteFile(cosignPath, []byte("#!/bin/sh\nprintf '%s\\n' \"$@\" >> \"$COSIGN_CALL_LOG\"\n"), 0o755); err != nil {
		t.Fatal(err)
	}
	tests := []struct {
		name   string
		commit string
		digest string
	}{
		{name: "short commit", commit: "01234567", digest: imageDigest},
		{name: "uppercase commit", commit: "0123456789ABCDEF0123456789ABCDEF01234567", digest: imageDigest},
		{name: "short digest", commit: "0123456789abcdef0123456789abcdef01234567", digest: "sha256:abc"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			_ = os.Remove(logPath)
			command := exec.Command("../scripts/gateway-image-signing-policy.sh", "verify-release", test.commit, test.digest)
			command.Env = append(os.Environ(), "PATH="+fakeDir+string(os.PathListSeparator)+os.Getenv("PATH"), "COSIGN_CALL_LOG="+logPath)
			if output, err := command.CombinedOutput(); err == nil {
				t.Fatalf("invalid release identity passed verification:\n%s", output)
			}
			if _, err := os.Stat(logPath); !os.IsNotExist(err) {
				t.Fatalf("invalid identity reached Cosign: %v", err)
			}
		})
	}
}
