package stash_client_test

import (
	"context"
	"encoding/json"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"testing"
	"time"

	openapiruntime "github.com/go-openapi/runtime"
	"github.com/go-openapi/strfmt"

	"code.tnxs.net/vernonkeenan/lib/api/stash/stash_client/stash_pdf"
	"code.tnxs.net/vernonkeenan/lib/api/stash/stash_models"
)

type pdfMetadataCaptureTransport struct {
	operation *openapiruntime.ClientOperation
}

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

func (transport *pdfMetadataCaptureTransport) SubmitContext(
	_ context.Context,
	operation *openapiruntime.ClientOperation,
) (any, error) {
	transport.operation = operation
	switch operation.ID {
	case "listPdfs":
		return stash_pdf.NewListPdfsOK(), nil
	case "getPdfs":
		return stash_pdf.NewGetPdfsOK(), nil
	default:
		panic("unexpected generated Stash PDF operation: " + operation.ID)
	}
}

type pdfMetadataRequest struct {
	headers http.Header
	query   url.Values
	path    map[string]string
	timeout time.Duration
}

func newPDFMetadataRequest() *pdfMetadataRequest {
	return &pdfMetadataRequest{
		headers: make(http.Header),
		query:   make(url.Values),
		path:    make(map[string]string),
	}
}

func (request *pdfMetadataRequest) SetHeaderParam(name string, values ...string) error {
	request.headers.Del(name)
	for _, value := range values {
		request.headers.Add(name, value)
	}
	return nil
}

func (request *pdfMetadataRequest) GetHeaderParams() http.Header {
	return request.headers
}

func (request *pdfMetadataRequest) SetQueryParam(name string, values ...string) error {
	request.query[name] = append([]string(nil), values...)
	return nil
}

func (request *pdfMetadataRequest) SetFormParam(string, ...string) error {
	return nil
}

func (request *pdfMetadataRequest) SetPathParam(name, value string) error {
	request.path[name] = value
	return nil
}

func (request *pdfMetadataRequest) GetQueryParams() url.Values {
	return request.query
}

func (request *pdfMetadataRequest) SetFileParam(string, ...openapiruntime.NamedReadCloser) error {
	return nil
}

func (request *pdfMetadataRequest) SetBodyParam(any) error {
	return nil
}

func (request *pdfMetadataRequest) SetTimeout(timeout time.Duration) error {
	request.timeout = timeout
	return nil
}

func (request *pdfMetadataRequest) GetMethod() string {
	return ""
}

func (request *pdfMetadataRequest) GetPath() string {
	return ""
}

func (request *pdfMetadataRequest) GetBody() []byte {
	return nil
}

func (request *pdfMetadataRequest) GetBodyParam() any {
	return nil
}

func (request *pdfMetadataRequest) GetFileParam() map[string][]openapiruntime.NamedReadCloser {
	return nil
}

func compoundPDFMetadataAuth(apiKey, session string) openapiruntime.ClientAuthInfoWriter {
	return openapiruntime.ClientAuthInfoWriterFunc(
		func(request openapiruntime.ClientRequest, _ strfmt.Registry) error {
			if err := request.SetHeaderParam("X-API-Key", apiKey); err != nil {
				return err
			}
			return request.SetHeaderParam("Cookie", "kvSession="+session)
		},
	)
}

func TestGeneratedPDFMetadataOperationContract(t *testing.T) {
	auth := compoundPDFMetadataAuth("fixture-api-key", "fixture-session")
	tests := []struct {
		name       string
		wantID     string
		wantMethod string
		wantPath   string
		call       func(openapiruntime.ContextualTransport) error
	}{
		{
			name:       "list",
			wantID:     "listPdfs",
			wantMethod: http.MethodGet,
			wantPath:   "/pdfs",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := stash_pdf.New(transport, strfmt.Default).ListPdfs(
					stash_pdf.NewListPdfsParams(), auth,
				)
				return err
			},
		},
		{
			name:       "get",
			wantID:     "getPdfs",
			wantMethod: http.MethodGet,
			wantPath:   "/pdfs/{pdfId}",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := stash_pdf.New(transport, strfmt.Default).GetPdfs(
					stash_pdf.NewGetPdfsParams().WithPdfID("pdf-fixture"), auth,
				)
				return err
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			transport := &pdfMetadataCaptureTransport{}
			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.Fatal("generated operation discarded compound auth writer")
			}

			request := newPDFMetadataRequest()
			if err := transport.operation.AuthInfo.AuthenticateRequest(request, strfmt.Default); err != nil {
				t.Fatalf("authenticate request: %v", err)
			}
			if got := request.headers.Get("X-API-Key"); got != "fixture-api-key" {
				t.Errorf("X-API-Key = %q, want fixture value", got)
			}
			if got := request.headers.Get("Cookie"); got != "kvSession=fixture-session" {
				t.Errorf("Cookie = %q, want only kvSession", got)
			}
			if len(request.headers) != 2 {
				t.Errorf("compound auth wrote unexpected headers: %v", request.headers)
			}
		})
	}
}

func TestGeneratedPDFMetadataParameters(t *testing.T) {
	limit, offset := int64(100), int64(10000)
	listRequest := newPDFMetadataRequest()
	if err := stash_pdf.NewListPdfsParams().
		WithLimit(&limit).
		WithOffset(&offset).
		WriteToRequest(listRequest, strfmt.Default); err != nil {
		t.Fatalf("write list params: %v", err)
	}
	if got := listRequest.query.Get("limit"); got != "100" {
		t.Errorf("limit = %q, want 100", got)
	}
	if got := listRequest.query.Get("offset"); got != "10000" {
		t.Errorf("offset = %q, want 10000", got)
	}

	getRequest := newPDFMetadataRequest()
	if err := stash_pdf.NewGetPdfsParams().
		WithPdfID("pdf-fixture").
		WriteToRequest(getRequest, strfmt.Default); err != nil {
		t.Fatalf("write get params: %v", err)
	}
	if got := getRequest.path["pdfId"]; got != "pdf-fixture" {
		t.Errorf("pdfId = %q, want fixture value", got)
	}
}

func TestPDFMetadataModelIsSanitized(t *testing.T) {
	modelType := reflect.TypeOf(stash_models.Document{})
	wantFields := map[string]string{
		"Filename": "string",
		"ID":       "string",
		"Title":    "string",
		"URI":      "string",
	}
	if modelType.NumField() != len(wantFields) {
		t.Fatalf("Document has %d fields, want %d", modelType.NumField(), len(wantFields))
	}
	for fieldName, wantType := range wantFields {
		field, exists := modelType.FieldByName(fieldName)
		if !exists {
			t.Errorf("Document is missing %s", fieldName)
			continue
		}
		if got := field.Type.String(); got != wantType {
			t.Errorf("Document.%s type = %s, want %s", fieldName, got, wantType)
		}
	}

	encoded, err := json.Marshal(stash_models.Document{
		ID:       "pdf-fixture",
		Filename: "fixture.pdf",
		Title:    "Fixture",
		URI:      "/fixture.pdf",
	})
	if err != nil {
		t.Fatalf("marshal Document: %v", err)
	}
	for _, forbidden := range []string{
		"HTML", "PDF", "ParentID", "SagaType", "OwnerID", "CreatedByID", "TenantID",
	} {
		if strings.Contains(string(encoded), forbidden) {
			t.Errorf("sanitized metadata encoded forbidden field %q: %s", forbidden, encoded)
		}
	}
}

func TestGeneratedPDFMetadataSurfaceIsReadOnly(t *testing.T) {
	clientType := reflect.TypeOf((*stash_pdf.ClientService)(nil)).Elem()
	for index := 0; index < clientType.NumMethod(); index++ {
		method := clientType.Method(index).Name
		for _, forbiddenPrefix := range []string{
			"Post", "Put", "Patch", "Delete", "Create", "Render", "Update",
		} {
			if strings.HasPrefix(method, forbiddenPrefix) {
				t.Errorf("Stash PDF metadata client exposes mutation method %s", method)
			}
		}
	}

	repoRoot := pdfMetadataRepoRoot(t)
	removed := []string{
		filepath.Join(repoRoot, "api", "stash", "stash_client", "stash_pdf", "post_pdfs_parameters.go"),
		filepath.Join(repoRoot, "api", "stash", "stash_client", "stash_pdf", "post_pdfs_responses.go"),
		filepath.Join(repoRoot, "api", "stash", "stash_models", "new_p_d_f.go"),
		filepath.Join(repoRoot, "api", "stash", "stash_models", "p_d_f_request.go"),
		filepath.Join(repoRoot, "api", "stash", "stash_models", "request_meta.go"),
	}
	for _, path := range removed {
		if _, err := os.Stat(path); !os.IsNotExist(err) {
			t.Errorf("legacy mutation artifact still exists: %s", path)
		}
	}
}

func TestPDFMetadataProjectionPinsCompoundAuthAndBounds(t *testing.T) {
	specText := readPDFMetadataProjection(t)
	required := []string{
		"x-provider-pr: \"vernonkeenan/stash#16\"",
		"x-provider-head: \"b2f5b4a6ae5ff507dfdc86bb7b9ee5a9bb924131\"",
		"operationId: listPdfs",
		"operationId: getPdfs",
		"maximum: 100",
		"maximum: 10000",
		"        - ApiKeyAuth: []\n          kvSessionCookie: []",
	}
	for _, fragment := range required {
		if !strings.Contains(specText, fragment) {
			t.Errorf("client projection is missing %q", fragment)
		}
	}
	for _, forbidden := range []string{
		"\n    post:", "\n    put:", "\n    patch:", "\n    delete:",
		"HTML:", "PDF:", "OwnerID:", "ParentID:", "SagaType:",
	} {
		if strings.Contains(specText, forbidden) {
			t.Errorf("client projection exposes forbidden contract fragment %q", forbidden)
		}
	}
}

func TestGeneratedPDFMetadataSurfaceHasNoExternalBypassOrSecrets(t *testing.T) {
	repoRoot := pdfMetadataRepoRoot(t)
	target := filepath.Join(repoRoot, "api", "stash")
	credentialLiteral := regexp.MustCompile(
		`(?i)(api[_-]?key|password|secret)\s*[:=]\s*["'][^"']+["']`,
	)
	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-----",
			"postpdf", "putpdf", "deletepdf", "renderpdf",
		} {
			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 Stash target: %v", err)
	}
}

func pdfMetadataRepoRoot(t *testing.T) string {
	t.Helper()
	_, sourceFile, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("resolve test source path")
	}
	return filepath.Clean(filepath.Join(filepath.Dir(sourceFile), "..", "..", ".."))
}

func readPDFMetadataProjection(t *testing.T) string {
	t.Helper()
	content, err := os.ReadFile(filepath.Join(
		pdfMetadataRepoRoot(t), "swagger", "client", "stash-pdf-metadata.yaml",
	))
	if err != nil {
		t.Fatalf("read Stash PDF metadata projection: %v", err)
	}
	return string(content)
}
