package bootstrapimpl

import (
	"fmt"
	"testing"
)

// TestNamedCredentialTableExists asserts that _System_NamedCredential is
// declared in system_tables.json and carries the expected key columns with the
// correct uniqueness/nullable constraints.
func TestNamedCredentialTableExists(t *testing.T) {
	const tableName = "_System_NamedCredential"
	defs := GetManagedTableDefinitions()

	var found bool
	for _, def := range defs {
		if def.TableName != tableName {
			continue
		}
		found = true

		// Required key columns and their constraints.
		type wantCol struct {
			name     string
			unique   bool
			nullable bool // false means NOT NULL
		}
		wantCols := []wantCol{
			{name: "Id"},
			{name: "DeveloperName", unique: true, nullable: false},
			{name: "Label"},
			{name: "Endpoint"},
			{name: "PrincipalType"},
			{name: "AuthType"},
		}

		colIdx := make(map[string]int, len(def.Columns))
		for i, col := range def.Columns {
			colIdx[col.Name] = i
		}

		for _, wc := range wantCols {
			i, ok := colIdx[wc.name]
			if !ok {
				t.Errorf("%s: missing expected column %q", tableName, wc.name)
				continue
			}
			col := def.Columns[i]
			if wc.unique && !col.Unique {
				t.Errorf("%s.%s: want unique=true, got false", tableName, wc.name)
			}
			// DeveloperName must be NOT NULL (nullable=false in schema; Go zero-value is false).
			if wc.name == "DeveloperName" && col.Nullable {
				t.Errorf("%s.%s: want nullable=false, got true", tableName, wc.name)
			}
		}
		break
	}

	if !found {
		t.Fatalf("%s not found in system_tables.json", tableName)
	}
}

// TestNamedCredentialRequiredReadPermissionIsBlocked pins the read permission
// on _System_NamedCredential to "BLOCKED". Generic SObject CRUD/query paths
// must be unable to read definitions directly; the only valid read path is
// namedcredential.Service.GetDefinition / ListDefinitions which enforce
// validation and (for secret-bearing operations) permission gates.
func TestNamedCredentialRequiredReadPermissionIsBlocked(t *testing.T) {
	const (
		tableName = "_System_NamedCredential"
		wantPerm  = "BLOCKED"
	)
	defs := GetManagedTableDefinitions()
	for _, def := range defs {
		if def.TableName != tableName {
			continue
		}
		if def.RequiredReadPermission != wantPerm {
			t.Fatalf(
				"%s.requiredReadPermission = %q, want %q; "+
					"generic CRUD/query reads would bypass service-layer validation and permission gates. "+
					"Read path is namedcredential.Service.GetDefinition / ListDefinitions only.",
				tableName, def.RequiredReadPermission, wantPerm,
			)
		}
		return
	}
	t.Fatalf("%s not found in system_tables.json", tableName)
}

// TestNamedCredentialRequiredWritePermissionIsBlocked pins the write
// permission on _System_NamedCredential to "BLOCKED". Generic SObject CRUD
// writes would let a caller bypass validateDefinition (allowing invalid
// AuthType / Endpoint values) AND would let a caller repoint Endpoint on an
// existing secret-bearing credential to an attacker-controlled URL,
// exfiltrating secrets on the next runtime callout. The only valid write
// path is namedcredential.Service.UpsertDefinition / DeleteDefinition,
// which gate on ManageNamedCredentials.
func TestNamedCredentialRequiredWritePermissionIsBlocked(t *testing.T) {
	const (
		tableName = "_System_NamedCredential"
		wantPerm  = "BLOCKED"
	)
	defs := GetManagedTableDefinitions()
	for _, def := range defs {
		if def.TableName != tableName {
			continue
		}
		if def.RequiredWritePermission != wantPerm {
			t.Fatalf(
				"%s.requiredWritePermission = %q, want %q; "+
					"generic CRUD write would bypass validateDefinition (invalid AuthType/Endpoint) and "+
					"allow repointing Endpoint to an attacker URL — secrets exfiltrate on next callout. "+
					"Write path is namedcredential.Service.UpsertDefinition / DeleteDefinition only.",
				tableName, def.RequiredWritePermission, wantPerm,
			)
		}
		return
	}
	t.Fatalf("%s not found in system_tables.json", tableName)
}

// TestNamedCredentialSecretTableExists asserts that _System_NamedCredentialSecret
// is declared in system_tables.json and carries the expected key columns.
func TestNamedCredentialSecretTableExists(t *testing.T) {
	const tableName = "_System_NamedCredentialSecret"
	defs := GetManagedTableDefinitions()

	var found bool
	for _, def := range defs {
		if def.TableName != tableName {
			continue
		}
		found = true

		wantCols := []string{"Id", "NamedCredentialId", "SecretKind", "SecretValue", "MaskedHint", "RotatedAt"}
		colIdx := make(map[string]bool, len(def.Columns))
		for _, col := range def.Columns {
			colIdx[col.Name] = true
		}
		for _, name := range wantCols {
			if !colIdx[name] {
				t.Errorf("%s: missing expected column %q", tableName, name)
			}
		}
		break
	}

	if !found {
		t.Fatalf("%s not found in system_tables.json", tableName)
	}
}

// TestNamedCredentialSecretRequiredReadPermissionIsBlocked pins the
// requiredReadPermission on _System_NamedCredentialSecret to "BLOCKED".
// Generic SObject CRUD/query paths must be unable to read raw ciphertext rows;
// the only valid read path is the namedcredential.Service which decrypts via
// SecretCipher and enforces the ManageNamedCredentials permission gate.
func TestNamedCredentialSecretRequiredReadPermissionIsBlocked(t *testing.T) {
	const (
		tableName = "_System_NamedCredentialSecret"
		wantPerm  = "BLOCKED"
	)
	defs := GetManagedTableDefinitions()
	for _, def := range defs {
		if def.TableName != tableName {
			continue
		}
		if def.RequiredReadPermission != wantPerm {
			t.Fatalf(
				"%s.requiredReadPermission = %q, want %q; "+
					"the storage table holds ciphertext envelopes only — generic CRUD must be unable to read it. "+
					"Read path is namedcredential.Service.GetSecret (which decrypts + permission-gates).",
				tableName, def.RequiredReadPermission, wantPerm,
			)
		}
		return
	}
	t.Fatalf("%s not found in system_tables.json", tableName)
}

// TestNamedCredentialSecretRequiredWritePermissionIsBlocked pins the
// requiredWritePermission on _System_NamedCredentialSecret to "BLOCKED".
// Generic SObject CRUD/query paths must be unable to INSERT/UPDATE secret rows
// directly — that would let a caller persist arbitrary plaintext into the
// ciphertext column, bypassing the namedcredential.Service envelope encryption
// + masking + permission gates. The only valid write path is
// namedcredential.Service.SetSecret.
func TestNamedCredentialSecretRequiredWritePermissionIsBlocked(t *testing.T) {
	const (
		tableName = "_System_NamedCredentialSecret"
		wantPerm  = "BLOCKED"
	)
	defs := GetManagedTableDefinitions()
	for _, def := range defs {
		if def.TableName != tableName {
			continue
		}
		if def.RequiredWritePermission != wantPerm {
			t.Fatalf(
				"%s.requiredWritePermission = %q, want %q; "+
					"generic CRUD write would bypass envelope encryption + masking + permission gate. "+
					"Write path is namedcredential.Service.SetSecret only.",
				tableName, def.RequiredWritePermission, wantPerm,
			)
		}
		return
	}
	t.Fatalf("%s not found in system_tables.json", tableName)
}

// TestNamedCredentialSecretForeignKey asserts that _System_NamedCredentialSecret
// declares a CASCADE FK from NamedCredentialId → _System_NamedCredential(Id).
func TestNamedCredentialSecretForeignKey(t *testing.T) {
	const (
		tableName  = "_System_NamedCredentialSecret"
		wantCol    = "NamedCredentialId"
		wantRef    = "_System_NamedCredential(Id)"
		wantDelete = "CASCADE"
	)
	defs := GetManagedTableDefinitions()
	for _, def := range defs {
		if def.TableName != tableName {
			continue
		}
		for _, fk := range def.ForeignKeys {
			if fk.Column == wantCol {
				if fk.References != wantRef {
					t.Errorf("%s FK %s: references = %q, want %q", tableName, wantCol, fk.References, wantRef)
				}
				if fk.OnDelete != wantDelete {
					t.Errorf("%s FK %s: onDelete = %q, want %q", tableName, wantCol, fk.OnDelete, wantDelete)
				}
				return
			}
		}
		t.Fatalf("%s: foreign key on %q not found; declared FKs: %v",
			tableName, wantCol, func() []string {
				var out []string
				for _, fk := range def.ForeignKeys {
					out = append(out, fmt.Sprintf("%s→%s", fk.Column, fk.References))
				}
				return out
			}())
	}
	t.Fatalf("%s not found in system_tables.json", tableName)
}

// TestNamedCredentialSecretUniqueIndex asserts that _System_NamedCredentialSecret
// declares the unique index on (NamedCredentialId, SecretKind).
func TestNamedCredentialSecretUniqueIndex(t *testing.T) {
	const tableName = "_System_NamedCredentialSecret"
	defs := GetManagedTableDefinitions()
	for _, def := range defs {
		if def.TableName != tableName {
			continue
		}
		for _, idx := range def.Indices {
			if !idx.Unique {
				continue
			}
			if len(idx.Columns) == 2 &&
				idx.Columns[0] == "NamedCredentialId" &&
				idx.Columns[1] == "SecretKind" {
				return // found
			}
		}
		t.Fatalf("%s: unique index on (NamedCredentialId, SecretKind) not found; declared indices: %v",
			tableName, func() []string {
				var out []string
				for _, idx := range def.Indices {
					out = append(out, fmt.Sprintf("unique=%v cols=%v", idx.Unique, idx.Columns))
				}
				return out
			}())
	}
	t.Fatalf("%s not found in system_tables.json", tableName)
}
