package members_client_test

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strings"
	"testing"

	"code.tnxs.net/vernonkeenan/lib/api/members/members_client"
	"code.tnxs.net/vernonkeenan/lib/api/members/members_client/clusters"
	"code.tnxs.net/vernonkeenan/lib/api/members/members_models"
	openapiruntime "github.com/go-openapi/runtime"
	"github.com/go-openapi/strfmt"
)

var clusterTestAuth = openapiruntime.ClientAuthInfoWriterFunc(
	func(openapiruntime.ClientRequest, strfmt.Registry) error { return nil },
)

type clusterCaptureTransport struct {
	operation *openapiruntime.ClientOperation
}

func (transport *clusterCaptureTransport) Submit(
	operation *openapiruntime.ClientOperation,
) (any, error) {
	return transport.SubmitContext(context.Background(), operation)
}

func (transport *clusterCaptureTransport) SubmitContext(
	_ context.Context,
	operation *openapiruntime.ClientOperation,
) (any, error) {
	transport.operation = operation
	switch operation.ID {
	case "getClusters":
		return clusters.NewGetClustersOK(), nil
	case "postClusters":
		return clusters.NewPostClustersOK(), nil
	case "putClusters":
		return clusters.NewPutClustersOK(), nil
	default:
		panic("unexpected generated Cluster operation: " + operation.ID)
	}
}

func TestGeneratedClusterOperationContract(t *testing.T) {
	type operationCall func(openapiruntime.ContextualTransport) error
	tests := []struct {
		name, wantID, wantMethod, wantPath string
		call                               operationCall
	}{
		{
			name: "list or get Cluster", wantID: "getClusters",
			wantMethod: "GET", wantPath: "/clusters",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := clusters.New(transport, strfmt.Default).GetClusters(
					clusters.NewGetClustersParams().WithTenantID("tenant-example"),
					clusterTestAuth,
				)
				return err
			},
		},
		{
			name: "create Cluster", wantID: "postClusters",
			wantMethod: "POST", wantPath: "/clusters",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := clusters.New(transport, strfmt.Default).PostClusters(
					clusters.NewPostClustersParams().WithTenantID("tenant-example"),
					clusterTestAuth,
				)
				return err
			},
		},
		{
			name: "CAS update Cluster", wantID: "putClusters",
			wantMethod: "PUT", wantPath: "/clusters",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := clusters.New(transport, strfmt.Default).PutClusters(
					clusters.NewPutClustersParams().WithTenantID("tenant-example"),
					clusterTestAuth,
				)
				return err
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			transport := &clusterCaptureTransport{}
			if err := test.call(transport); err != nil {
				t.Fatalf("generated client operation failed: %v", err)
			}
			if transport.operation == nil {
				t.Fatal("generated client did not submit an operation")
			}
			if transport.operation.ID != test.wantID {
				t.Errorf("operation ID = %q, want %q", transport.operation.ID, test.wantID)
			}
			if transport.operation.Method != test.wantMethod {
				t.Errorf("method = %q, want %q", transport.operation.Method, test.wantMethod)
			}
			if transport.operation.PathPattern != test.wantPath {
				t.Errorf("path = %q, want %q", transport.operation.PathPattern, test.wantPath)
			}
			if transport.operation.AuthInfo == nil {
				t.Error("generated operation discarded its compound auth-info writer")
			}
		})
	}
}

func TestClusterTenantBindingCompoundAuthExactScopesAndCAS(t *testing.T) {
	specText := readMembersLearningSpec(t)
	pathBlock := learningSpecPathBlock(t, specText, "/clusters")
	for _, forbidden := range []string{"    delete:", "    patch:"} {
		if strings.Contains(pathBlock, forbidden) {
			t.Errorf("/clusters exposes unsupported %s", strings.TrimSpace(forbidden))
		}
	}

	operations := map[string]struct {
		id, scope string
	}{
		"get":  {id: "getClusters", scope: "members:cluster:read"},
		"post": {id: "postClusters", scope: "members:cluster:create"},
		"put":  {id: "putClusters", scope: "members:cluster:update"},
	}
	const compoundSecurity = "      security:\n        - ApiKeyAuth: []\n          kvSessionCookie: []"
	for method, expected := range operations {
		block := learningSpecOperationBlock(t, specText, "/clusters", method)
		if !strings.Contains(block, "      operationId: "+expected.id+"\n") {
			t.Errorf("%s /clusters lost operationId %q", strings.ToUpper(method), expected.id)
		}
		if strings.Count(block, compoundSecurity) != 1 {
			t.Errorf("%s /clusters does not preserve compound authentication", strings.ToUpper(method))
		}
		if !strings.Contains(block, expected.scope) {
			t.Errorf("%s /clusters lost exact scope %q", strings.ToUpper(method), expected.scope)
		}
		if !strings.Contains(block, `- $ref: "#/parameters/clusterTenantId"`) {
			t.Errorf("%s /clusters is not tenantId-bound", strings.ToUpper(method))
		}
	}

	recordID := "cluster-id"
	getParams := clusters.NewGetClustersParams().
		WithTenantID("tenant-example").
		WithClusterID(&recordID)
	if getParams.TenantID != "tenant-example" {
		t.Fatal("Cluster list/get client lost the required tenantId")
	}
	if getParams.ClusterID == nil || *getParams.ClusterID != recordID {
		t.Fatal("Cluster list/get client lost its clusterId filter")
	}
	if clusters.NewPostClustersParams().WithTenantID("tenant-example").TenantID != "tenant-example" {
		t.Fatal("Cluster create client lost the required tenantId")
	}
	if clusters.NewPutClustersParams().WithTenantID("tenant-example").TenantID != "tenant-example" {
		t.Fatal("Cluster update client lost the required tenantId")
	}
	if !clusters.NewPutClustersConflict().IsCode(409) {
		t.Fatal("Cluster update client lost the optimistic concurrency response")
	}
}

func TestClusterModelAndSingleRecordRequestAreBounded(t *testing.T) {
	assertExactModelFields(t, members_models.Cluster{}, map[string]string{
		"CreatedByID": "*string", "CreatedDate": "*string", "Description": "*string",
		"Environment": "*string", "Gateway": "*string", "ID": "string",
		"IPAddress": "*string", "LastModifiedByID": "*string",
		"LastModifiedDate": "*string", "Name": "*string", "OwnerID": "*string",
		"Ref": "*string", "Status": "*string", "Subnet": "*string",
		"TenantID": "*string", "Type": "*string", "Zone": "*string",
	})

	firstName, secondName := "One", "Two"
	request := &members_models.ClusterRequest{
		Data: []*members_models.Cluster{{Name: &firstName}, {Name: &secondName}},
	}
	if err := request.Validate(strfmt.Default); err == nil {
		t.Fatal("generated Cluster request accepted a bulk payload")
	}
	if err := (&members_models.ClusterRequest{
		Data: []*members_models.Cluster{{Name: &firstName}},
	}).Validate(strfmt.Default); err != nil {
		t.Fatalf("generated Cluster request rejected one valid record: %v", err)
	}
}

func TestMembersCompoundAuthWritesBothCredentialsWithoutExposure(t *testing.T) {
	const (
		apiKey       = "test-api-key"
		sessionToken = "test-session-token"
	)
	writer, err := members_client.NewMembersCompoundAuth(apiKey, sessionToken)
	if err != nil {
		t.Fatalf("construct compound auth writer: %v", err)
	}
	request := new(openapiruntime.TestClientRequest)
	if err := writer.AuthenticateRequest(request, strfmt.Default); err != nil {
		t.Fatalf("write compound authentication: %v", err)
	}
	if got := request.Headers.Get("X-API-Key"); got != apiKey {
		t.Errorf("X-API-Key = %q, want test value", got)
	}
	if got := request.Headers.Get("Cookie"); got != "kvSession="+sessionToken {
		t.Errorf("Cookie = %q, want kvSession test value", got)
	}
	formatted := fmt.Sprintf("%v", writer)
	if strings.Contains(formatted, apiKey) || strings.Contains(formatted, sessionToken) {
		t.Fatal("compound auth writer formatting exposed credential material")
	}
}

func TestMembersCompoundAuthRejectsMissingOrUnsafeCredentialsWithoutEcho(t *testing.T) {
	tests := []struct {
		name, apiKey, sessionToken string
	}{
		{name: "missing API key", sessionToken: "session"},
		{name: "missing session", apiKey: "api-key"},
		{name: "API key header injection", apiKey: "api-key\r\nunsafe", sessionToken: "session"},
		{name: "session cookie injection", apiKey: "api-key", sessionToken: "session;unsafe"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			if writer, err := members_client.NewMembersCompoundAuth(
				test.apiKey, test.sessionToken,
			); err == nil || writer != nil {
				t.Fatal("unsafe compound authentication was accepted")
			} else if (test.apiKey != "" && strings.Contains(err.Error(), test.apiKey)) ||
				(test.sessionToken != "" && strings.Contains(err.Error(), test.sessionToken)) {
				t.Fatal("validation error exposed credential material")
			}
		})
	}
}

func TestClusterSurfaceOmitsDeleteDatabaseAndDSN(t *testing.T) {
	contract := reflect.TypeOf((*clusters.ClientService)(nil)).Elem()
	for index := 0; index < contract.NumMethod(); index++ {
		method := contract.Method(index).Name
		for _, forbidden := range []string{"Delete", "Database", "DSN"} {
			if strings.Contains(method, forbidden) {
				t.Errorf("Cluster client exposes forbidden method %s", method)
			}
		}
	}

	root := reflect.TypeOf(members_client.Members{})
	if _, exists := root.FieldByName("Clusters"); !exists {
		t.Fatal("root Members client does not expose governed Clusters")
	}
}

func TestGeneratedClusterSurfaceHasNoExternalBypassOrSecrets(t *testing.T) {
	repoRoot := learningRepoRoot(t)
	targets := []string{
		filepath.Join(repoRoot, "api", "members", "members_client", "clusters"),
		filepath.Join(repoRoot, "api", "members", "members_client", "compound_auth.go"),
		filepath.Join(repoRoot, "api", "members", "members_models", "cluster.go"),
		filepath.Join(repoRoot, "api", "members", "members_models", "cluster_request.go"),
		filepath.Join(repoRoot, "api", "members", "members_models", "cluster_response.go"),
	}
	credentialLiteral := regexp.MustCompile(
		`(?i)(api[_-]?key|password|secret)\s*[:=]\s*["'][^"']+["']`,
	)
	for _, target := range targets {
		err := filepath.WalkDir(target, func(
			path string, entry os.DirEntry, walkErr error,
		) error {
			if walkErr != nil {
				return walkErr
			}
			if entry.IsDir() || !strings.HasSuffix(path, ".go") ||
				strings.HasSuffix(path, "_test.go") {
				return nil
			}
			content, err := os.ReadFile(path)
			if err != nil {
				return err
			}
			lower := strings.ToLower(string(content))
			for _, forbidden := range []string{
				"salesforce", "sf-gate", "go-force", "private key-----",
				"members_client/databases", "dsn",
			} {
				if strings.Contains(lower, forbidden) {
					t.Errorf("%s contains forbidden boundary %q", path, forbidden)
				}
			}
			if credentialLiteral.Match(content) {
				t.Errorf("%s contains a credential-shaped literal", path)
			}
			return nil
		})
		if err != nil {
			t.Fatalf("scan generated Cluster target %s: %v", target, err)
		}
	}
}
