package members_client_test

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"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/tracks"
	"code.tnxs.net/vernonkeenan/lib/api/members/members_models"
	openapiruntime "github.com/go-openapi/runtime"
	"github.com/go-openapi/strfmt"
)

const membersTrackSpecSHA256 = "631ed1313bf3a2fee7d7f1cd053c09b058bf8acb6117d3100af24e340c57291e"

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

type trackCaptureTransport struct {
	operation *openapiruntime.ClientOperation
}

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

func (transport *trackCaptureTransport) SubmitContext(
	_ context.Context,
	operation *openapiruntime.ClientOperation,
) (any, error) {
	transport.operation = operation
	switch operation.ID {
	case "getTracks":
		return tracks.NewGetTracksOK(), nil
	case "postTracks":
		return tracks.NewPostTracksOK(), nil
	case "putTracks":
		return tracks.NewPutTracksOK(), nil
	default:
		panic("unexpected generated Track operation: " + operation.ID)
	}
}

func TestGeneratedTrackOperationContract(t *testing.T) {
	type operationCall func(openapiruntime.ContextualTransport) error
	tests := []struct {
		name, wantID, wantMethod, wantPath string
		call                               operationCall
	}{
		{
			name: "list or get Track", wantID: "getTracks",
			wantMethod: "GET", wantPath: "/tracks",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := tracks.New(transport, strfmt.Default).GetTracks(
					tracks.NewGetTracksParams(), trackTestAuth,
				)
				return err
			},
		},
		{
			name: "create Track", wantID: "postTracks",
			wantMethod: "POST", wantPath: "/tracks",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := tracks.New(transport, strfmt.Default).PostTracks(
					tracks.NewPostTracksParams(), trackTestAuth,
				)
				return err
			},
		},
		{
			name: "CAS update Track", wantID: "putTracks",
			wantMethod: "PUT", wantPath: "/tracks",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := tracks.New(transport, strfmt.Default).PutTracks(
					tracks.NewPutTracksParams(), trackTestAuth,
				)
				return err
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			transport := &trackCaptureTransport{}
			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 TestTrackSpecPreservesExactScopesCompoundAuthAndCAS(t *testing.T) {
	specText := readMembersLearningSpec(t)
	pathBlock := learningSpecPathBlock(t, specText, "/tracks")
	for _, forbidden := range []string{"    delete:", "    patch:"} {
		if strings.Contains(pathBlock, forbidden) {
			t.Errorf("/tracks exposes unsupported %s", strings.TrimSpace(forbidden))
		}
	}
	operations := map[string]struct {
		id, scope string
	}{
		"get":  {id: "getTracks", scope: "members:track:read"},
		"post": {id: "postTracks", scope: "members:track:create"},
		"put":  {id: "putTracks", scope: "members:track:update"},
	}
	const compoundSecurity = "      security:\n        - ApiKeyAuth: []\n          kvSessionCookie: []"
	for method, expected := range operations {
		block := learningSpecOperationBlock(t, specText, "/tracks", method)
		if !strings.Contains(block, "      operationId: "+expected.id+"\n") {
			t.Errorf("%s /tracks lost operationId %q", strings.ToUpper(method), expected.id)
		}
		if strings.Count(block, compoundSecurity) != 1 {
			t.Errorf("%s /tracks does not preserve compound authentication", strings.ToUpper(method))
		}
		if !strings.Contains(block, expected.scope) {
			t.Errorf("%s /tracks lost exact scope %q", strings.ToUpper(method), expected.scope)
		}
	}
	if !tracks.NewPutTracksConflict().IsCode(409) {
		t.Fatal("Track update client lost the optimistic concurrency response")
	}
	recordID := "track-id"
	if got := tracks.NewGetTracksParams().WithID(&recordID).ID; got == nil || *got != recordID {
		t.Fatal("Track list/get client lost its ID filter")
	}
}

func TestTrackModelAndSingleRecordRequestAreBounded(t *testing.T) {
	assertExactModelFields(t, members_models.Track{}, map[string]string{
		"CreatedByID": "*string", "CreatedDate": "*string", "Description": "*string",
		"ID": "string", "ImageAltText": "*string", "ImageURL": "*string",
		"LastModifiedByID": "*string", "LastModifiedDate": "*string",
		"Logo": "*string", "Name": "string", "Slug": "string",
	})
	request := &members_models.TrackRequest{
		Data: []*members_models.Track{{Name: "One", Slug: "one"}, {Name: "Two", Slug: "two"}},
	}
	if err := request.Validate(strfmt.Default); err == nil {
		t.Fatal("generated Track request accepted a bulk payload")
	}
}

func TestTrackSurfaceSeparatesBoundedRelationshipAndOmitsUnsafeAliases(t *testing.T) {
	contract := reflect.TypeOf((*tracks.ClientService)(nil)).Elem()
	for index := 0; index < contract.NumMethod(); index++ {
		method := contract.Method(index).Name
		for _, forbidden := range []string{"Delete", "TrackEvent", "TrackTopic", "TrackUser"} {
			if strings.Contains(method, forbidden) {
				t.Errorf("Track client exposes unsafe method %s", method)
			}
		}
	}

	root := reflect.TypeOf(members_client.Members{})
	if _, exists := root.FieldByName("Tracks"); !exists {
		t.Fatal("root Members client does not expose governed Tracks")
	}
	if _, exists := root.FieldByName("TrackEvents"); !exists {
		t.Fatal("root Members client does not expose governed TrackEvents")
	}
	for _, forbidden := range []string{"TrackTopic", "TrackTopics", "TrackUser", "TrackUsers"} {
		if _, exists := root.FieldByName(forbidden); exists {
			t.Errorf("root Members client exposes provider-pending %s", forbidden)
		}
	}

	repoRoot := learningRepoRoot(t)
	for _, path := range []string{"/tracktopics", "/trackusers"} {
		if strings.Contains(readMembersLearningSpec(t), "\n  "+path+":") {
			t.Errorf("authoritative spec exposes provider-pending %s", path)
		}
	}
	for _, artifact := range []string{
		"tracktopic.go", "tracktopic_request.go", "tracktopic_response.go",
		"track_topic.go", "track_topic_request.go", "track_topic_response.go",
		"trackuser.go", "trackuser_request.go", "trackuser_response.go",
		"track_user.go", "track_user_request.go", "track_user_response.go",
	} {
		if _, err := os.Stat(filepath.Join(
			repoRoot, "api", "members", "members_models", artifact,
		)); !os.IsNotExist(err) {
			t.Errorf("provider-pending generated artifact exists: %s", artifact)
		}
	}
	for _, clientDir := range []string{"tracktopics", "track_topics", "trackusers", "track_users"} {
		path := filepath.Join(repoRoot, "api", "members", "members_client", clientDir)
		if _, err := os.Stat(path); !os.IsNotExist(err) {
			t.Errorf("provider-pending generated client directory exists: %s", clientDir)
		}
	}
}

func TestMembersSpecIsPinnedToTrackProviderCommit(t *testing.T) {
	repoRoot := learningRepoRoot(t)
	mainSpec, err := os.ReadFile(filepath.Join(
		repoRoot, "swagger", "members-vernonkeenan.yaml",
	))
	if err != nil {
		t.Fatalf("read Members spec: %v", err)
	}
	sum := sha256.Sum256(mainSpec)
	if got := hex.EncodeToString(sum[:]); got != membersTrackSpecSHA256 {
		t.Fatalf(
			"Members spec drifted from the governed TrackEvent provider source: SHA-256 = %s, want %s",
			got, membersTrackSpecSHA256,
		)
	}

	externalSpec, err := os.ReadFile(filepath.Join(
		repoRoot, "swagger", "external", "members-vernonkeenan.yaml",
	))
	if err != nil {
		t.Fatalf("read external Members spec: %v", err)
	}
	normalized := bytes.ReplaceAll(externalSpec, []byte(`"https"`), []byte(`"http"`))
	normalized = bytes.ReplaceAll(
		normalized, []byte("gw.tnxs.net"), []byte("members.vernonkeenan.com:8080"),
	)
	normalized = bytes.ReplaceAll(
		normalized, []byte(`"/vk/members/v1"`), []byte(`"/v1"`),
	)
	if !bytes.Equal(normalized, mainSpec) {
		t.Fatal("external Members spec differs from the conventional gateway rewrite")
	}
}

func TestGeneratedTrackSurfaceHasNoExternalBypassOrSecrets(t *testing.T) {
	repoRoot := learningRepoRoot(t)
	targets := []string{
		filepath.Join(repoRoot, "api", "members", "members_client", "tracks"),
		filepath.Join(repoRoot, "api", "members", "members_models", "track.go"),
		filepath.Join(repoRoot, "api", "members", "members_models", "track_request.go"),
		filepath.Join(repoRoot, "api", "members", "members_models", "track_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-----",
			} {
				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 Track target %s: %v", target, err)
		}
	}
}
