package servingbindingv1

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"testing"

	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/delegationv1"
)

func TestBindingValidatesEveryExactServingAxis(t *testing.T) {
	binding := testBinding()
	if err := Validate(binding); err != nil {
		t.Fatalf("Validate(valid binding): %v", err)
	}

	for _, test := range []struct {
		name   string
		mutate func(*Binding)
	}{
		{"org", func(value *Binding) { value.OrgID = " org-1" }},
		{"installation", func(value *Binding) { value.AppInstallationID = "installation-1" }},
		{"app release", func(value *Binding) { value.AppReleaseDigest = "sha256:bad" }},
		{"workload", func(value *Binding) { value.WorkloadID = "workload\n" }},
		{"resource", func(value *Binding) { value.CanonicalResource = " https://catalog.example/mcp" }},
		{"tool contract digest", func(value *Binding) { value.ToolContractDigest = "sha256:bad" }},
		{"scope route receipt digest", func(value *Binding) { value.ScopeRouteReceiptDigest = "sha256:bad" }},
		{"unsorted ceiling", func(value *Binding) {
			value.WorkloadCapabilityCeiling = []string{"runtime.records.read-selected-root.v1", "runtime.records.query.v1"}
		}},
		{"empty ceiling", func(value *Binding) { value.WorkloadCapabilityCeiling = nil }},
		{"wrong receipt capability", func(value *Binding) { value.Receipts.CapabilityVersion = "other.v1" }},
		{"zero receipt revision", func(value *Binding) { value.Receipts.CatalogRevision = 0 }},
	} {
		t.Run(test.name, func(t *testing.T) {
			changed := binding
			changed.WorkloadCapabilityCeiling = append([]string(nil), binding.WorkloadCapabilityCeiling...)
			test.mutate(&changed)
			if err := Validate(changed); err == nil {
				t.Fatal("Validate unexpectedly accepted changed binding")
			}
		})
	}
}

func TestBindingCapabilityCeilingUsesAuthorityBound(t *testing.T) {
	for _, count := range []int{delegationv1.MaxCapabilities + 1, delegationv1.MaxAuthorityCapabilities, delegationv1.MaxAuthorityCapabilities + 1} {
		t.Run(fmt.Sprint(count), func(t *testing.T) {
			binding := testBinding()
			binding.WorkloadCapabilityCeiling = make([]string, count)
			for i := range binding.WorkloadCapabilityCeiling {
				binding.WorkloadCapabilityCeiling[i] = fmt.Sprintf("runtime.capability-%04d.v1", i)
			}
			wantError := count > delegationv1.MaxAuthorityCapabilities
			if err := Validate(binding); (err != nil) != wantError {
				t.Fatalf("Validate error = %v, wantError %v", err, wantError)
			}
			before, err := BindingDigest(binding)
			if (err != nil) != wantError {
				t.Fatalf("BindingDigest error = %v, wantError %v", err, wantError)
			}
			if wantError {
				return
			}
			binding.WorkloadCapabilityCeiling[count-1] = "runtime.zzz.v1"
			after, err := BindingDigest(binding)
			if err != nil || before == after {
				t.Fatalf("digest does not commit broad ceiling tail: before %s, after %s, error %v", before, after, err)
			}
			binding.WorkloadCapabilityCeiling[count-1] = binding.WorkloadCapabilityCeiling[count-2]
			if _, err := BindingDigest(binding); err == nil {
				t.Fatal("duplicate broad ceiling tail was accepted")
			}
			binding.WorkloadCapabilityCeiling[count-1] = "runtime.Invalid.v1"
			if err := Validate(binding); err == nil {
				t.Fatal("invalid broad ceiling tail was accepted")
			}
		})
	}
}

func TestMatchesExactRejectsEveryIdentitySubstitution(t *testing.T) {
	binding := testBinding()
	expected := ExpectedBinding{
		OrgID: binding.OrgID, AppInstallationID: binding.AppInstallationID,
		AppReleaseDigest: binding.AppReleaseDigest, WorkloadID: binding.WorkloadID,
		CanonicalResource: binding.CanonicalResource, ToolContractDigest: binding.ToolContractDigest,
		ScopeRouteReceiptDigest: binding.ScopeRouteReceiptDigest,
	}
	if !MatchesExact(binding, expected) {
		t.Fatal("MatchesExact rejected the exact trusted binding")
	}
	for _, test := range []struct {
		name   string
		mutate func(*ExpectedBinding)
	}{
		{"wrong org", func(value *ExpectedBinding) { value.OrgID = "org-2" }},
		{"wrong workload", func(value *ExpectedBinding) { value.WorkloadID = "workload-2" }},
		{"wrong installation", func(value *ExpectedBinding) { value.AppInstallationID = "01900000-0000-7000-8000-000000000002" }},
		{"wrong AppRelease", func(value *ExpectedBinding) { value.AppReleaseDigest = digest("c") }},
		{"wrong canonical resource", func(value *ExpectedBinding) { value.CanonicalResource = "https://other.example/mcp" }},
		{"wrong ToolContract digest", func(value *ExpectedBinding) { value.ToolContractDigest = digest("d") }},
		{"wrong scope route receipt", func(value *ExpectedBinding) { value.ScopeRouteReceiptDigest = digest("e") }},
	} {
		t.Run(test.name, func(t *testing.T) {
			changed := expected
			test.mutate(&changed)
			if MatchesExact(binding, changed) {
				t.Fatal("MatchesExact accepted substituted identity")
			}
		})
	}
}

func TestMatchesReceiptsRejectsStaleOrDowngradedState(t *testing.T) {
	binding := testBinding()
	if !MatchesReceipts(binding, binding.Receipts) {
		t.Fatal("MatchesReceipts rejected exact receipts")
	}
	for _, test := range []struct {
		name   string
		mutate func(*common.Receipts)
	}{
		{"stale catalog revision", func(value *common.Receipts) { value.CatalogRevision-- }},
		{"downgraded schema generation", func(value *common.Receipts) { value.SchemaGeneration-- }},
		{"downgraded engine generation", func(value *common.Receipts) { value.EngineGeneration-- }},
		{"stale security epoch", func(value *common.Receipts) { value.SecurityEpoch-- }},
	} {
		t.Run(test.name, func(t *testing.T) {
			changed := binding.Receipts
			test.mutate(&changed)
			if MatchesReceipts(binding, changed) {
				t.Fatal("MatchesReceipts accepted stale or downgraded receipts")
			}
		})
	}
}

func TestBindingDigestCommitsEveryField(t *testing.T) {
	binding := testBinding()
	want, err := BindingDigest(binding)
	if err != nil {
		t.Fatal(err)
	}
	for _, test := range []struct {
		name   string
		mutate func(*Binding)
	}{
		{"org", func(value *Binding) { value.OrgID = "org-2" }},
		{"installation", func(value *Binding) { value.AppInstallationID = "01900000-0000-7000-8000-000000000002" }},
		{"release", func(value *Binding) { value.AppReleaseDigest = digest("c") }},
		{"workload", func(value *Binding) { value.WorkloadID = "workload-2" }},
		{"resource", func(value *Binding) { value.CanonicalResource = "https://other.example/mcp" }},
		{"tool contract digest", func(value *Binding) { value.ToolContractDigest = digest("d") }},
		{"scope route receipt digest", func(value *Binding) { value.ScopeRouteReceiptDigest = digest("e") }},
		{"capability ceiling", func(value *Binding) {
			value.WorkloadCapabilityCeiling = []string{"runtime.records.read-selected-root.v1"}
		}},
		{"catalog revision", func(value *Binding) { value.Receipts.CatalogRevision++ }},
		{"schema generation", func(value *Binding) { value.Receipts.SchemaGeneration++ }},
		{"engine generation", func(value *Binding) { value.Receipts.EngineGeneration++ }},
		{"security epoch", func(value *Binding) { value.Receipts.SecurityEpoch++ }},
	} {
		t.Run(test.name, func(t *testing.T) {
			changed := binding
			changed.WorkloadCapabilityCeiling = append([]string(nil), binding.WorkloadCapabilityCeiling...)
			test.mutate(&changed)
			got, err := BindingDigest(changed)
			if err != nil {
				t.Fatal(err)
			}
			if got == want {
				t.Fatal("BindingDigest did not commit changed field")
			}
		})
	}
}

func TestImmutableConformanceVector(t *testing.T) {
	data, err := os.ReadFile("conformance_vectors.json")
	if err != nil {
		t.Fatal(err)
	}
	var vectorFile struct {
		SchemaVersion string `json:"schema_version"`
		Vectors       []struct {
			Binding struct {
				OrgID                     string          `json:"org_id"`
				AppInstallationID         string          `json:"app_installation_id"`
				AppReleaseDigest          string          `json:"app_release_digest"`
				WorkloadID                string          `json:"workload_id"`
				CanonicalResource         string          `json:"canonical_resource"`
				ToolContractDigest        string          `json:"tool_contract_digest"`
				ScopeRouteReceiptDigest   string          `json:"scope_route_receipt_digest"`
				WorkloadCapabilityCeiling []string        `json:"workload_capability_ceiling"`
				Receipts                  common.Receipts `json:"receipts"`
			} `json:"binding"`
			CanonicalBytesHex string `json:"canonical_bytes_hex"`
			BindingDigest     string `json:"binding_digest"`
		} `json:"vectors"`
	}
	if err := json.Unmarshal(data, &vectorFile); err != nil {
		t.Fatal(err)
	}
	if vectorFile.SchemaVersion != canonicalBindingDomain || len(vectorFile.Vectors) != 1 {
		t.Fatalf("unexpected conformance vector identity: %#v", vectorFile)
	}
	vector := vectorFile.Vectors[0]
	binding := Binding{
		OrgID: vector.Binding.OrgID, AppInstallationID: vector.Binding.AppInstallationID,
		AppReleaseDigest: vector.Binding.AppReleaseDigest, WorkloadID: vector.Binding.WorkloadID,
		CanonicalResource: vector.Binding.CanonicalResource, ToolContractDigest: vector.Binding.ToolContractDigest,
		ScopeRouteReceiptDigest:   vector.Binding.ScopeRouteReceiptDigest,
		WorkloadCapabilityCeiling: vector.Binding.WorkloadCapabilityCeiling, Receipts: vector.Binding.Receipts,
	}
	canonical, err := CanonicalBytes(binding)
	if err != nil {
		t.Fatal(err)
	}
	if got := hex.EncodeToString(canonical); got != vector.CanonicalBytesHex {
		t.Fatalf("canonical bytes = %s, want %s", got, vector.CanonicalBytesHex)
	}
	gotDigest, err := BindingDigest(binding)
	if err != nil {
		t.Fatal(err)
	}
	if gotDigest != vector.BindingDigest {
		t.Fatalf("binding digest = %s, want %s", gotDigest, vector.BindingDigest)
	}
}

func testBinding() Binding {
	return Binding{
		OrgID: "org-1", AppInstallationID: "01900000-0000-7000-8000-000000000001",
		AppReleaseDigest: digest("a"), WorkloadID: "workload-1",
		CanonicalResource: "https://catalog.example/mcp", ToolContractDigest: digest("b"),
		ScopeRouteReceiptDigest:   digest("c"),
		WorkloadCapabilityCeiling: []string{"runtime.records.query.v1", "runtime.records.read-selected-root.v1"},
		Receipts: common.Receipts{
			ProtocolVersion: "1.0", CatalogRevision: 7, SchemaGeneration: 8,
			EngineGeneration: 9, SecurityEpoch: 10, CapabilityVersion: CapabilityName,
		},
	}
}

func digest(value string) string { return "sha256:" + strings.Repeat(value, 64) }
