openapi: "3.1.0"
info:
  title: Mockly Management API
  description: |
    REST API for managing Mockly's HTTP, WebSocket, gRPC, GraphQL, TCP, Redis, SMTP, MQTT, SNMP, DNS, AMQP, Kafka, LDAP, IMAP, FTP, Memcached, STOMP, CoAP, and SIP servers at runtime.
    All endpoints are served on the management port (default: **9091**).
  version: "1.0.0"
  license:
    name: MIT

servers:
  - url: http://localhost:9091
    description: Local Mockly instance (default)

tags:
  - name: System
    description: Health, protocol status, and global reset
  - name: HTTP Mocks
    description: Manage HTTP mock definitions
  - name: WebSocket Mocks
    description: Manage WebSocket mock definitions
  - name: gRPC Mocks
    description: Manage gRPC mock definitions
  - name: GraphQL Mocks
    description: Manage GraphQL mock definitions
  - name: TCP Mocks
    description: Manage TCP mock definitions
  - name: Redis Mocks
    description: Manage Redis mock definitions
  - name: SMTP Mocks
    description: Manage SMTP rules and captured email inbox
  - name: MQTT Mocks
    description: Manage MQTT mock definitions and captured messages
  - name: NATS Mocks
    description: Manage NATS mock definitions and captured messages
  - name: SNMP Mocks
    description: Manage SNMP OID mock definitions and outbound TRAP configurations
  - name: DNS Mocks
    description: Manage DNS record mocks
  - name: AMQP Mocks
    description: Manage AMQP publish/consume mocks and captured messages
  - name: Kafka Mocks
    description: Manage Kafka topic mocks and captured messages
  - name: LDAP Mocks
    description: Manage LDAP bind/search mocks
  - name: IMAP Mocks
    description: Manage IMAP mailboxes and messages
  - name: FTP Mocks
    description: Manage FTP files
  - name: Memcached Mocks
    description: Manage Memcached command mocks
  - name: STOMP Mocks
    description: Manage STOMP destination mocks and captured messages
  - name: CoAP Mocks
    description: Manage CoAP request/response mocks
  - name: SIP Mocks
    description: Manage SIP method/URI mocks
  - name: State
    description: Shared key-value state used by state-conditional mocks
  - name: Scenarios
    description: Named sets of mock patches and protocol faults for deterministic testing
  - name: Fault Injection
    description: Protocol-native direct fault injection
  - name: Logs
    description: Request/response log access and streaming
  - name: Call Verification
    description: HTTP mock call counts and wait-for-call helpers
  - name: Webhooks
    description: Outbound webhook attempt history and manual "send now" dispatch

paths:
  # ---------------------------------------------------------------------------
  # System
  # ---------------------------------------------------------------------------
  /api/health:
    get:
      tags: [System]
      operationId: getHealth
      summary: Health check
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok

  /api/protocols:
    get:
      tags: [System]
      operationId: listProtocols
      summary: List active protocol servers and their status
      responses:
        "200":
          description: Array of protocol status objects
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true

  /api/reset:
    post:
      tags: [System]
      operationId: reset
      summary: Reset all mocks, state, scenarios, and call counts to their startup values
      responses:
        "200":
          description: Reset successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  # ---------------------------------------------------------------------------
  # HTTP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/http:
    get:
      tags: [HTTP Mocks]
      operationId: listHTTPMocks
      summary: List all HTTP mocks
      responses:
        "200":
          description: Array of HTTP mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/HTTPMock"
    post:
      tags: [HTTP Mocks]
      operationId: addHTTPMock
      summary: Create an HTTP mock
      description: >-
        body must be a plain string. The {file: ...}/{dir: ...} structured
        forms (see mockly.yaml's "HTML page mocking" feature) are rejected
        with 400 on this endpoint — the management API never resolves a
        caller-supplied filesystem path, by design. Use "mockly add page
        --file/--dir" instead, which reads the file/folder client-side and
        submits its content as a literal body.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HTTPMock"
            examples:
              simple:
                summary: Simple GET mock
                value:
                  id: get-users
                  request:
                    method: GET
                    path: /users
                  response:
                    status: 200
                    headers:
                      Content-Type: application/json
                    body: '[{"id":1,"name":"Alice"}]'
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/http/{id}:
    put:
      tags: [HTTP Mocks]
      operationId: updateHTTPMock
      summary: Replace an HTTP mock
      description: >-
        Like POST /api/mocks/http, body must be a plain string — the
        {file: ...}/{dir: ...} structured forms are rejected with 400.
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HTTPMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      tags: [HTTP Mocks]
      operationId: patchHTTPMock
      summary: Partially update an HTTP mock response
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HTTPResponsePatch"
            examples:
              status:
                summary: Temporarily change status code
                value:
                  status: 503
      responses:
        "200":
          description: Mock patched
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [HTTP Mocks]
      operationId: deleteHTTPMock
      summary: Delete an HTTP mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  # ---------------------------------------------------------------------------
  # WebSocket Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/websocket:
    get:
      tags: [WebSocket Mocks]
      operationId: listWSMocks
      summary: List all WebSocket mocks
      responses:
        "200":
          description: Array of WebSocket mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/WebSocketMock"
    post:
      tags: [WebSocket Mocks]
      operationId: addWSMock
      summary: Create a WebSocket mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebSocketMock"
            examples:
              simple:
                summary: Echo WebSocket mock
                value:
                  id: ws-echo
                  path: /ws/echo
                  on_connect:
                    send: '{"status":"connected"}'
                  on_message:
                    - match: ping
                      respond: pong
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebSocketMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/websocket/{id}:
    put:
      tags: [WebSocket Mocks]
      operationId: updateWSMock
      summary: Replace a WebSocket mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebSocketMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebSocketMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      tags: [WebSocket Mocks]
      operationId: patchWSMock
      summary: Partially update a WebSocket mock on_connect behaviour
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WSOnConnectPatch"
      responses:
        "200":
          description: Mock patched
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebSocketMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [WebSocket Mocks]
      operationId: deleteWSMock
      summary: Delete a WebSocket mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  # ---------------------------------------------------------------------------
  # gRPC Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/grpc:
    get:
      tags: [gRPC Mocks]
      operationId: listGRPCMocks
      summary: List all gRPC mocks
      responses:
        "200":
          description: Array of gRPC mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/GRPCMock"
    post:
      tags: [gRPC Mocks]
      operationId: addGRPCMock
      summary: Create a gRPC mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GRPCMock"
            examples:
              simple:
                summary: SayHello mock
                value:
                  id: say-hello
                  method: SayHello
                  response:
                    message: Hello from Mockly
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GRPCMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/grpc/{id}:
    put:
      tags: [gRPC Mocks]
      operationId: updateGRPCMock
      summary: Replace a gRPC mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GRPCMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GRPCMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      tags: [gRPC Mocks]
      operationId: patchGRPCMock
      summary: Partially update a gRPC mock response
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GRPCResponsePatch"
      responses:
        "200":
          description: Mock patched
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GRPCMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [gRPC Mocks]
      operationId: deleteGRPCMock
      summary: Delete a gRPC mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  # ---------------------------------------------------------------------------
  # GraphQL Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/graphql:
    get:
      tags: [GraphQL Mocks]
      operationId: listGraphQLMocks
      summary: List all GraphQL mocks
      responses:
        "200":
          description: Array of GraphQL mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/GraphQLMock"
    post:
      tags: [GraphQL Mocks]
      operationId: addGraphQLMock
      summary: Create a GraphQL mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GraphQLMock"
            examples:
              simple:
                summary: Query mock
                value:
                  id: gql-users
                  operation_type: query
                  operation_name: GetUsers
                  response:
                    users:
                      - id: "1"
                        name: Alice
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GraphQLMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/graphql/{id}:
    put:
      tags: [GraphQL Mocks]
      operationId: updateGraphQLMock
      summary: Replace a GraphQL mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GraphQLMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GraphQLMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [GraphQL Mocks]
      operationId: deleteGraphQLMock
      summary: Delete a GraphQL mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  # ---------------------------------------------------------------------------
  # TCP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/tcp:
    get:
      tags: [TCP Mocks]
      operationId: listTCPMocks
      summary: List all TCP mocks
      responses:
        "200":
          description: Array of TCP mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/TCPMock"
    post:
      tags: [TCP Mocks]
      operationId: addTCPMock
      summary: Create a TCP mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TCPMock"
            examples:
              simple:
                summary: Respond to PING
                value:
                  id: tcp-ping
                  match: PING
                  response: PONG
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TCPMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/tcp/{id}:
    put:
      tags: [TCP Mocks]
      operationId: updateTCPMock
      summary: Replace a TCP mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TCPMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TCPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [TCP Mocks]
      operationId: deleteTCPMock
      summary: Delete a TCP mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  # ---------------------------------------------------------------------------
  # Redis Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/redis:
    get:
      tags: [Redis Mocks]
      operationId: listRedisMocks
      summary: List all Redis mocks
      responses:
        "200":
          description: Array of Redis mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/RedisMock"
    post:
      tags: [Redis Mocks]
      operationId: addRedisMock
      summary: Create a Redis mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RedisMock"
            examples:
              simple:
                summary: GET key mock
                value:
                  id: redis-get-user
                  command: GET
                  key: "user:*"
                  response:
                    type: bulk
                    value: '{"id":1,"name":"Alice"}'
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RedisMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/redis/{id}:
    put:
      tags: [Redis Mocks]
      operationId: updateRedisMock
      summary: Replace a Redis mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RedisMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RedisMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [Redis Mocks]
      operationId: deleteRedisMock
      summary: Delete a Redis mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  # ---------------------------------------------------------------------------
  # SMTP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/smtp:
    get:
      tags: [SMTP Mocks]
      operationId: listSMTPRules
      summary: List all SMTP rules
      responses:
        "200":
          description: Array of SMTP rules
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SMTPRule"
    post:
      tags: [SMTP Mocks]
      operationId: addSMTPRule
      summary: Create an SMTP rule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SMTPRule"
            examples:
              reject:
                summary: Reject mail from unknown senders
                value:
                  id: smtp-reject-spam
                  from: "re:.*@spam\\.example\\.com"
                  action: reject
                  message: "550 Rejected"
      responses:
        "201":
          description: Rule created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SMTPRule"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/smtp/{id}:
    put:
      tags: [SMTP Mocks]
      operationId: updateSMTPRule
      summary: Replace an SMTP rule
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SMTPRule"
      responses:
        "200":
          description: Rule updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SMTPRule"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [SMTP Mocks]
      operationId: deleteSMTPRule
      summary: Delete an SMTP rule
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/emails:
    get:
      tags: [SMTP Mocks]
      operationId: listEmails
      summary: List all emails captured in the SMTP inbox
      responses:
        "200":
          description: Array of captured emails
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ReceivedEmail"
    delete:
      tags: [SMTP Mocks]
      operationId: clearEmails
      summary: Clear the SMTP inbox
      responses:
        "200":
          description: Inbox cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  # ---------------------------------------------------------------------------
  # Webhooks
  # ---------------------------------------------------------------------------
  /api/webhooks:
    get:
      tags: [Webhooks]
      operationId: listWebhooks
      summary: List the outbound webhook attempt history (oldest first)
      responses:
        "200":
          description: Array of webhook attempt records
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/WebhookRecord"
    delete:
      tags: [Webhooks]
      operationId: clearWebhooks
      summary: Clear the outbound webhook attempt history
      responses:
        "200":
          description: History cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  /api/webhooks/send:
    post:
      tags: [Webhooks]
      operationId: sendWebhook
      summary: Fire a one-off outbound webhook synchronously, independent of any mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookSendRequest"
      responses:
        "200":
          description: The resulting webhook attempt record
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookRecord"
        "400":
          description: Missing or invalid url
        "503":
          description: Webhooks are not enabled

  # ---------------------------------------------------------------------------
  # MQTT Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/mqtt:
    get:
      tags: [MQTT Mocks]
      operationId: listMQTTMocks
      summary: List all MQTT mocks
      responses:
        "200":
          description: Array of MQTT mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MQTTMock"
    post:
      tags: [MQTT Mocks]
      operationId: addMQTTMock
      summary: Create an MQTT mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MQTTMock"
            examples:
              simple:
                summary: Respond to sensor topic
                value:
                  id: mqtt-sensor
                  topic: "sensors/+"
                  response:
                    topic: "sensors/response"
                    payload: '{"ack":true}'
                    qos: 0
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MQTTMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/mqtt/{id}:
    put:
      tags: [MQTT Mocks]
      operationId: updateMQTTMock
      summary: Replace an MQTT mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MQTTMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MQTTMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [MQTT Mocks]
      operationId: deleteMQTTMock
      summary: Delete an MQTT mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/mqtt/messages:
    get:
      tags: [MQTT Mocks]
      operationId: listMQTTMessages
      summary: List all captured MQTT messages
      responses:
        "200":
          description: Array of captured messages
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
    delete:
      tags: [MQTT Mocks]
      operationId: clearMQTTMessages
      summary: Clear captured MQTT messages
      responses:
        "200":
          description: Messages cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  # ---------------------------------------------------------------------------
  # NATS Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/nats:
    get:
      tags: [NATS Mocks]
      operationId: listNATSMocks
      summary: List all NATS mocks
      responses:
        "200":
          description: Array of NATS mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/NATSMock"
    post:
      tags: [NATS Mocks]
      operationId: addNATSMock
      summary: Create a NATS mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NATSMock"
            examples:
              pubsub:
                summary: Pub/sub reaction
                value:
                  id: nats-order-ack
                  subject: "orders.created"
                  response:
                    subject: "orders.ack"
                    payload: '{"received":"{{ .body }}"}'
              request_reply:
                summary: Request/reply (no response.subject needed)
                value:
                  id: nats-ping
                  subject: "svc.ping"
                  response:
                    payload: "pong"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NATSMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/nats/{id}:
    put:
      tags: [NATS Mocks]
      operationId: updateNATSMock
      summary: Replace a NATS mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NATSMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NATSMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [NATS Mocks]
      operationId: deleteNATSMock
      summary: Delete a NATS mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/nats/messages:
    get:
      tags: [NATS Mocks]
      operationId: listNATSMessages
      summary: List all captured NATS messages
      responses:
        "200":
          description: Array of captured messages
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
    delete:
      tags: [NATS Mocks]
      operationId: clearNATSMessages
      summary: Clear captured NATS messages
      responses:
        "200":
          description: Messages cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  # ---------------------------------------------------------------------------
  # SNMP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/snmp:
    get:
      tags: [SNMP Mocks]
      operationId: listSNMPMocks
      summary: List all SNMP OID mocks
      responses:
        "200":
          description: Array of SNMP mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SNMPMock"
    post:
      tags: [SNMP Mocks]
      operationId: addSNMPMock
      summary: Add an SNMP OID mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SNMPMock"
            examples:
              string_oid:
                summary: System description string OID
                value:
                  id: sys-descr
                  oid: "1.3.6.1.2.1.1.1.0"
                  type: string
                  value: "Mockly Virtual Device"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SNMPMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/snmp/{id}:
    put:
      tags: [SNMP Mocks]
      operationId: updateSNMPMock
      summary: Replace an SNMP OID mock
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SNMPMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SNMPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [SNMP Mocks]
      operationId: deleteSNMPMock
      summary: Delete an SNMP OID mock
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Mock deleted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/snmp/traps:
    get:
      tags: [SNMP Mocks]
      operationId: listSNMPTraps
      summary: List configured SNMP traps
      responses:
        "200":
          description: Array of trap configurations
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SNMPTrap"
    post:
      tags: [SNMP Mocks]
      operationId: addSNMPTrap
      summary: Add an SNMP trap configuration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SNMPTrap"
            examples:
              cold_start:
                summary: coldStart trap to localhost
                value:
                  id: cold-start
                  target: "127.0.0.1:1162"
                  version: "2c"
                  community: "public"
                  oid: "1.3.6.1.6.3.1.1.5.1"
                  bindings:
                    - oid: "1.3.6.1.2.1.1.1.0"
                      type: string
                      value: "Device restarted"
      responses:
        "201":
          description: Trap configuration created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SNMPTrap"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/snmp/traps/{id}/send:
    post:
      tags: [SNMP Mocks]
      operationId: sendSNMPTrap
      summary: Send an SNMP trap immediately
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: ID of a configured trap to send
      responses:
        "200":
          description: Trap sent successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  # ---------------------------------------------------------------------------
  # DNS Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/dns:
    get:
      tags: [DNS Mocks]
      operationId: listDNSMocks
      summary: List all DNS mocks
      responses:
        "200":
          description: Array of DNS mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/DNSMock"
    post:
      tags: [DNS Mocks]
      operationId: addDNSMock
      summary: Create a DNS mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DNSMock"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DNSMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/dns/{id}:
    put:
      tags: [DNS Mocks]
      operationId: updateDNSMock
      summary: Replace a DNS mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DNSMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DNSMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [DNS Mocks]
      operationId: deleteDNSMock
      summary: Delete a DNS mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  # ---------------------------------------------------------------------------
  # AMQP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/amqp:
    get:
      tags: [AMQP Mocks]
      operationId: listAMQPMocks
      summary: List all AMQP mocks
      responses:
        "200":
          description: Array of AMQP mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AMQPMock"
    post:
      tags: [AMQP Mocks]
      operationId: addAMQPMock
      summary: Create an AMQP mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AMQPMock"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AMQPMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/amqp/{id}:
    put:
      tags: [AMQP Mocks]
      operationId: updateAMQPMock
      summary: Replace an AMQP mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AMQPMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AMQPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [AMQP Mocks]
      operationId: deleteAMQPMock
      summary: Delete an AMQP mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  /api/amqp/messages:
    get:
      tags: [AMQP Mocks]
      operationId: listAMQPMessages
      summary: List captured AMQP messages
      responses:
        "200":
          description: Array of captured AMQP messages
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ReceivedAMQPMessage"
    delete:
      tags: [AMQP Mocks]
      operationId: clearAMQPMessages
      summary: Clear captured AMQP messages
      responses:
        "200":
          description: Message store cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  # ---------------------------------------------------------------------------
  # Kafka Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/kafka:
    get:
      tags: [Kafka Mocks]
      operationId: listKafkaMocks
      summary: List all Kafka mocks
      responses:
        "200":
          description: Array of Kafka mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/KafkaMock"
    post:
      tags: [Kafka Mocks]
      operationId: addKafkaMock
      summary: Create a Kafka mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KafkaMock"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KafkaMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/kafka/{id}:
    put:
      tags: [Kafka Mocks]
      operationId: updateKafkaMock
      summary: Replace a Kafka mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KafkaMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KafkaMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [Kafka Mocks]
      operationId: deleteKafkaMock
      summary: Delete a Kafka mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  /api/kafka/messages:
    get:
      tags: [Kafka Mocks]
      operationId: listKafkaMessages
      summary: List captured Kafka messages
      responses:
        "200":
          description: Array of captured Kafka messages
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ProducedKafkaMessage"
    delete:
      tags: [Kafka Mocks]
      operationId: clearKafkaMessages
      summary: Clear captured Kafka messages
      responses:
        "200":
          description: Message store cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  # ---------------------------------------------------------------------------
  # LDAP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/ldap:
    get:
      tags: [LDAP Mocks]
      operationId: listLDAPMocks
      summary: List all LDAP mocks
      responses:
        "200":
          description: Array of LDAP mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/LDAPMock"
    post:
      tags: [LDAP Mocks]
      operationId: addLDAPMock
      summary: Create an LDAP mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LDAPMock"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LDAPMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/ldap/{id}:
    put:
      tags: [LDAP Mocks]
      operationId: updateLDAPMock
      summary: Replace an LDAP mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LDAPMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LDAPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [LDAP Mocks]
      operationId: deleteLDAPMock
      summary: Delete an LDAP mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  # ---------------------------------------------------------------------------
  # IMAP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/imap:
    get:
      tags: [IMAP Mocks]
      operationId: listIMAPMailboxes
      summary: List all IMAP mailboxes
      responses:
        "200":
          description: Array of IMAP mailboxes
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/IMAPMailbox"
    post:
      tags: [IMAP Mocks]
      operationId: addIMAPMailbox
      summary: Create an IMAP mailbox
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IMAPMailbox"
      responses:
        "201":
          description: Mailbox created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IMAPMailbox"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/imap/{id}:
    put:
      tags: [IMAP Mocks]
      operationId: updateIMAPMailbox
      summary: Replace an IMAP mailbox
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IMAPMailbox"
      responses:
        "200":
          description: Mailbox updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IMAPMailbox"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [IMAP Mocks]
      operationId: deleteIMAPMailbox
      summary: Delete an IMAP mailbox
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  # ---------------------------------------------------------------------------
  # FTP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/ftp:
    get:
      tags: [FTP Mocks]
      operationId: listFTPFiles
      summary: List all FTP files
      responses:
        "200":
          description: Array of FTP files
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/FTPFile"
    post:
      tags: [FTP Mocks]
      operationId: addFTPFile
      summary: Create an FTP file
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FTPFile"
      responses:
        "201":
          description: File created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FTPFile"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/ftp/{id}:
    put:
      tags: [FTP Mocks]
      operationId: updateFTPFile
      summary: Replace an FTP file
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FTPFile"
      responses:
        "200":
          description: File updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FTPFile"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [FTP Mocks]
      operationId: deleteFTPFile
      summary: Delete an FTP file
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  # ---------------------------------------------------------------------------
  # Memcached Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/memcached:
    get:
      tags: [Memcached Mocks]
      operationId: listMemcachedMocks
      summary: List all Memcached mocks
      responses:
        "200":
          description: Array of Memcached mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MemcachedMock"
    post:
      tags: [Memcached Mocks]
      operationId: addMemcachedMock
      summary: Create a Memcached mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MemcachedMock"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MemcachedMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/memcached/{id}:
    put:
      tags: [Memcached Mocks]
      operationId: updateMemcachedMock
      summary: Replace a Memcached mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MemcachedMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MemcachedMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [Memcached Mocks]
      operationId: deleteMemcachedMock
      summary: Delete a Memcached mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  # ---------------------------------------------------------------------------
  # STOMP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/stomp:
    get:
      tags: [STOMP Mocks]
      operationId: listSTOMPMocks
      summary: List all STOMP mocks
      responses:
        "200":
          description: Array of STOMP mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/STOMPMock"
    post:
      tags: [STOMP Mocks]
      operationId: addSTOMPMock
      summary: Create a STOMP mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/STOMPMock"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/STOMPMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/stomp/{id}:
    put:
      tags: [STOMP Mocks]
      operationId: updateSTOMPMock
      summary: Replace a STOMP mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/STOMPMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/STOMPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [STOMP Mocks]
      operationId: deleteSTOMPMock
      summary: Delete a STOMP mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  /api/stomp/messages:
    get:
      tags: [STOMP Mocks]
      operationId: listSTOMPMessages
      summary: List captured STOMP messages
      responses:
        "200":
          description: Array of captured STOMP messages
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ReceivedSTOMPMessage"
    delete:
      tags: [STOMP Mocks]
      operationId: clearSTOMPMessages
      summary: Clear captured STOMP messages
      responses:
        "200":
          description: Message store cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  # ---------------------------------------------------------------------------
  # CoAP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/coap:
    get:
      tags: [CoAP Mocks]
      operationId: listCoAPMocks
      summary: List all CoAP mocks
      responses:
        "200":
          description: Array of CoAP mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CoAPMock"
    post:
      tags: [CoAP Mocks]
      operationId: addCoAPMock
      summary: Create a CoAP mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CoAPMock"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CoAPMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/coap/{id}:
    put:
      tags: [CoAP Mocks]
      operationId: updateCoAPMock
      summary: Replace a CoAP mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CoAPMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CoAPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [CoAP Mocks]
      operationId: deleteCoAPMock
      summary: Delete a CoAP mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  # ---------------------------------------------------------------------------
  # SIP Mocks
  # ---------------------------------------------------------------------------
  /api/mocks/sip:
    get:
      tags: [SIP Mocks]
      operationId: listSIPMocks
      summary: List all SIP mocks
      responses:
        "200":
          description: Array of SIP mocks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SIPMock"
    post:
      tags: [SIP Mocks]
      operationId: addSIPMock
      summary: Create a SIP mock
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SIPMock"
      responses:
        "201":
          description: Mock created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SIPMock"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/mocks/sip/{id}:
    put:
      tags: [SIP Mocks]
      operationId: updateSIPMock
      summary: Replace a SIP mock
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SIPMock"
      responses:
        "200":
          description: Mock updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SIPMock"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [SIP Mocks]
      operationId: deleteSIPMock
      summary: Delete a SIP mock
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  # ---------------------------------------------------------------------------
  # State
  # ---------------------------------------------------------------------------
  /api/state:
    get:
      tags: [State]
      operationId: getState
      summary: Get all state key-value pairs
      responses:
        "200":
          description: Current state map
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
    post:
      tags: [State]
      operationId: setState
      summary: Set one or more state keys
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
            examples:
              simple:
                summary: Set checkout state
                value:
                  checkout: "paid"
                  user_role: "admin"
      responses:
        "200":
          description: Updated state
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string

  /api/state/{key}:
    delete:
      tags: [State]
      operationId: deleteState
      summary: Delete a state key
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  # ---------------------------------------------------------------------------
  # Scenarios
  # ---------------------------------------------------------------------------
  /api/scenarios:
    get:
      tags: [Scenarios]
      operationId: listScenarios
      summary: List all scenarios
      responses:
        "200":
          description: Array of scenarios
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Scenario"
    post:
      tags: [Scenarios]
      operationId: createScenario
      summary: Create a scenario
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Scenario"
            examples:
              simple:
                summary: Payment failure scenario
                value:
                  id: payment-failure
                  name: Payment Service Down
                  description: Simulates the payment service returning 503
                  patches:
                    - mock_id: post-payment
                      status: 503
                      body: '{"error":"service unavailable"}'
      responses:
        "201":
          description: Scenario created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Scenario"
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/scenarios/active:
    get:
      tags: [Scenarios]
      operationId: listActiveScenarios
      summary: List currently active scenarios
      responses:
        "200":
          description: Active scenario IDs and their full definitions
          content:
            application/json:
              schema:
                type: object
                properties:
                  active:
                    type: array
                    items:
                      type: string
                  scenarios:
                    type: array
                    items:
                      $ref: "#/components/schemas/Scenario"

  /api/scenarios/{id}:
    get:
      tags: [Scenarios]
      operationId: getScenario
      summary: Get a scenario by ID
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          description: Scenario found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Scenario"
        "404":
          $ref: "#/components/responses/NotFound"
    put:
      tags: [Scenarios]
      operationId: updateScenario
      summary: Replace a scenario
      parameters:
        - $ref: "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Scenario"
      responses:
        "200":
          description: Scenario updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Scenario"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [Scenarios]
      operationId: deleteScenario
      summary: Delete a scenario
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          $ref: "#/components/responses/Deleted"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/scenarios/{id}/activate:
    post:
      tags: [Scenarios]
      operationId: activateScenario
      summary: Activate a scenario
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          description: Scenario activated
          content:
            application/json:
              schema:
                type: object
                properties:
                  activated:
                    type: string
                  active:
                    type: array
                    items:
                      type: string
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags: [Scenarios]
      operationId: deactivateScenario
      summary: Deactivate a scenario
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          description: Scenario deactivated
          content:
            application/json:
              schema:
                type: object
                properties:
                  deactivated:
                    type: string
                  active:
                    type: array
                    items:
                      type: string

  /api/scenarios/{id}/deactivate:
    post:
      tags: [Scenarios]
      operationId: deactivateScenarioPost
      summary: Deactivate a scenario (POST alias)
      parameters:
        - $ref: "#/components/parameters/id"
      responses:
        "200":
          description: Scenario deactivated
          content:
            application/json:
              schema:
                type: object
                properties:
                  deactivated:
                    type: string
                  active:
                    type: array
                    items:
                      type: string

  # ---------------------------------------------------------------------------
  # Fault Injection
  # ---------------------------------------------------------------------------
  /api/fault:
    get:
      tags: [Fault Injection]
      operationId: getAllFaults
      summary: Get all active direct protocol faults
      responses:
        "200":
          description: Active direct faults keyed by protocol
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProtocolFaults"
    delete:
      tags: [Fault Injection]
      operationId: clearAllFaults
      summary: Clear all direct protocol faults
      responses:
        "204":
          description: All faults cleared

  /api/fault/{protocol}:
    get:
      tags: [Fault Injection]
      operationId: getProtocolFault
      summary: Get direct fault config for a protocol
      parameters:
        - $ref: "#/components/parameters/protocol"
      responses:
        "200":
          description: Fault config for the selected protocol, or null when not set
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/HTTPFault"
                  - $ref: "#/components/schemas/WebSocketFault"
                  - $ref: "#/components/schemas/GRPCFault"
                  - $ref: "#/components/schemas/TCPFault"
                  - $ref: "#/components/schemas/RedisFault"
                  - $ref: "#/components/schemas/MQTTFault"
                  - $ref: "#/components/schemas/SMTPFault"
                  - $ref: "#/components/schemas/SNMPFault"
                  - $ref: "#/components/schemas/DNSFault"
                  - $ref: "#/components/schemas/AMQPFault"
                  - $ref: "#/components/schemas/KafkaFault"
                  - $ref: "#/components/schemas/LDAPFault"
                  - $ref: "#/components/schemas/IMAPFault"
                  - $ref: "#/components/schemas/FTPFault"
                  - $ref: "#/components/schemas/MemcachedFault"
                  - $ref: "#/components/schemas/STOMPFault"
                  - $ref: "#/components/schemas/CoAPFault"
                  - $ref: "#/components/schemas/SIPFault"
                  - type: "null"
    post:
      tags: [Fault Injection]
      operationId: setProtocolFault
      summary: Set direct fault config for a protocol
      parameters:
        - $ref: "#/components/parameters/protocol"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/HTTPFault"
                - $ref: "#/components/schemas/WebSocketFault"
                - $ref: "#/components/schemas/GRPCFault"
                - $ref: "#/components/schemas/TCPFault"
                - $ref: "#/components/schemas/RedisFault"
                - $ref: "#/components/schemas/MQTTFault"
                - $ref: "#/components/schemas/SMTPFault"
                - $ref: "#/components/schemas/SNMPFault"
                - $ref: "#/components/schemas/DNSFault"
                - $ref: "#/components/schemas/AMQPFault"
                - $ref: "#/components/schemas/KafkaFault"
                - $ref: "#/components/schemas/LDAPFault"
                - $ref: "#/components/schemas/IMAPFault"
                - $ref: "#/components/schemas/FTPFault"
                - $ref: "#/components/schemas/MemcachedFault"
                - $ref: "#/components/schemas/STOMPFault"
                - $ref: "#/components/schemas/CoAPFault"
                - $ref: "#/components/schemas/SIPFault"
      responses:
        "200":
          description: Fault config stored
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"
        "400":
          $ref: "#/components/responses/BadRequest"
    delete:
      tags: [Fault Injection]
      operationId: clearProtocolFault
      summary: Clear direct fault config for a protocol
      parameters:
        - $ref: "#/components/parameters/protocol"
      responses:
        "204":
          description: Fault cleared for protocol

  /api/fault/{protocol}/effective:
    get:
      tags: [Fault Injection]
      operationId: getEffectiveProtocolFault
      summary: Get the effective fault config for a protocol
      description: >-
        Returns the fault that will actually be applied to the next matching
        request for this protocol — the direct fault set via `/api/fault/{protocol}`
        merged with any fault contributed by a currently active scenario.
      parameters:
        - $ref: "#/components/parameters/protocol"
      responses:
        "200":
          description: Effective fault config for the selected protocol, or null when none is active
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/HTTPFault"
                  - $ref: "#/components/schemas/WebSocketFault"
                  - $ref: "#/components/schemas/GRPCFault"
                  - $ref: "#/components/schemas/TCPFault"
                  - $ref: "#/components/schemas/RedisFault"
                  - $ref: "#/components/schemas/MQTTFault"
                  - $ref: "#/components/schemas/SMTPFault"
                  - $ref: "#/components/schemas/SNMPFault"
                  - $ref: "#/components/schemas/DNSFault"
                  - $ref: "#/components/schemas/AMQPFault"
                  - $ref: "#/components/schemas/KafkaFault"
                  - $ref: "#/components/schemas/LDAPFault"
                  - $ref: "#/components/schemas/IMAPFault"
                  - $ref: "#/components/schemas/FTPFault"
                  - $ref: "#/components/schemas/MemcachedFault"
                  - $ref: "#/components/schemas/STOMPFault"
                  - $ref: "#/components/schemas/CoAPFault"
                  - $ref: "#/components/schemas/SIPFault"
                  - type: "null"
        "404":
          description: Unknown protocol

  # ---------------------------------------------------------------------------
  # Logs
  # ---------------------------------------------------------------------------
  /api/logs:
    get:
      tags: [Logs]
      operationId: getLogs
      summary: Get captured request/response logs
      description: Get recent log entries across all protocols. Optionally filter results by matched mock ID.
      parameters:
        - name: matched_id
          in: query
          required: false
          description: Return only log entries captured for the given mock ID.
          schema:
            type: string
      responses:
        "200":
          description: Array of log entries
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
    delete:
      tags: [Logs]
      operationId: clearLogs
      summary: Clear all captured logs
      responses:
        "200":
          description: Logs cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  /api/logs/count:
    get:
      tags: [Logs]
      operationId: countLogs
      summary: Count captured request/response logs
      description: Count recent log entries across all protocols. Optionally filter by matched mock ID.
      parameters:
        - name: matched_id
          in: query
          required: false
          description: Count only log entries captured for the given mock ID.
          schema:
            type: string
      responses:
        "200":
          description: Log entry count
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer
                    format: int64
                required: [count]

  /api/logs/stream:
    get:
      tags: [Logs]
      operationId: streamLogs
      summary: Stream logs as Server-Sent Events
      responses:
        "200":
          description: SSE stream of log entries
          content:
            text/event-stream:
              schema:
                type: string

  # ---------------------------------------------------------------------------
  # Call Verification
  # ---------------------------------------------------------------------------
  /api/calls/http/{mockId}:
    get:
      tags: [Call Verification]
      operationId: getHTTPCalls
      summary: Get call count and details for an HTTP mock
      parameters:
        - name: mockId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Call count information
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
    delete:
      tags: [Call Verification]
      operationId: clearHTTPMockCalls
      summary: Reset call count for a specific HTTP mock
      parameters:
        - name: mockId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Deleted"

  /api/calls/http:
    delete:
      tags: [Call Verification]
      operationId: clearAllHTTPCalls
      summary: Reset call counts for all HTTP mocks
      responses:
        "200":
          description: All call counts cleared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusMessage"

  /api/calls/http/{mockId}/wait:
    post:
      tags: [Call Verification]
      operationId: waitHTTPCalls
      summary: Block until a mock has been called at least N times (or timeout)
      parameters:
        - name: mockId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                count:
                  type: integer
                  description: Minimum number of calls to wait for
                  example: 1
                timeout:
                  type: string
                  description: Max wait duration (Go duration string)
                  example: "5s"
      responses:
        "200":
          description: Call count reached
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "408":
          description: Timeout waiting for calls
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

components:
  parameters:
    id:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Mock or resource ID
    protocol:
      name: protocol
      in: path
      required: true
      schema:
        type: string
        enum: [http, graphql, websocket, grpc, tcp, redis, mqtt, nats, smtp, snmp, dns, amqp, kafka, ldap, imap, ftp, memcached, stomp, coap, sip]
      description: Protocol name

  responses:
    BadRequest:
      description: Invalid request body
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Deleted:
      description: Resource deleted
      content:
        application/json:
          schema:
            type: object
            properties:
              deleted:
                type: string

  schemas:
    # ── Primitives ─────────────────────────────────────────────────────────────
    Duration:
      type: string
      description: Go duration string (e.g. "500ms", "1s", "2m")
      example: "500ms"

    ErrorResponse:
      type: object
      properties:
        error:
          type: string

    StatusMessage:
      type: object
      properties:
        status:
          type: string

    StateCondition:
      type: object
      required: [key, value]
      properties:
        key:
          type: string
          description: State key name
        value:
          type: string
          description: Required state value for this mock to match

    # ── HTTP ──────────────────────────────────────────────────────────────────
    HTTPRequest:
      type: object
      required: [method, path]
      properties:
        method:
          type: string
          enum: [GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS]
          example: GET
        path:
          type: string
          description: URL path (exact or with wildcards)
          example: /users/{id}
        headers:
          type: object
          additionalProperties:
            type: string
          description: Required request headers (exact match)
        body:
          type: string
          description: Required request body (exact or wildcard match)
        query:
          type: object
          additionalProperties:
            type: string
          description: Required query params ("*" for wildcard value)
        body_json:
          type: object
          additionalProperties:
            type: string
          description: Match JSON body fields using dot-notation paths
        body_form:
          type: object
          additionalProperties:
            type: string
          description: >-
            Match fields in an application/x-www-form-urlencoded body (e.g. a
            payment page's card-details post-back). Values support exact
            match, "*" (wildcard), and a "re:" regex prefix. Requires the
            request's Content-Type to declare form-urlencoded encoding.

    HTTPResponse:
      type: object
      required: [status]
      properties:
        status:
          type: integer
          example: 200
        headers:
          type: object
          additionalProperties:
            type: string
        body:
          type: string
          description: Response body (supports Go template variables)
        delay:
          $ref: "#/components/schemas/Duration"

    MockFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        status_override:
          type: integer
        body:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    HTTPMock:
      type: object
      required: [request, response]
      properties:
        id:
          type: string
          description: Unique ID (auto-generated if omitted)
        request:
          $ref: "#/components/schemas/HTTPRequest"
        response:
          $ref: "#/components/schemas/HTTPResponse"
        state:
          $ref: "#/components/schemas/StateCondition"
        sequence:
          type: array
          items:
            $ref: "#/components/schemas/HTTPResponse"
          description: Cycled responses — returned in order on successive calls
        sequence_exhausted:
          type: string
          enum: [loop, hold_last, not_found]
          description: "Behaviour when sequence is exhausted (default: hold_last)"
        fault:
          $ref: "#/components/schemas/MockFault"

    HTTPResponsePatch:
      type: object
      properties:
        status:
          type: integer
        headers:
          type: object
          additionalProperties:
            type: string
        body:
          type: string
        delay:
          $ref: "#/components/schemas/Duration"

    # ── WebSocket ─────────────────────────────────────────────────────────────
    WebSocketAction:
      type: object
      properties:
        send:
          type: string
          description: Message to send
        delay:
          $ref: "#/components/schemas/Duration"

    WebSocketRule:
      type: object
      required: [match]
      properties:
        match:
          type: string
          description: Incoming message to match (exact, "re:…" regex, or "*")
        respond:
          type: string
          description: Message to send back
        close:
          type: boolean
          description: Close connection after responding
        delay:
          $ref: "#/components/schemas/Duration"

    WebSocketMock:
      type: object
      required: [path]
      properties:
        id:
          type: string
        path:
          type: string
          description: WebSocket path to mount (e.g. /ws/chat)
        on_connect:
          $ref: "#/components/schemas/WebSocketAction"
        on_message:
          type: array
          items:
            $ref: "#/components/schemas/WebSocketRule"
        state:
          $ref: "#/components/schemas/StateCondition"

    WSOnConnectPatch:
      type: object
      properties:
        send:
          type: string
        delay:
          $ref: "#/components/schemas/Duration"

    # ── gRPC ──────────────────────────────────────────────────────────────────
    GRPCError:
      type: object
      required: [code, message]
      properties:
        code:
          type: integer
          description: gRPC status code
        message:
          type: string

    GRPCMock:
      type: object
      required: [method]
      properties:
        id:
          type: string
        method:
          type: string
          description: Fully-qualified gRPC method name (e.g. SayHello or pkg.Service/Method)
        response:
          type: object
          additionalProperties: true
          description: Response payload fields
        error:
          $ref: "#/components/schemas/GRPCError"
        delay:
          $ref: "#/components/schemas/Duration"

    GRPCResponsePatch:
      type: object
      properties:
        response:
          type: object
          additionalProperties: true
        error:
          $ref: "#/components/schemas/GRPCError"
        delay:
          $ref: "#/components/schemas/Duration"

    # ── GraphQL ───────────────────────────────────────────────────────────────
    GraphQLError:
      type: object
      required: [message]
      properties:
        message:
          type: string
        path:
          type: array
          items:
            type: string
        extensions:
          type: object
          additionalProperties: true

    GraphQLMock:
      type: object
      properties:
        id:
          type: string
        operation_type:
          type: string
          enum: [query, mutation, subscription]
          description: Operation type filter (omit for any)
        operation_name:
          type: string
          description: Operation name filter (exact or wildcard; omit for any)
        response:
          type: object
          additionalProperties: true
          description: Data fields returned under the "data" key
        errors:
          type: array
          items:
            $ref: "#/components/schemas/GraphQLError"
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    # ── TCP ───────────────────────────────────────────────────────────────────
    TCPMock:
      type: object
      required: [match, response]
      properties:
        id:
          type: string
        match:
          type: string
          description: Exact string, "re:…" regex, or "hex:…" hex bytes
        response:
          type: string
          description: Plain string or "hex:…" hex bytes
        close:
          type: boolean
          description: Close connection after responding
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    # ── Redis ─────────────────────────────────────────────────────────────────
    RedisResponse:
      type: object
      required: [type]
      properties:
        type:
          type: string
          enum: [string, bulk, integer, array, error, nil]
        value:
          description: Response value (type depends on `type` field)

    RedisMock:
      type: object
      required: [command, response]
      properties:
        id:
          type: string
        command:
          type: string
          description: Redis command (e.g. GET, SET, HGET)
          example: GET
        key:
          type: string
          description: Key to match (exact, "re:…" regex, or "*" wildcard)
        response:
          $ref: "#/components/schemas/RedisResponse"
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    # ── SMTP ──────────────────────────────────────────────────────────────────
    SMTPRule:
      type: object
      required: [action]
      properties:
        id:
          type: string
        from:
          type: string
          description: Sender address filter (exact, "re:…", or wildcard)
        to:
          type: string
          description: Recipient filter
        subject:
          type: string
          description: Subject filter
        action:
          type: string
          enum: [accept, reject]
        message:
          type: string
          description: SMTP error message when action is "reject"

    ReceivedEmail:
      type: object
      properties:
        id:
          type: string
        from:
          type: string
        to:
          type: array
          items:
            type: string
        subject:
          type: string
        body:
          type: string
        received_at:
          type: string
          format: date-time

    # ── Webhooks ────────────────────────────────────────────────────────────
    WebhookRecord:
      type: object
      properties:
        id:
          type: string
        mock_id:
          type: string
          description: ID of the mock that triggered this webhook (absent for ad-hoc sends)
        protocol:
          type: string
        timestamp:
          type: string
          format: date-time
        url:
          type: string
        method:
          type: string
        request_body:
          type: string
        status_code:
          type: integer
        response_body:
          type: string
        error:
          type: string
        attempt:
          type: integer
          description: 1-based attempt number (>1 indicates a retry)
        duration_ms:
          type: integer

    WebhookSendRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
        method:
          type: string
          default: POST
        headers:
          type: object
          additionalProperties:
            type: string
        body:
          type: string

    # ── MQTT ──────────────────────────────────────────────────────────────────
    MQTTResponse:
      type: object
      required: [payload]
      properties:
        topic:
          type: string
          description: Publish topic (empty = <incoming>/response)
        payload:
          type: string
        qos:
          type: integer
          minimum: 0
          maximum: 2
        retain:
          type: boolean
        delay:
          $ref: "#/components/schemas/Duration"

    MQTTMock:
      type: object
      required: [topic]
      properties:
        id:
          type: string
        topic:
          type: string
          description: MQTT topic pattern (supports + and # wildcards)
        response:
          $ref: "#/components/schemas/MQTTResponse"
        state:
          $ref: "#/components/schemas/StateCondition"

    # ── NATS ──────────────────────────────────────────────────────────────────
    NATSResponse:
      type: object
      required: [payload]
      properties:
        subject:
          type: string
          description: Publish subject for pub/sub reactions (ignored for request/reply — the caller's reply-to subject is used instead)
        payload:
          type: string
        delay:
          $ref: "#/components/schemas/Duration"

    NATSMock:
      type: object
      required: [subject]
      properties:
        id:
          type: string
        subject:
          type: string
          description: NATS subject pattern (supports * and > wildcards, plus {name} captures)
        queue_group:
          type: string
          description: Join this NATS queue group so only one group member answers each matching message
        response:
          $ref: "#/components/schemas/NATSResponse"
        state:
          $ref: "#/components/schemas/StateCondition"

    # ── DNS / AMQP / Kafka / LDAP / IMAP / FTP / Memcached / STOMP / CoAP / SIP ─
    DNSMock:
      type: object
      required: [name, type]
      properties:
        id:
          type: string
        name:
          type: string
          description: DNS name to match (supports * wildcards)
        type:
          type: string
          enum: [A, AAAA, CNAME, MX, TXT, PTR, SRV, NS]
        records:
          type: array
          items:
            type: string
          description: Record values; MX uses "<priority> <host>", SRV uses "<priority> <weight> <port> <target>"
        ttl:
          type: integer
          minimum: 0
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    AMQPResponse:
      type: object
      required: [body]
      properties:
        exchange:
          type: string
        routing_key:
          type: string
        body:
          type: string

    AMQPMock:
      type: object
      properties:
        id:
          type: string
        exchange:
          type: string
        routing_key:
          type: string
          description: "Routing key filter (exact, wildcard, or re: regex)"
        response:
          $ref: "#/components/schemas/AMQPResponse"
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    ReceivedAMQPMessage:
      type: object
      properties:
        id:
          type: string
        exchange:
          type: string
        routing_key:
          type: string
        body:
          type: string
        timestamp:
          type: string
          format: date-time

    KafkaRecord:
      type: object
      required: [value]
      properties:
        key:
          type: string
        value:
          type: string
        headers:
          type: object
          additionalProperties:
            type: string

    KafkaMock:
      type: object
      required: [topic]
      properties:
        id:
          type: string
        topic:
          type: string
        records:
          type: array
          items:
            $ref: "#/components/schemas/KafkaRecord"
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    ProducedKafkaMessage:
      type: object
      properties:
        id:
          type: string
        topic:
          type: string
        partition:
          type: integer
        key:
          type: string
        value:
          type: string
        timestamp:
          type: string
          format: date-time

    LDAPMock:
      type: object
      required: [base_dn, attributes]
      properties:
        id:
          type: string
        base_dn:
          type: string
        filter:
          type: string
        attributes:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    IMAPMessage:
      type: object
      required: [seq_num, from, to, subject, body]
      properties:
        seq_num:
          type: integer
        uid:
          type: integer
          minimum: 0
        from:
          type: string
        to:
          type: string
        subject:
          type: string
        body:
          type: string
        flags:
          type: array
          items:
            type: string
        date:
          type: string

    IMAPMailbox:
      type: object
      required: [name, messages]
      properties:
        id:
          type: string
        name:
          type: string
        messages:
          type: array
          items:
            $ref: "#/components/schemas/IMAPMessage"

    FTPFile:
      type: object
      required: [path, content]
      properties:
        id:
          type: string
        path:
          type: string
        content:
          type: string
        permissions:
          type: string
        size:
          type: integer

    MemcachedResponse:
      type: object
      properties:
        value:
          type: string
        flags:
          type: integer
          minimum: 0
        status:
          type: string

    MemcachedMock:
      type: object
      required: [command, response]
      properties:
        id:
          type: string
        command:
          type: string
        key:
          type: string
          description: "Key filter (exact, wildcard, or re: regex)"
        response:
          $ref: "#/components/schemas/MemcachedResponse"
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    STOMPResponse:
      type: object
      required: [body]
      properties:
        destination:
          type: string
        body:
          type: string
        content_type:
          type: string
        headers:
          type: object
          additionalProperties:
            type: string

    STOMPMock:
      type: object
      required: [destination]
      properties:
        id:
          type: string
        destination:
          type: string
          description: "Destination filter (exact, wildcard, or re: regex)"
        response:
          $ref: "#/components/schemas/STOMPResponse"
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    ReceivedSTOMPMessage:
      type: object
      properties:
        id:
          type: string
        destination:
          type: string
        body:
          type: string
        headers:
          type: object
          additionalProperties:
            type: string
        timestamp:
          type: string
          format: date-time

    CoAPResponse:
      type: object
      required: [code]
      properties:
        code:
          type: string
        payload:
          type: string
        content_format:
          type: integer

    CoAPMock:
      type: object
      required: [method, path, response]
      properties:
        id:
          type: string
        method:
          type: string
          enum: [GET, POST, PUT, DELETE]
        path:
          type: string
          description: "Path filter (exact, wildcard, or re: regex)"
        response:
          $ref: "#/components/schemas/CoAPResponse"
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    SIPResponse:
      type: object
      required: [status]
      properties:
        status:
          type: integer
        reason:
          type: string
        headers:
          type: object
          additionalProperties:
            type: string
        body:
          type: string

    SIPMock:
      type: object
      required: [method, response]
      properties:
        id:
          type: string
        method:
          type: string
        uri:
          type: string
          description: "URI filter (exact, wildcard, or re: regex)"
        response:
          $ref: "#/components/schemas/SIPResponse"
        delay:
          $ref: "#/components/schemas/Duration"
        state:
          $ref: "#/components/schemas/StateCondition"

    # ── Scenarios ─────────────────────────────────────────────────────────────
    MockPatch:
      type: object
      required: [mock_id]
      properties:
        mock_id:
          type: string
        status:
          type: integer
        headers:
          type: object
          additionalProperties:
            type: string
        body:
          type: string
        delay:
          $ref: "#/components/schemas/Duration"
        disabled:
          type: boolean
          description: If true, the mock is disabled while this scenario is active

    Scenario:
      type: object
      required: [name]
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        patches:
          type: array
          items:
            $ref: "#/components/schemas/MockPatch"
        faults:
          $ref: "#/components/schemas/ProtocolFaults"

    # ── Fault Injection ───────────────────────────────────────────────────────
    DNSFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        rcode:
          type: string
          enum: [NXDOMAIN, SERVFAIL, REFUSED, NOTIMP, FORMERR]
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    GRPCFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        code:
          type: string
          enum: [UNAVAILABLE, NOT_FOUND, DEADLINE_EXCEEDED, PERMISSION_DENIED, RESOURCE_EXHAUSTED, INTERNAL]
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    HTTPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        status:
          type: integer
        body:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    WebSocketFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        close_code:
          type: integer
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    TCPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        response:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    RedisFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        error:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    MQTTFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    NATSFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    SMTPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        code:
          type: integer
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    SNMPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    AMQPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    KafkaFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        error_code:
          type: integer
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    LDAPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        result_code:
          type: integer
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    IMAPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        response:
          type: string
          enum: [NO, BAD, BYE]
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    FTPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        code:
          type: integer
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    MemcachedFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        error_type:
          type: string
          enum: [SERVER_ERROR, CLIENT_ERROR]
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    STOMPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        message:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    CoAPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        code:
          type: string
          enum: ["4.01", "4.03", "4.04", "5.00", "5.03"]
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    SIPFault:
      type: object
      properties:
        delay:
          $ref: "#/components/schemas/Duration"
        status:
          type: integer
        reason:
          type: string
        error_rate:
          type: number
          format: float
          minimum: 0
          maximum: 1

    ProtocolFaults:
      type: object
      description: Active direct or scenario fault configs by protocol
      properties:
        http:
          $ref: "#/components/schemas/HTTPFault"
        graphql:
          $ref: "#/components/schemas/HTTPFault"
        websocket:
          $ref: "#/components/schemas/WebSocketFault"
        grpc:
          $ref: "#/components/schemas/GRPCFault"
        tcp:
          $ref: "#/components/schemas/TCPFault"
        redis:
          $ref: "#/components/schemas/RedisFault"
        mqtt:
          $ref: "#/components/schemas/MQTTFault"
        nats:
          $ref: "#/components/schemas/NATSFault"
        smtp:
          $ref: "#/components/schemas/SMTPFault"
        snmp:
          $ref: "#/components/schemas/SNMPFault"
        dns:
          $ref: "#/components/schemas/DNSFault"
        amqp:
          $ref: "#/components/schemas/AMQPFault"
        kafka:
          $ref: "#/components/schemas/KafkaFault"
        ldap:
          $ref: "#/components/schemas/LDAPFault"
        imap:
          $ref: "#/components/schemas/IMAPFault"
        ftp:
          $ref: "#/components/schemas/FTPFault"
        memcached:
          $ref: "#/components/schemas/MemcachedFault"
        stomp:
          $ref: "#/components/schemas/STOMPFault"
        coap:
          $ref: "#/components/schemas/CoAPFault"
        sip:
          $ref: "#/components/schemas/SIPFault"

    # ── SNMP ──────────────────────────────────────────────────────────────────
    SNMPMock:
      type: object
      required: [id, oid, type, value]
      properties:
        id:
          type: string
          description: Unique identifier for this mock
        oid:
          type: string
          description: OID in dotted-decimal notation (e.g. "1.3.6.1.2.1.1.1.0")
        type:
          type: string
          enum: [string, octetstring, integer, int, gauge32, counter32, counter64, timeticks, ipaddress, objectidentifier, oid]
          description: SNMP data type for this OID
        value:
          description: The value to return for GET requests (can be string, integer, or float)

    SNMPTrapBinding:
      type: object
      required: [oid, type, value]
      properties:
        oid:
          type: string
          description: OID in dotted-decimal notation
        type:
          type: string
          description: SNMP data type (same values as SNMPMock.type)
        value:
          description: Variable binding value

    SNMPTrap:
      type: object
      required: [id, target, oid]
      properties:
        id:
          type: string
          description: Unique identifier for this trap configuration
        target:
          type: string
          description: "Destination host:port for the trap UDP datagram (e.g. \"127.0.0.1:1162\")"
        version:
          type: string
          enum: ["1", "2c", "3"]
          default: "2c"
          description: SNMP version for the outbound trap
        community:
          type: string
          default: "public"
          description: Community string (v1/v2c)
        oid:
          type: string
          description: Trap OID / enterprise OID
        bindings:
          type: array
          items:
            $ref: "#/components/schemas/SNMPTrapBinding"
          description: Variable bindings to include in the trap PDU

    SNMPUser:
      type: object
      required: [username]
      properties:
        username:
          type: string
        auth_protocol:
          type: string
          enum: [md5, sha, sha224, sha256, sha384, sha512]
          default: md5
        auth_passphrase:
          type: string
          format: password
        priv_protocol:
          type: string
          enum: [des, aes, aes192, aes256]
          default: des
        priv_passphrase:
          type: string
          format: password
