package server

import (
	"net/http"
	"reflect"

	"github.com/mem9-ai/drive9/pkg/backend"
	"github.com/mem9-ai/drive9/pkg/journal"
	"github.com/mem9-ai/drive9/pkg/openapi"
	"github.com/mem9-ai/drive9/pkg/s3client"
	"github.com/mem9-ai/drive9/pkg/vault"
)

// This file is the single source of truth for the generated OpenAPI
// documents in docs/api/. It lives in the server package so it can reference
// the (mostly unexported) request/response structs the handlers actually
// encode; pkg/openapi reflects those types into JSON Schema components.
//
// After editing this registry, regenerate the documents with:
//
//	go run ./cmd/swagger-gen
//
// and commit the updated JSON files together with the registry change.

// ctrlDocs is the set of control-plane documents (TiDB Cloud API-key and
// TiDB Cloud JWT authentication).
var ctrlDocs = []openapi.DocKind{openapi.ControlPlaneAKSK, openapi.ControlPlaneJWT}

// allDocs is every generated document.
var allDocs = []openapi.DocKind{openapi.ControlPlaneAKSK, openapi.ControlPlaneJWT, openapi.DataPlane}

// openapiRef builds a $ref schema for the given component name. The ref is
// resolved against the schema registry; registry entries are created by the
// endpoints that use these types (see the registry below), so every name used
// here must also be produced by an endpoint in this file.
func openapiRef(name string) map[string]any {
	return map[string]any{"$ref": "#/components/schemas/" + name}
}

func ref(s openapi.SchemaRef) *openapi.SchemaRef { return &s }

func rf(t reflect.Type) *openapi.SchemaRef { return ref(openapi.Ref(t)) }

// endpoint is a convenience constructor for registry entries.
func endpoint(method, path, summary string, docs []openapi.DocKind) openapi.Endpoint {
	return openapi.Endpoint{Method: method, Path: path, Summary: summary, Docs: docs}
}

func ok(schema *openapi.SchemaRef, desc string) []openapi.Response {
	return []openapi.Response{{Code: http.StatusOK, Description: desc, Schema: schema}}
}

func created(schema *openapi.SchemaRef, desc string) []openapi.Response {
	return []openapi.Response{{Code: http.StatusCreated, Description: desc, Schema: schema}}
}

func accepted(schema *openapi.SchemaRef, desc string) []openapi.Response {
	return []openapi.Response{{Code: http.StatusAccepted, Description: desc, Schema: schema}}
}

// queryParam builds a required query parameter.
func queryParam(name string, schema openapi.SchemaRef) openapi.Parameter {
	return openapi.Parameter{In: "query", Name: name, Required: true, Schema: schema}
}

// queryParamOpt builds an optional query parameter.
func queryParamOpt(name string, schema openapi.SchemaRef) openapi.Parameter {
	return openapi.Parameter{In: "query", Name: name, Schema: schema}
}

func pathParam(name string, schema openapi.SchemaRef) openapi.Parameter {
	return openapi.Parameter{In: "path", Name: name, Required: true, Schema: schema}
}

func strSchema() openapi.SchemaRef { return openapi.String() }
func intSchema() openapi.SchemaRef { return openapi.Integer("int64") }

func tenantIDParam() openapi.Parameter {
	return queryParamOpt("tenant_id", strSchema())
}

// openapiRegistry returns the complete endpoint registry shared by all three
// generated documents.
func openapiRegistry() []openapi.Endpoint {
	eps := []openapi.Endpoint{}

	// ------------------------------------------------------------------
	// Control plane: tenant provisioning and admin.
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodPost, "/v1/provision", "Provision a new tenant", ctrlDocs).
			WithBody(rf(reflect.TypeOf(credentialProvisionRequest{})), "Public and private TiDB Cloud API keys, plus optional quota fields. The authenticated TiDB Cloud credentials may override the body keys (JWT / inner-auth mode).").
			WithResponses(accepted(provisionAcceptedRef, "Tenant accepted; provisioning runs asynchronously. Body: tenant_id, api_key, status (and provider-specific fields).")),

		endpoint(http.MethodGet, "/v1/quota", "Query tenant quota configuration and usage", allDocs).
			WithParams(queryParamOpt("tenant_id", strSchema()), tenantIDHeaderParam()).
			WithDescription("Tenant is selected by the tenant_id query parameter, falling back to the X-Drive9-Tenant-ID header. With a drive9 API key the tenant is implied by the key.").
			WithResponses(ok(rf(reflect.TypeOf(quotaResponse{})), "Quota configuration and current usage.")),

		endpoint(http.MethodPost, "/v1/quota", "Set or update tenant quota limits", ctrlDocs).
			WithBody(rf(reflect.TypeOf(quotaRequest{})), "Tenant id plus quota fields; public_key/private_key in the body fall back to the X-TiDBCloud-* headers.").
			WithResponses(ok(rf(reflect.TypeOf(quotaResponse{})), "Updated quota configuration and usage.")),

		endpoint(http.MethodGet, "/v1/admin/tenants", "List tenants of the authenticated organization", ctrlDocs).
			WithParams(
				queryParamOpt("page_size", intSchema()),
				queryParamOpt("page", intSchema()),
				queryParamOpt("display_name", strSchema()),
				queryParamOpt("label", strSchema()),
			).
			WithResponses(ok(rf(reflect.TypeOf(adminTenantListResponse{})), "Paginated tenant list with quota snapshots.")),

		endpoint(http.MethodPost, "/v1/admin/tenants", "Create a tenant (claims from the organization tenant pool, or provisions directly)", ctrlDocs).
			WithBody(rf(reflect.TypeOf(adminTenantCreateRequest{})), "Tenant creation options including optional quota fields.").
			WithResponses(accepted(rf(reflect.TypeOf(adminTenantCreateResponse{})), "Tenant accepted; provisioning is asynchronous.")),

		endpoint(http.MethodGet, "/v1/admin/tenants/{tenant_id}", "Get one tenant with quota config and usage", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(adminTenantResponse{})), "Tenant details.")),

		endpoint(http.MethodDelete, "/v1/admin/tenants/{tenant_id}", "Delete a tenant (async cleanup)", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema())).
			WithOptionalBody(credentialsBodyRef, "Optional public_key/private_key credentials for cluster-backed deprovisioning.").
			WithResponses(accepted(rf(reflect.TypeOf(adminTenantDeleteResponse{})), "Tenant delete accepted; cleanup runs asynchronously.")),

		endpoint(http.MethodPost, "/v1/admin/tenants/{tenant_id}/quota", "Update tenant quota limits", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema())).
			WithBody(rf(reflect.TypeOf(adminTenantQuotaRequest{})), "Quota fields to update.").
			WithResponses(ok(rf(reflect.TypeOf(adminQuotaResponse{})), "Updated quota view.")),

		endpoint(http.MethodGet, "/v1/admin/tenants/{tenant_id}/embedding-config", "Get tenant embedding configuration", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(embeddingConfigResponse{})), "Effective embedding config (api_key masked).")),

		endpoint(http.MethodPut, "/v1/admin/tenants/{tenant_id}/embedding-config", "Write tenant embedding configuration", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema())).
			WithBody(rf(reflect.TypeOf(embeddingConfigPutRequest{})), "Embedding provider configuration.").
			WithResponses(ok(rf(reflect.TypeOf(embeddingConfigResponse{})), "Persisted embedding config.")),

		endpoint(http.MethodGet, "/v1/admin/tenants/{tenant_id}/extract-config/{media_type}", "Get tenant extract configuration", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema()), pathParam("media_type", strSchema())).
			WithDescription("media_type is one of image, audio or video.").
			WithResponses(ok(rf(reflect.TypeOf(extractConfigResponse{})), "Effective extract config (provider keys masked).")),

		endpoint(http.MethodPut, "/v1/admin/tenants/{tenant_id}/extract-config/{media_type}", "Write tenant extract configuration", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema()), pathParam("media_type", strSchema())).
			WithDescription("media_type is one of image, audio or video.").
			WithBody(rf(reflect.TypeOf(extractConfigPutRequest{})), "Partial extract config update.").
			WithResponses(ok(rf(reflect.TypeOf(extractConfigResponse{})), "Persisted extract config.")),

		endpoint(http.MethodGet, "/v1/admin/tenants/{tenant_id}/object-namespace", "Get the tenant object namespace binding", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema())).
			WithResponses(ok(tenantNamespaceRef, "tenant_id and namespace_id.")),

		endpoint(http.MethodPut, "/v1/admin/tenants/{tenant_id}/object-namespace", "Set the tenant object namespace binding", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema())).
			WithBody(rf(reflect.TypeOf(adminObjectNamespaceRequest{})), "namespace_id to bind.").
			WithResponses(ok(tenantNamespaceRef, "tenant_id and namespace_id.")),

		endpoint(http.MethodDelete, "/v1/admin/tenants/{tenant_id}/object-namespace", "Clear the tenant object namespace binding", ctrlDocs).
			WithParams(pathParam("tenant_id", strSchema())).
			WithResponses(ok(tenantNamespaceRef, "tenant_id and empty namespace_id.")),

		endpoint(http.MethodGet, "/v1/admin/object-backends", "List object backends of the authenticated organization", ctrlDocs).
			WithResponses(ok(backendsListRef, "List of object backend views.")),

		endpoint(http.MethodPost, "/v1/admin/object-backends", "Create an object backend", ctrlDocs).
			WithBody(rf(reflect.TypeOf(adminObjectBackendRequest{})), "Object backend definition (scheme, bucket, credentials).").
			WithResponses(created(rf(reflect.TypeOf(adminObjectBackendView{})), "Created object backend.")),

		endpoint(http.MethodGet, "/v1/admin/object-backends/{backend_id}", "Get one object backend", ctrlDocs).
			WithParams(pathParam("backend_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(adminObjectBackendView{})), "Object backend view.")),

		endpoint(http.MethodPatch, "/v1/admin/object-backends/{backend_id}", "Update an object backend (partial patch)", ctrlDocs).
			WithParams(pathParam("backend_id", strSchema())).
			WithBody(rf(reflect.TypeOf(adminObjectBackendPatchRequest{})), "Fields to update (all optional).").
			WithResponses(ok(rf(reflect.TypeOf(adminObjectBackendView{})), "Updated object backend.")),

		endpoint(http.MethodPut, "/v1/admin/object-backends/{backend_id}", "Update an object backend (PUT alias of PATCH)", ctrlDocs).
			WithParams(pathParam("backend_id", strSchema())).
			WithBody(rf(reflect.TypeOf(adminObjectBackendPatchRequest{})), "Fields to update (all optional).").
			WithResponses(ok(rf(reflect.TypeOf(adminObjectBackendView{})), "Updated object backend.")),

		endpoint(http.MethodDelete, "/v1/admin/object-backends/{backend_id}", "Delete an object backend", ctrlDocs).
			WithParams(pathParam("backend_id", strSchema())).
			WithResponses(ok(objectBackendDeleteRef, "id and status.")),

		endpoint(http.MethodPost, "/v1/admin/tenant-pool", "Create a free-tenant pool for the organization", ctrlDocs).
			WithBody(rf(reflect.TypeOf(adminTenantPoolRequest{})), "Pool target size and options.").
			WithResponses(accepted(rf(reflect.TypeOf(adminTenantPoolResponse{})), "Pool create accepted; provisioning is asynchronous.")),

		endpoint(http.MethodGet, "/v1/admin/tenant-pool", "Get the organization tenant pool status", ctrlDocs).
			WithResponses(ok(rf(reflect.TypeOf(adminTenantPoolResponse{})), "Pool status.")),

		endpoint(http.MethodPatch, "/v1/admin/tenant-pool", "Resize the organization tenant pool", ctrlDocs).
			WithBody(rf(reflect.TypeOf(adminTenantPoolRequest{})), "New target size.").
			WithResponses(accepted(rf(reflect.TypeOf(adminTenantPoolResponse{})), "Resize accepted; runs asynchronously.")),

		endpoint(http.MethodDelete, "/v1/admin/tenant-pool", "Delete the organization tenant pool", ctrlDocs).
			WithOptionalBody(credentialsBodyRef, "Optional public_key/private_key credentials.").
			WithResponses(accepted(rf(reflect.TypeOf(adminTenantPoolResponse{})), "Pool delete accepted; cleanup runs asynchronously.")),
	)

	// ------------------------------------------------------------------
	// Tokens: control-plane token management AND data-plane scoped tokens.
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodPost, "/v1/tokens", "Issue a scoped token for the authenticated tenant (owner drive9 key only)", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(issueScopedTokenRequest{})), "Token subject, TTL and FS scope rules.").
			WithResponses(created(rf(reflect.TypeOf(scopedTokenResponse{})), "Issued scoped token.")),

		endpoint(http.MethodGet, "/v1/tokens", "List API keys / tokens", allDocs).
			WithParams(
				tenantIDParam(),
				queryParamOpt("include_expired", openapi.Boolean()),
				queryParamOpt("offset", intSchema()),
				queryParamOpt("limit", intSchema()),
			).
			WithDescription("With a drive9 key, lists the authenticated tenant's tokens (tenant_id must match when provided). With control-plane credentials, lists organization tokens; tenant_id selects one tenant, omit for org-wide listing.").
			WithResponses(ok(rf(reflect.TypeOf(tokenListResponse{})), "Token list with pagination.")),

		endpoint(http.MethodPost, "/v1/tokens/refresh", "Rotate the current drive9 bearer token", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(refreshTokenRequest{})), "Optional TTL override.").
			WithResponses(ok(rf(reflect.TypeOf(refreshTokenResponse{})), "Rotated token.")),

		endpoint(http.MethodPost, "/v1/tokens/generate", "Generate a control-plane owner token for a tenant", ctrlDocs).
			WithParams(tenantIDParam()).
			WithBody(rf(reflect.TypeOf(generateTokenRequest{})), "Tenant, key name, TTL; public_key/private_key may be in the body or the X-TiDBCloud-* headers.").
			WithResponses(created(rf(reflect.TypeOf(generateTokenResponse{})), "Generated owner token.")),

		endpoint(http.MethodPost, "/v1/tokens/revoke", "Revoke a token by its API key", ctrlDocs).
			WithParams(tenantIDParam()).
			WithBody(rf(reflect.TypeOf(controlPlaneRevokeByAPIKeyRequest{})), "api_key to revoke plus optional credentials.").
			WithResponses(ok(rf(reflect.TypeOf(controlPlaneTokenMutationResponse{})), "Revoked token summary.")),

		endpoint(http.MethodPost, "/v1/tokens/revoke", "Revoke a scoped token by its API key (drive9 owner key)", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(revokeScopedTokenByAPIKeyRequest{})), "api_key to revoke.").
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodDelete, "/v1/tokens/{token_id}", "Revoke a token by id (control plane)", ctrlDocs).
			WithParams(pathParam("token_id", strSchema()), tenantIDParam()).
			WithOptionalBody(rf(reflect.TypeOf(tokenDeleteRequest{})), "Optional credentials.").
			WithResponses(ok(rf(reflect.TypeOf(controlPlaneTokenMutationResponse{})), "Revoked token summary.")),

		endpoint(http.MethodDelete, "/v1/tokens/{token_id}", "Revoke a token by id (drive9 owner key)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("token_id", strSchema())).
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodPost, "/v1/tokens/{token_id}/activate", "Activate a token (disabled → active)", allDocs).
			WithParams(pathParam("token_id", strSchema()), tenantIDParam()).
			WithOptionalBody(rf(reflect.TypeOf(tokenStatusTransitionRequest{})), "Optional credentials.").
			WithResponses(ok(rf(reflect.TypeOf(tokenStatusTransitionResponse{})), "New token status.")),

		endpoint(http.MethodPost, "/v1/tokens/{token_id}/deactivate", "Deactivate a token (active → disabled)", allDocs).
			WithParams(pathParam("token_id", strSchema()), tenantIDParam()).
			WithOptionalBody(rf(reflect.TypeOf(tokenStatusTransitionRequest{})), "Optional credentials.").
			WithResponses(ok(rf(reflect.TypeOf(tokenStatusTransitionResponse{})), "New token status.")),
	)

	// ------------------------------------------------------------------
	// Data plane: filesystem.
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodGet, "/v1/fs/{path}", "Read a file or query filesystem metadata", []openapi.DocKind{openapi.DataPlane}).
			WithParams(
				pathParam("path", strSchema()),
				queryParamOpt("stat", openapi.Boolean()),
				queryParamOpt("list", openapi.Boolean()),
				queryParamOpt("grep", strSchema()),
				queryParamOpt("find", openapi.Boolean()),
				queryParamOpt("limit", intSchema()),
				queryParamOpt("name", strSchema()),
				queryParamOpt("tag", strSchema()),
				queryParamOpt("newer", strSchema()),
				queryParamOpt("older", strSchema()),
				queryParamOpt("minsize", intSchema()),
				queryParamOpt("maxsize", intSchema()),
				queryParamOpt("layer", strSchema()),
			).
			WithDescription("Without query parameters the file bytes are returned (application/octet-stream; S3-backed files redirect with 302). With one action selector: stat=1 returns enriched JSON metadata, list=1 returns directory entries, grep=<query> returns grep matches, find=1 returns matching nodes (name/tag/newer/older/minsize/maxsize/limit filters).").
			WithResponses([]openapi.Response{
				{Code: http.StatusOK, Description: "File bytes (plain GET) or JSON metadata for action selectors (stat=1 enriched stat, list=1 directory entries, grep=<q> matches, find=1 nodes).", Content: []openapi.MediaContent{
					{MediaType: "application/octet-stream", Schema: fsReadRespRef},
					{MediaType: "application/json", Schema: fsJsonRespRef},
				}},
				{Code: http.StatusFound, Description: "Redirect to the presigned object URL for S3-backed files."},
			}),

		endpoint(http.MethodHead, "/v1/fs/{path}", "Stat a file (header-only)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("path", strSchema())).
			WithDescription("Returns X-Dat9-* headers (size, isdir, revision, mtime) with no body.").
			WithResponses([]openapi.Response{{Code: http.StatusOK, Description: "Stat headers; no body."}}),

		endpoint(http.MethodPut, "/v1/fs/{path}", "Write a file (raw body)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("path", strSchema())).
			WithDescription("Writes the raw request body to the path. For large files (>= inline threshold) the server returns 202 with an upload plan; send X-Dat9-Part-Checksums on those. Optional headers: X-Dat9-Expected-Revision, X-Dat9-Tag, X-Dat9-Content-Length, X-Dat9-Description.").
			WithBody(&openapi.SchemaRef{Raw: map[string]any{"type": "string", "format": "binary"}}, "Raw file bytes.", "application/octet-stream").
			WithHeaders("X-Dat9-Expected-Revision", "X-Dat9-Tag", "X-Dat9-Content-Length", "X-Dat9-Part-Checksums", "X-Dat9-Description").
			WithResponses([]openapi.Response{
				{Code: http.StatusOK, Description: "Write committed. Body: {status, revision}.", Schema: revisionRef},
				{Code: http.StatusAccepted, Description: "Large-file upload initiated. Body: upload plan with presigned part URLs.", Schema: rf(reflect.TypeOf(backend.UploadPlan{})), MediaType: "application/json"},
			}),

		endpoint(http.MethodPatch, "/v1/fs/{path}", "Patch a large file (dirty parts)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("path", strSchema())).
			WithBody(patchBodyRef, "new_size, dirty_parts (1-based), optional part_size/expected_revision.").
			WithResponses(accepted(rf(reflect.TypeOf(backend.PatchPlan{})), "Patch upload plan with presigned part URLs.")),

		endpoint(http.MethodPost, "/v1/fs/{path}", "Filesystem action (append-log/append/copy/rename/mkdir/chmod/setmeta/create/symlink/hardlink)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(
				pathParam("path", strSchema()),
				queryParamOpt("append-log", openapi.Boolean()),
				queryParamOpt("append", openapi.Boolean()),
				queryParamOpt("copy", openapi.Boolean()),
				queryParamOpt("rename", openapi.Boolean()),
				queryParamOpt("mkdir", openapi.Boolean()),
				queryParamOpt("chmod", openapi.Boolean()),
				queryParamOpt("setmeta", openapi.Boolean()),
				queryParamOpt("create", openapi.Boolean()),
				queryParamOpt("symlink", openapi.Boolean()),
				queryParamOpt("hardlink", openapi.Boolean()),
				queryParamOpt("mode", intSchema()),
				openapi.Parameter{In: "header", Name: "X-Dat9-Expected-Revision", Description: "Required when append-log is selected; non-negative committed revision.", Schema: intSchema()},
				openapi.Parameter{In: "header", Name: "X-Dat9-Expected-Size", Description: "Required when append-log is selected; non-negative committed byte size.", Schema: intSchema()},
			).
			WithHeaders("X-Dat9-Copy-Source", "X-Dat9-Rename-Source", "X-Dat9-Hardlink-Source").
			WithDescription("Exactly one action selector must be present. append-log requires Content-Length, X-Dat9-Expected-Revision, and X-Dat9-Expected-Size; its raw binary body is appended at the committed size and 200 returns {revision, size_bytes}. append requires body {append_size, part_size?, expected_revision?} and returns 202 with an append plan; chmod takes body {mode}; symlink takes body {target}; setmeta takes body {tags?, description?} and updates user tags and/or the description of an existing file without rewriting content (absent fields stay unchanged, an empty tags object clears user tags, an empty description clears it). copy requires the X-Dat9-Copy-Source header, rename requires X-Dat9-Rename-Source, hardlink requires X-Dat9-Hardlink-Source; mkdir accepts query mode; create/copy/rename/mkdir/hardlink return {status: ok}.").
			WithBodyContent("Action-specific JSON body; append-log uses raw binary bytes with an explicit Content-Length (zero is valid for creation). Bodyless actions remain supported.", false,
				openapi.MediaContent{MediaType: "application/json", Schema: fsPostBodyRef},
				openapi.MediaContent{MediaType: "application/octet-stream", Schema: &openapi.SchemaRef{Raw: map[string]any{"type": "string", "format": "binary"}}},
			).
			WithClientError(fsActionErrorRef).
			WithResponses([]openapi.Response{
				{Code: http.StatusOK, Description: "Action completed. append-log returns {revision, size_bytes}; other actions return {status, ...}.", Schema: fsPostResponseRef},
				{Code: http.StatusAccepted, Description: "Append upload plan (append=1).", Schema: rf(reflect.TypeOf(backend.AppendPlan{})), MediaType: "application/json"},
			}),

		endpoint(http.MethodDelete, "/v1/fs/{path}", "Delete a file or directory", []openapi.DocKind{openapi.DataPlane}).
			WithParams(
				pathParam("path", strSchema()),
				queryParamOpt("recursive", openapi.Boolean()),
				queryParamOpt("kind", strSchema()),
			).
			WithDescription("kind is file or dir for non-recursive deletes; recursive=1 removes a directory tree.").
			WithResponses(ok(statusOKRef, "status: ok")),
	)

	// ------------------------------------------------------------------
	// Data plane: batch endpoints.
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodPost, "/v1/fs:batch-stat", "Stat multiple paths in one request", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(batchStatRequest{})), "paths to stat, optionally include_checksum.").
			WithResponses(ok(rf(reflect.TypeOf(batchStatResponse{})), "Per-path results.")),

		endpoint(http.MethodPost, "/v1/fs:batch-read-small", "Read multiple small files in one request", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(batchReadSmallRequest{})), "paths and optional max_bytes cap.").
			WithResponses(ok(rf(reflect.TypeOf(batchReadSmallResponse{})), "Per-path contents.")),

		endpoint(http.MethodPost, "/v1/fs:batch-write", "Write multiple files in one request", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(batchWriteRequest{})), "items with path, expected_revision, data (base64).").
			WithResponses(ok(rf(reflect.TypeOf(batchWriteResponse{})), "Per-path results.")),
	)

	// ------------------------------------------------------------------
	// Data plane: uploads (V1 and V2 multipart).
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodGet, "/v1/uploads", "List in-progress uploads at a path", []openapi.DocKind{openapi.DataPlane}).
			WithParams(queryParam("path", strSchema()), queryParamOpt("status", strSchema())).
			WithDescription("Lists upload sessions targeting the given path. status defaults to uploading.").
			WithResponses(ok(uploadsListRespRef, "Upload session list.")),

		endpoint(http.MethodPost, "/v1/uploads", "Initiate a V1 multipart upload (alias of /v1/uploads/initiate)", []openapi.DocKind{openapi.DataPlane}).
			WithBody(uploadInitiateBodyRef, "path, total_size, part_checksums (one per part), optional expected_revision/description.").
			WithResponses(accepted(rf(reflect.TypeOf(backend.UploadPlan{})), "Upload plan with presigned part URLs.")),

		endpoint(http.MethodPost, "/v1/uploads/initiate", "Initiate a V1 multipart upload", []openapi.DocKind{openapi.DataPlane}).
			WithBody(uploadInitiateBodyRef, "path, total_size, part_checksums (one per part), optional expected_revision/description.").
			WithResponses(accepted(rf(reflect.TypeOf(backend.UploadPlan{})), "Upload plan with presigned part URLs.")),

		endpoint(http.MethodPost, "/v1/uploads/{upload_id}/complete", "Complete a V1 multipart upload", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("upload_id", strSchema())).
			WithBody(rf(reflect.TypeOf(uploadCompleteRequest{})), "Optional tags and whole-file checksum.").
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodPost, "/v1/uploads/{upload_id}/resume", "Resume a V1 multipart upload", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("upload_id", strSchema())).
			WithDescription("Body is JSON {part_checksums: [...]} or a plain list of checksums (one per line) with text/plain content type.").
			WithBody(resumeBodyRef, "part_checksums for remaining parts.").
			WithResponses(ok(rf(reflect.TypeOf(backend.UploadPlan{})), "Resumed upload plan.")),

		endpoint(http.MethodGet, "/v1/uploads/{upload_id}/resume", "Resume a V1 multipart upload (GET alias)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("upload_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(backend.UploadPlan{})), "Resumed upload plan.")),

		endpoint(http.MethodDelete, "/v1/uploads/{upload_id}", "Abort a V1 multipart upload", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("upload_id", strSchema())).
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodPost, "/v2/uploads/initiate", "Initiate a V2 multipart upload", []openapi.DocKind{openapi.DataPlane}).
			WithBody(v2UploadInitiateBodyRef, "path, total_size, optional expected_revision/description.").
			WithResponses(accepted(rf(reflect.TypeOf(backend.UploadPlanV2{})), "V2 upload plan (no presigned URLs; use presign endpoints).")),

		endpoint(http.MethodPost, "/v2/uploads/{upload_id}/presign", "Presign one upload part (V2)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("upload_id", strSchema())).
			WithBody(presignPartBodyRef, "part_number and optional checksum.").
			WithResponses(ok(rf(reflect.TypeOf(s3client.UploadPartURL{})), "Presigned part URL.")),

		endpoint(http.MethodPost, "/v2/uploads/{upload_id}/presign-batch", "Presign multiple upload parts (V2)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("upload_id", strSchema())).
			WithBody(presignBatchBodyRef, "List of parts to presign.").
			WithResponses(ok(presignBatchRespRef, "List of presigned part URLs.")),

		endpoint(http.MethodPost, "/v2/uploads/{upload_id}/complete", "Complete a V2 multipart upload", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("upload_id", strSchema())).
			WithBody(rf(reflect.TypeOf(uploadCompleteRequest{})), "Optional tags and whole-file checksum.").
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodPost, "/v2/uploads/{upload_id}/abort", "Abort a V2 multipart upload", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("upload_id", strSchema())).
			WithResponses(ok(statusOKRef, "status: ok")),
	)

	// ------------------------------------------------------------------
	// Data plane: tenant lifecycle and misc.
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodDelete, "/v1/tenant", "Delete the current tenant", []openapi.DocKind{openapi.DataPlane}).
			WithOptionalBody(credentialsBodyRef, "Optional public_key/private_key (required for tidb_cloud_native tenants).").
			WithResponses(accepted(statusMapRef, "status: deleting or deleted")),

		endpoint(http.MethodPost, "/v1/fork", "Create an asynchronous fork of the current tenant", []openapi.DocKind{openapi.DataPlane}).
			WithOptionalBody(rf(reflect.TypeOf(forkRequest{})), "Optional fork options; may be empty.").
			WithResponses(accepted(rf(reflect.TypeOf(forkResponse{})), "Fork accepted; provisioning is asynchronous.")),

		endpoint(http.MethodDelete, "/v1/fork", "Delete the current fork tenant", []openapi.DocKind{openapi.DataPlane}).
			WithOptionalBody(credentialsBodyRef, "Optional public_key/private_key.").
			WithResponses(accepted(statusMapRef, "status: deleting or deleted")),

		endpoint(http.MethodPost, "/v1/sql", "Execute a SQL statement against the tenant metadata database", []openapi.DocKind{openapi.DataPlane}).
			WithBody(sqlBodyRef, "SQL query to execute.").
			WithResponses(ok(sqlRespRef, "Query result rows (objects keyed by column name).")),

		endpoint(http.MethodPost, "/v1/migration/events", "Ingest a migration CAS event", []openapi.DocKind{openapi.DataPlane}).
			WithBody(migrationEventBodyRef, "Migration event payload with the exact field set described by the schema.").
			WithResponses([]openapi.Response{{Code: http.StatusNoContent, Description: "Event accepted."}}),

		endpoint(http.MethodGet, "/v1/events", "SSE change-notification stream", []openapi.DocKind{openapi.DataPlane}).
			WithParams(queryParamOpt("since", intSchema())).
			WithDescription("Server-sent events (text/event-stream). Event types: file_changed (ChangeEvent JSON), reset ({seq, reason, path?, op?, actor?}), heartbeat (every 30s).").
			WithResponses([]openapi.Response{{Code: http.StatusOK, Description: "SSE stream.", MediaType: "text/event-stream"}}),

		endpoint(http.MethodGet, "/v1/status", "Get the authenticated tenant's status and limits", []openapi.DocKind{openapi.DataPlane}).
			WithResponses(ok(rf(reflect.TypeOf(TenantStatusResponse{})), "Tenant status, max upload bytes, inline threshold and migration capabilities.")),

		endpoint(http.MethodPost, "/v1/object-credentials", "Mint scoped object-storage credentials for a URI", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(objectCredentialsRequest{})), "Object URI and write flag.").
			WithResponses(ok(rf(reflect.TypeOf(objectCredentialsResponse{})), "Scoped credentials (s3/cos/tos/oss/gs/az).")),
	)

	// ------------------------------------------------------------------
	// Data plane: journals.
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodPost, "/v1/journals", "Create an append-only journal", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(journal.CreateRequest{})), "Journal metadata.").
			WithResponses(ok(rf(reflect.TypeOf(journal.Journal{})), "Created journal.")),

		endpoint(http.MethodPost, "/v1/journals/{journal_id}/entries", "Append entries to a journal", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("journal_id", strSchema())).
			WithDescription("Body is a JSON array of entries, a single entry, or an NDJSON stream (Content-Type application/x-ndjson). Requires the Idempotency-Key header.").
			WithBody(rf(reflect.TypeOf([]journal.EntryInput{})), "Entries to append.").
			WithRequiredHeaders("Idempotency-Key").
			WithResponses(ok(rf(reflect.TypeOf(journal.AppendResponse{})), "Append result.")),

		endpoint(http.MethodGet, "/v1/journals/{journal_id}/entries", "List journal entries", []openapi.DocKind{openapi.DataPlane}).
			WithParams(
				pathParam("journal_id", strSchema()),
				queryParamOpt("after_seq", intSchema()),
				queryParamOpt("after", intSchema()),
				queryParamOpt("limit", intSchema()),
			).
			WithDescription("Returns an NDJSON stream (application/x-ndjson), one Entry per line.").
			WithResponses([]openapi.Response{{Code: http.StatusOK, Description: "NDJSON stream of entries.", Schema: rf(reflect.TypeOf([]journal.Entry{})), MediaType: "application/x-ndjson"}}),

		endpoint(http.MethodGet, "/v1/journals/{journal_id}/verify", "Verify a journal's hash chain", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("journal_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(journal.VerifyResult{})), "Verification result.")),

		endpoint(http.MethodGet, "/v1/journal-entries", "Search across journals", []openapi.DocKind{openapi.DataPlane}).
			WithParams(
				queryParamOpt("since", strSchema()),
				queryParamOpt("until", strSchema()),
				queryParamOpt("type", strSchema()),
				queryParamOpt("status", strSchema()),
				queryParamOpt("kind", strSchema()),
				queryParamOpt("subject", strSchema()),
				queryParamOpt("meta", strSchema()),
				queryParamOpt("limit", intSchema()),
				queryParamOpt("include", strSchema()),
				queryParamOpt("entries", strSchema()),
				queryParamOpt("actor", strSchema()),
				queryParamOpt("cursor", strSchema()),
			).
			WithDescription("Returns an NDJSON stream of SearchMatch objects (application/x-ndjson); the last line carries the next cursor.").
			WithResponses([]openapi.Response{{Code: http.StatusOK, Description: "NDJSON stream of search matches.", Schema: rf(reflect.TypeOf([]journal.SearchMatch{})), MediaType: "application/x-ndjson"}}),
	)

	// ------------------------------------------------------------------
	// Data plane: git workspaces.
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodPost, "/v1/git-workspaces", "Create or update a git workspace", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(gitWorkspaceRequest{})), "Workspace root, git url, refs.").
			WithResponses(ok(rf(reflect.TypeOf(gitWorkspaceResponse{})), "Workspace state.")),

		endpoint(http.MethodGet, "/v1/git-workspaces", "List git workspaces (or get one by root_path)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(queryParamOpt("root_path", strSchema())).
			WithDescription("With root_path returns a single workspace; without it returns the workspace list.").
			WithResponses(ok(gitWorkspaceUnionRef, "Workspace list (no root_path) or a single workspace (root_path given).")),

		endpoint(http.MethodGet, "/v1/git-workspaces/{workspace_id}", "Get a git workspace", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(gitWorkspaceResponse{})), "Workspace state.")),

		endpoint(http.MethodDelete, "/v1/git-workspaces/{workspace_id}", "Soft-delete a git workspace", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodPost, "/v1/git-workspaces/{workspace_id}/tree", "Replace a commit's tree", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithBody(rf(reflect.TypeOf(gitTreeReplaceRequest{})), "Commit and tree nodes.").
			WithResponses(ok(gitTreeReplaceRef, "status and replaced count.")),

		endpoint(http.MethodGet, "/v1/git-workspaces/{workspace_id}/tree", "List a commit's tree nodes", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema()), queryParam("commit_sha", strSchema())).
			WithResponses(ok(gitTreeListRef, "Tree nodes.")),

		endpoint(http.MethodPost, "/v1/git-workspaces/{workspace_id}/git-state", "Upsert git workspace state", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithBody(rf(reflect.TypeOf(gitStateRequest{})), "State payload (inline or storage reference).").
			WithResponses(ok(rf(reflect.TypeOf(gitStateResponse{})), "Stored state.")),

		endpoint(http.MethodPut, "/v1/git-workspaces/{workspace_id}/git-state", "Upsert git workspace state (PUT alias)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithBody(rf(reflect.TypeOf(gitStateRequest{})), "State payload (inline or storage reference).").
			WithResponses(ok(rf(reflect.TypeOf(gitStateResponse{})), "Stored state.")),

		endpoint(http.MethodGet, "/v1/git-workspaces/{workspace_id}/git-state", "Get git workspace state", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(gitStateResponse{})), "Stored state.")),

		endpoint(http.MethodPost, "/v1/git-workspaces/{workspace_id}/object-packs", "Upload a git object pack", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithBody(rf(reflect.TypeOf(gitObjectPackRequest{})), "Pack content or storage reference.").
			WithResponses(ok(rf(reflect.TypeOf(gitObjectPackResponse{})), "Stored pack.")),

		endpoint(http.MethodPut, "/v1/git-workspaces/{workspace_id}/object-packs", "Upload a git object pack (PUT alias)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithBody(rf(reflect.TypeOf(gitObjectPackRequest{})), "Pack content or storage reference.").
			WithResponses(ok(rf(reflect.TypeOf(gitObjectPackResponse{})), "Stored pack.")),

		endpoint(http.MethodGet, "/v1/git-workspaces/{workspace_id}/object-packs", "List git object packs", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithResponses(ok(gitPacksListRef, "Pack list without content.")),

		endpoint(http.MethodGet, "/v1/git-workspaces/{workspace_id}/object-packs/{pack_id}", "Get a git object pack", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema()), pathParam("pack_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(gitObjectPackResponse{})), "Pack with content.")),

		endpoint(http.MethodPost, "/v1/git-workspaces/{workspace_id}/overlay", "Upsert a git overlay entry", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithBody(rf(reflect.TypeOf(gitOverlayEntryRequest{})), "Overlay entry.").
			WithResponses(ok(rf(reflect.TypeOf(gitOverlayEntryResponse{})), "Stored overlay entry.")),

		endpoint(http.MethodPut, "/v1/git-workspaces/{workspace_id}/overlay", "Upsert a git overlay entry (PUT alias)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema())).
			WithBody(rf(reflect.TypeOf(gitOverlayEntryRequest{})), "Overlay entry.").
			WithResponses(ok(rf(reflect.TypeOf(gitOverlayEntryResponse{})), "Stored overlay entry.")),

		endpoint(http.MethodGet, "/v1/git-workspaces/{workspace_id}/overlay", "Get git overlay entries (or one by path)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("workspace_id", strSchema()), queryParamOpt("path", strSchema())).
			WithResponses(ok(gitOverlayUnionRef, "Overlay entry list (no path) or a single overlay entry (path given).")),
	)

	// ------------------------------------------------------------------
	// Data plane: layered filesystem (layers).
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodPost, "/v1/layers", "Create an FS layer", []openapi.DocKind{openapi.DataPlane}).
			WithBody(rf(reflect.TypeOf(fsLayerCreateRequest{})), "Layer name and options; layer_id may be omitted.").
			WithResponses(ok(rf(reflect.TypeOf(fsLayerResponse{})), "Created layer.")),

		endpoint(http.MethodGet, "/v1/layers", "List FS layers visible to the scope", []openapi.DocKind{openapi.DataPlane}).
			WithResponses(ok(layersListRef, "Layer list.")),

		endpoint(http.MethodGet, "/v1/layers/{layer_ref}", "Get an FS layer", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(fsLayerResponse{})), "Layer state.")),

		endpoint(http.MethodDelete, "/v1/layers/{layer_ref}", "Abandon an FS layer", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema()), queryParamOpt("cascade", openapi.Boolean())).
			WithResponses(ok(layerAbandonRef, "status and layer_id.")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/fork", "Fork a child layer", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithOptionalBody(rf(reflect.TypeOf(fsLayerForkRequest{})), "Optional fork options.").
			WithResponses(created(rf(reflect.TypeOf(fsLayerResponse{})), "Forked layer.")),

		endpoint(http.MethodGet, "/v1/layers/{layer_ref}/chain", "List the layer ancestry chain", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithResponses(ok(layerChainRef, "Chain entries.")),

		endpoint(http.MethodGet, "/v1/layers/{layer_ref}/diff", "List layer entries (merged view or raw log)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(
				pathParam("layer_ref", strSchema()),
				queryParamOpt("max_seq", intSchema()),
				queryParamOpt("replay", openapi.Boolean()),
				queryParamOpt("mode", strSchema()),
			).
			WithDescription("mode=log (or replay=1) returns the raw event log instead of the merged view.").
			WithResponses(ok(layerDiffRef, "Entries.")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/checkpoints", "Create a layer checkpoint", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithBody(rf(reflect.TypeOf(fsLayerCheckpointRequest{})), "Checkpoint options.").
			WithResponses(ok(rf(reflect.TypeOf(fsLayerCheckpointResponse{})), "Created checkpoint.")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/rollback", "Roll the layer overlay back to its base state", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/commit", "Apply layer changes to the live filesystem", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithResponses([]openapi.Response{
				{Code: http.StatusOK, Description: "Committed (status=committed).", Schema: rf(reflect.TypeOf(fsLayerCommitResponse{}))},
				{Code: http.StatusConflict, Description: "Conflicted (status=conflicted, conflicts listed).", Schema: rf(reflect.TypeOf(fsLayerCommitResponse{}))},
			}),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/uploads/initiate", "Initiate a layer object upload (V2 plan)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithBody(rf(reflect.TypeOf(fsLayerUploadInitiateRequest{})), "Entry path and size.").
			WithResponses(accepted(rf(reflect.TypeOf(backend.UploadPlanV2{})), "V2 upload plan.")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/uploads/{upload_id}/presign", "Presign a layer upload part", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema()), pathParam("upload_id", strSchema())).
			WithBody(presignPartBodyRef, "part_number and optional checksum.").
			WithResponses(ok(rf(reflect.TypeOf(s3client.UploadPartURL{})), "Presigned part URL.")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/uploads/{upload_id}/presign-batch", "Presign a batch of layer upload parts", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema()), pathParam("upload_id", strSchema())).
			WithBody(presignBatchBodyRef, "List of parts.").
			WithResponses(ok(presignBatchRespRef, "List of presigned part URLs.")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/uploads/{upload_id}/complete", "Complete a layer upload", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema()), pathParam("upload_id", strSchema())).
			WithBody(rf(reflect.TypeOf(fsLayerUploadCompleteRequest{})), "Parts to complete and optional checksum.").
			WithResponses(ok(rf(reflect.TypeOf(fsLayerEntryResponse{})), "Created entry.")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/uploads/{upload_id}/abort", "Abort a layer upload", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema()), pathParam("upload_id", strSchema())).
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/entries", "Upsert a layer entry", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithBody(rf(reflect.TypeOf(fsLayerEntryRequest{})), "Entry action and payload.").
			WithResponses(ok(rf(reflect.TypeOf(fsLayerEntryResponse{})), "Entry state.")),

		endpoint(http.MethodPut, "/v1/layers/{layer_ref}/entries", "Upsert a layer entry (PUT alias)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema())).
			WithBody(rf(reflect.TypeOf(fsLayerEntryRequest{})), "Entry action and payload.").
			WithResponses(ok(rf(reflect.TypeOf(fsLayerEntryResponse{})), "Entry state.")),

		endpoint(http.MethodGet, "/v1/layers/{layer_ref}/entries", "Get a layer entry", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema()), queryParam("path", strSchema()), queryParamOpt("max_seq", intSchema())).
			WithResponses(ok(rf(reflect.TypeOf(fsLayerEntryResponse{})), "Entry state.")),

		endpoint(http.MethodGet, "/v1/layers/{layer_ref}/objects", "Read a layer object's bytes", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema()), queryParam("path", strSchema()), queryParamOpt("max_seq", intSchema())).
			WithDescription("Returns raw bytes (application/octet-stream); S3-backed objects redirect (307) to a presigned URL.").
			WithResponses([]openapi.Response{
				{Code: http.StatusOK, Description: "Object bytes.", Schema: fsReadRespRef, MediaType: "application/octet-stream"},
				{Code: http.StatusTemporaryRedirect, Description: "Redirect to the presigned object URL for S3-backed objects."},
			}),

		endpoint(http.MethodPost, "/v1/layers/{layer_ref}/objects", "Upload a layer object (raw body)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(
				pathParam("layer_ref", strSchema()),
				queryParam("path", strSchema()),
				queryParamOpt("size", intSchema()),
				queryParamOpt("base_revision", intSchema()),
				queryParamOpt("mode", intSchema()),
			).
			WithBody(&openapi.SchemaRef{Raw: map[string]any{"type": "string", "format": "binary"}}, "Raw object bytes.", "application/octet-stream").
			WithResponses(ok(rf(reflect.TypeOf(fsLayerEntryResponse{})), "Created entry.")),

		endpoint(http.MethodPut, "/v1/layers/{layer_ref}/objects", "Upload a layer object (PUT alias)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(
				pathParam("layer_ref", strSchema()),
				queryParam("path", strSchema()),
				queryParamOpt("size", intSchema()),
				queryParamOpt("base_revision", intSchema()),
				queryParamOpt("mode", intSchema()),
			).
			WithBody(&openapi.SchemaRef{Raw: map[string]any{"type": "string", "format": "binary"}}, "Raw object bytes.", "application/octet-stream").
			WithResponses(ok(rf(reflect.TypeOf(fsLayerEntryResponse{})), "Created entry.")),

		endpoint(http.MethodGet, "/v1/layers/{layer_ref}/events", "List layer events", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("layer_ref", strSchema()), queryParamOpt("since", intSchema())).
			WithResponses(ok(layerEventsRef, "Event list.")),

		endpoint(http.MethodGet, "/v1/layer-checkpoints/{checkpoint_id}", "Get a layer checkpoint", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("checkpoint_id", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(fsLayerCheckpointResponse{})), "Checkpoint state.")),
	)

	// ------------------------------------------------------------------
	// Data plane: vault management (drive9 key) and vault read
	// (capability token).
	// ------------------------------------------------------------------
	eps = append(eps,
		endpoint(http.MethodGet, "/v1/vault/secrets", "List vault secrets (metadata)", []openapi.DocKind{openapi.DataPlane}).
			WithResponses(ok(secretsListRef, "Secret metadata list.")),

		endpoint(http.MethodPost, "/v1/vault/secrets", "Create a vault secret", []openapi.DocKind{openapi.DataPlane}).
			WithBody(vaultSecretCreateRef, "name, secret_type, fields (plaintext at creation).").
			WithResponses(created(rf(reflect.TypeOf(vault.Secret{})), "Created secret metadata.")),

		endpoint(http.MethodGet, "/v1/vault/secrets/{name}", "Get vault secret metadata", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("name", strSchema())).
			WithResponses(ok(rf(reflect.TypeOf(vault.Secret{})), "Secret metadata (values masked).")),

		endpoint(http.MethodPut, "/v1/vault/secrets/{name}", "Rotate / update a vault secret's fields", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("name", strSchema())).
			WithBody(vaultSecretUpdateRef, "New field values (plaintext).").
			WithResponses(ok(rf(reflect.TypeOf(vault.Secret{})), "Updated secret metadata.")),

		endpoint(http.MethodDelete, "/v1/vault/secrets/{name}", "Delete a vault secret", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("name", strSchema())).
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodGet, "/v1/vault/secrets/{name}/value", "Read all plaintext secret fields (owner)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("name", strSchema()), queryParamOpt("format", strSchema())).
			WithDescription("format=env returns text/plain KEY=value lines; otherwise JSON object of field name → value.").
			WithResponses(ok(readValueRef, "Plaintext field values.")),

		endpoint(http.MethodGet, "/v1/vault/secrets/{name}/value/{field}", "Read one plaintext secret field (owner)", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("name", strSchema()), pathParam("field", strSchema())).
			WithDescription("Returns the raw field bytes as text/plain.").
			WithResponses([]openapi.Response{{Code: http.StatusOK, Description: "Plaintext field bytes.", MediaType: "text/plain"}}),

		endpoint(http.MethodPost, "/v1/vault/tokens", "Issue a legacy vault capability token", []openapi.DocKind{openapi.DataPlane}).
			WithBody(vaultTokenIssueRef, "agent_id, task_id, scope, ttl_seconds.").
			WithResponses(created(tokenIssueRef, "Issued capability token.")),

		endpoint(http.MethodDelete, "/v1/vault/tokens/{id}", "Revoke a vault capability token", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("id", strSchema())).
			WithOptionalBody(vaultRevokeRef, "Optional revocation metadata.").
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodPost, "/v1/vault/grants", "Issue a delegated vault grant token", []openapi.DocKind{openapi.DataPlane}).
			WithBody(vaultGrantIssueRef, "agent, scope, perm, ttl_seconds, label_hint.").
			WithResponses(created(grantIssueRef, "Issued grant token.")),

		endpoint(http.MethodDelete, "/v1/vault/grants/{id}", "Revoke a vault grant", []openapi.DocKind{openapi.DataPlane}).
			WithParams(pathParam("id", strSchema())).
			WithOptionalBody(vaultRevokeRef, "Optional revocation metadata.").
			WithResponses(ok(statusOKRef, "status: ok")),

		endpoint(http.MethodGet, "/v1/vault/audit", "Query vault audit log", []openapi.DocKind{openapi.DataPlane}).
			WithParams(queryParamOpt("secret", strSchema()), queryParamOpt("limit", intSchema())).
			WithResponses(ok(auditEventsRef, "Audit events.")),

		endpoint(http.MethodGet, "/v1/vault/read", "Enumerate vault secrets visible to a capability token", []openapi.DocKind{openapi.DataPlane}).
			WithSecurity(openapi.SecurityVaultCapability).
			WithDescription("Authenticated with a vault capability token (vault_ prefix) or delegated grant token (vt_ prefix).").
			WithResponses(ok(secretsNamesRef, "Visible secret names.")),

		endpoint(http.MethodGet, "/v1/vault/read/{name}", "Read plaintext vault secret fields with a capability token", []openapi.DocKind{openapi.DataPlane}).
			WithSecurity(openapi.SecurityVaultCapability).
			WithParams(pathParam("name", strSchema()), queryParamOpt("format", strSchema())).
			WithDescription("Authenticated with a vault capability token (vault_ prefix) or delegated grant token (vt_ prefix). format=env returns text/plain KEY=value lines; otherwise JSON.").
			WithResponses(ok(readValueRef, "Plaintext field values.")),

		endpoint(http.MethodGet, "/v1/vault/read/{name}/{field}", "Read one plaintext vault secret field with a capability token", []openapi.DocKind{openapi.DataPlane}).
			WithSecurity(openapi.SecurityVaultCapability).
			WithParams(pathParam("name", strSchema()), pathParam("field", strSchema())).
			WithDescription("Authenticated with a vault capability token (vault_ prefix) or delegated grant token (vt_ prefix).").
			WithResponses([]openapi.Response{{Code: http.StatusOK, Description: "Plaintext field bytes.", MediaType: "text/plain"}}),
	)

	return eps
}

// tenantIDHeaderParam is the X-Drive9-Tenant-ID header fallback used by the
// quota endpoint.
func tenantIDHeaderParam() openapi.Parameter {
	return openapi.Parameter{
		In:          "header",
		Name:        "X-Drive9-Tenant-ID",
		Description: "Tenant id fallback when the tenant_id query parameter is absent.",
		Schema:      openapi.String(),
	}
}
