package research_client_test

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

	"code.tnxs.net/vernonkeenan/lib/api/research/research_client/company_products"
	"code.tnxs.net/vernonkeenan/lib/api/research/research_client/company_services"
	"code.tnxs.net/vernonkeenan/lib/api/research/research_models"
	"github.com/go-openapi/loads"
	openapiruntime "github.com/go-openapi/runtime"
	"github.com/go-openapi/strfmt"
)

type catalogCaptureTransport struct {
	operation *openapiruntime.ClientOperation
}

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

func (transport *catalogCaptureTransport) SubmitContext(
	_ context.Context,
	operation *openapiruntime.ClientOperation,
) (any, error) {
	transport.operation = operation
	switch operation.ID {
	case "getCompanyProducts":
		return company_products.NewGetCompanyProductsOK(), nil
	case "postCompanyProducts":
		return company_products.NewPostCompanyProductsOK(), nil
	case "getCompanyServices":
		return company_services.NewGetCompanyServicesOK(), nil
	case "postCompanyServices":
		return company_services.NewPostCompanyServicesOK(), nil
	case "putCompanyService":
		return company_services.NewPutCompanyServiceOK(), nil
	default:
		panic("unexpected generated company catalog operation: " + operation.ID)
	}
}

func TestGeneratedCompanyCatalogOperationContract(t *testing.T) {
	type operationCall func(openapiruntime.ContextualTransport) error
	tests := []struct {
		name       string
		wantID     string
		wantMethod string
		wantPath   string
		call       operationCall
	}{
		{
			name: "list or get product", wantID: "getCompanyProducts",
			wantMethod: "GET", wantPath: "/companyproducts",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := company_products.New(transport, strfmt.Default).
					GetCompanyProducts(company_products.NewGetCompanyProductsParams(), nil)
				return err
			},
		},
		{
			name: "create product", wantID: "postCompanyProducts",
			wantMethod: "POST", wantPath: "/companyproducts",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := company_products.New(transport, strfmt.Default).
					PostCompanyProducts(company_products.NewPostCompanyProductsParams(), nil)
				return err
			},
		},
		{
			name: "list or get service", wantID: "getCompanyServices",
			wantMethod: "GET", wantPath: "/companyservices",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := company_services.New(transport, strfmt.Default).
					GetCompanyServices(company_services.NewGetCompanyServicesParams(), nil)
				return err
			},
		},
		{
			name: "create service", wantID: "postCompanyServices",
			wantMethod: "POST", wantPath: "/companyservices",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := company_services.New(transport, strfmt.Default).
					PostCompanyServices(company_services.NewPostCompanyServicesParams(), nil)
				return err
			},
		},
		{
			name: "CAS update service", wantID: "putCompanyService",
			wantMethod: "PUT", wantPath: "/companyservices",
			call: func(transport openapiruntime.ContextualTransport) error {
				_, err := company_services.New(transport, strfmt.Default).
					PutCompanyService(company_services.NewPutCompanyServiceParams(), nil)
				return err
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			transport := &catalogCaptureTransport{}
			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)
			}
		})
	}
}

func TestGeneratedCompanyCatalogListGetAndMutationShapes(t *testing.T) {
	productID, serviceID := "product-id", "service-id"
	if got := company_products.NewGetCompanyProductsParams().
		WithCompanyProductID(&productID).CompanyProductID; got == nil || *got != productID {
		t.Fatal("CompanyProduct get-by-ID filter is absent")
	}
	if got := company_services.NewGetCompanyServicesParams().
		WithCompanyServiceID(&serviceID).CompanyServiceID; got == nil || *got != serviceID {
		t.Fatal("CompanyService get-by-ID filter is absent")
	}

	productRequest := &research_models.CompanyProductRequest{
		Data: []*research_models.CompanyProduct{{ID: "ignored-client-id"}},
	}
	if got := company_products.NewPostCompanyProductsParams().
		WithCompanyProductRequest(productRequest).CompanyProductRequest; got != productRequest {
		t.Fatal("CompanyProduct create request body is absent")
	}

	serviceRequest := &research_models.CompanyServiceRequest{
		Data: []*research_models.CompanyService{{
			ID: "service-id", LastModifiedDate: stringPointer("2026-07-24T12:00:00Z"),
		}},
	}
	if got := company_services.NewPostCompanyServicesParams().
		WithCompanyServiceRequest(serviceRequest).CompanyServiceRequest; got != serviceRequest {
		t.Fatal("CompanyService create request body is absent")
	}
	if got := company_services.NewPutCompanyServiceParams().
		WithCompanyServiceRequest(serviceRequest).CompanyServiceRequest; got != serviceRequest {
		t.Fatal("CompanyService CAS update request body is absent")
	}
}

func TestGeneratedCompanyCatalogNegativeMutationResponses(t *testing.T) {
	for name, response := range map[string]interface{ IsCode(int) bool }{
		"product unauthorized": company_products.NewPostCompanyProductsUnauthorized(),
		"product forbidden":    company_products.NewPostCompanyProductsForbidden(),
		"product not found":    company_products.NewPostCompanyProductsNotFound(),
		"product invalid":      company_products.NewPostCompanyProductsUnprocessableEntity(),
		"service unauthorized": company_services.NewPostCompanyServicesUnauthorized(),
		"service forbidden":    company_services.NewPostCompanyServicesForbidden(),
		"service invalid":      company_services.NewPostCompanyServicesUnprocessableEntity(),
		"update unauthorized":  company_services.NewPutCompanyServiceUnauthorized(),
		"update forbidden":     company_services.NewPutCompanyServiceForbidden(),
		"update not found":     company_services.NewPutCompanyServiceNotFound(),
		"update conflict":      company_services.NewPutCompanyServiceConflict(),
		"update invalid":       company_services.NewPutCompanyServiceUnprocessableEntity(),
	} {
		t.Run(name, func(t *testing.T) {
			expected := map[string]int{
				"product unauthorized": 401, "product forbidden": 403,
				"product not found": 404, "product invalid": 422,
				"service unauthorized": 401, "service forbidden": 403,
				"service invalid": 422, "update unauthorized": 401,
				"update forbidden": 403, "update not found": 404,
				"update conflict": 409, "update invalid": 422,
			}[name]
			if !response.IsCode(expected) {
				t.Errorf("%T does not preserve status %d", response, expected)
			}
		})
	}
}

func TestGeneratedCompanyCatalogDoesNotExposeUnsafeMutations(t *testing.T) {
	productContract := reflect.TypeOf((*company_products.ClientService)(nil)).Elem()
	for _, method := range []string{
		"PutCompanyProduct", "PutCompanyProductContext",
		"DeleteCompanyProduct", "DeleteCompanyProductContext",
	} {
		if _, exists := productContract.MethodByName(method); exists {
			t.Errorf("CompanyProduct client exposes forbidden mutation %s", method)
		}
	}

	serviceContract := reflect.TypeOf((*company_services.ClientService)(nil)).Elem()
	for _, method := range []string{"DeleteCompanyService", "DeleteCompanyServiceContext"} {
		if _, exists := serviceContract.MethodByName(method); exists {
			t.Errorf("CompanyService client exposes forbidden mutation %s", method)
		}
	}
}

func TestCompanyCatalogProviderProjectionRequiresCompoundAuth(t *testing.T) {
	repoRoot := companyCatalogRepoRoot(t)
	document, err := loads.Spec(filepath.Join(repoRoot, "swagger", "research-vernonkeenan.yaml"))
	if err != nil {
		t.Fatalf("load Research provider projection: %v", err)
	}
	products := document.Spec().Paths.Paths["/companyproducts"]
	services := document.Spec().Paths.Paths["/companyservices"]
	operations := []*struct {
		Security []map[string][]string
	}{
		{Security: products.Get.Security},
		{Security: products.Post.Security},
		{Security: services.Get.Security},
		{Security: services.Post.Security},
		{Security: services.Put.Security},
	}
	for _, operation := range operations {
		if len(operation.Security) != 1 {
			t.Fatalf("catalog operation security alternatives = %d, want exactly one compound alternative", len(operation.Security))
		}
		if _, ok := operation.Security[0]["ApiKeyAuth"]; !ok {
			t.Fatal("catalog operation is missing ApiKeyAuth")
		}
		if _, ok := operation.Security[0]["kvSessionCookie"]; !ok {
			t.Fatal("catalog operation is missing kvSessionCookie")
		}
	}
	if products.Put != nil || products.Delete != nil || services.Delete != nil {
		t.Fatal("provider projection reintroduced Product PUT or a catalog DELETE")
	}
}

func TestGeneratedCompanyCatalogHasNoProviderRuntimeOrEmbeddedSecrets(t *testing.T) {
	repoRoot := companyCatalogRepoRoot(t)
	targets := []string{
		filepath.Join(repoRoot, "api", "research", "research_client", "company_products"),
		filepath.Join(repoRoot, "api", "research", "research_client", "company_services"),
	}
	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_api", "simple_salesforce", "sf-gate",
				"go-force", "soql", "/services/apex", "cache.",
			} {
				if strings.Contains(lower, forbidden) {
					t.Errorf("%s contains forbidden provider 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 catalog target %s: %v", target, err)
		}
	}
}

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

func stringPointer(value string) *string {
	return &value
}
