openapi: 3.1.0
info:
  title: Prompt Universe Engine API
  version: "1.0.0"
  description: >
    universe servisinin PUBLIC HTTP yüzeyi (bkz. queries/engine.md §8,
    queries/TODO.md §8). TÜM /v1 uçları X-API-Key ile korunur. Yanıtlar
    { data, meta } zarfıyla döner; hatalar { error: {code, message, details} }.

servers:
  - url: http://localhost:8094
    description: docker compose (api → 8094:8093)

security:
  - ApiKeyAuth: []

tags:
  - name: universe
  - name: evidence
  - name: system

paths:
  /healthz:
    get:
      tags: [system]
      security: []
      summary: Liveness probe
      responses:
        "200": { description: OK }

  /metrics:
    get:
      tags: [system]
      security: []
      summary: Prometheus metrikleri (§16)
      responses:
        "200":
          description: Prometheus text exposition
          content:
            text/plain: {}

  /v1/me:
    get:
      tags: [system]
      summary: Kimlik doğrulama teşhisi
      responses:
        "200":
          description: Principal
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Envelope" }
        "401": { $ref: "#/components/responses/Error" }

  /v1/evidence/datasets:
    post:
      tags: [evidence]
      summary: Evidence dataset yarat
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateDatasetRequest" }
      responses:
        "201": { $ref: "#/components/responses/Envelope" }
        "400": { $ref: "#/components/responses/Error" }
  /v1/evidence/datasets/{id}:
    get:
      tags: [evidence]
      summary: Evidence dataset getir
      parameters: [{ $ref: "#/components/parameters/PathId" }]
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }
  /v1/evidence/datasets/{id}/items:
    post:
      tags: [evidence]
      summary: Evidence item'ları ingest et
      parameters: [{ $ref: "#/components/parameters/PathId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        "201": { $ref: "#/components/responses/Envelope" }
        "400": { $ref: "#/components/responses/Error" }

  /v1/universes:
    post:
      tags: [universe]
      summary: Yeni universe + build başlat (async pipeline)
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateUniverseRequest" }
      responses:
        "201":
          description: Build yaratıldı (asenkron çalışıyor)
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BuildEnvelope" }
        "400": { $ref: "#/components/responses/Error" }
        "403": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}:
    get:
      tags: [universe]
      summary: Universe + build özetleri
      parameters: [{ $ref: "#/components/parameters/PathId" }]
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/builds/{build_id}/status:
    get:
      tags: [universe]
      summary: Build durumu (§87)
      parameters:
        - { $ref: "#/components/parameters/PathId" }
        - { $ref: "#/components/parameters/PathBuildId" }
      responses:
        "200":
          description: Build durumu
          content:
            application/json:
              schema: { $ref: "#/components/schemas/StatusEnvelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/builds/{build_id}/lock:
    post:
      tags: [universe]
      summary: Build'i kilitle (birincil, build-scoped)
      parameters:
        - { $ref: "#/components/parameters/PathId" }
        - { $ref: "#/components/parameters/PathBuildId" }
      responses:
        "200": { $ref: "#/components/responses/VersionEnvelope" }
        "409": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/builds/{build_id}/review:
    post:
      tags: [universe]
      summary: Build'e review ekle (birincil, build-scoped) — FAZ 15
      parameters:
        - { $ref: "#/components/parameters/PathId" }
        - { $ref: "#/components/parameters/PathBuildId" }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ReviewRequest" }
      responses:
        "202": { $ref: "#/components/responses/Envelope" }
        "422": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/lock:
    post:
      tags: [universe]
      summary: Kilitle (alias — yalnız TEK aktif build varsa)
      parameters: [{ $ref: "#/components/parameters/PathId" }]
      responses:
        "200": { $ref: "#/components/responses/VersionEnvelope" }
        "409": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/review:
    post:
      tags: [universe]
      summary: Review ekle (alias) — FAZ 15
      parameters: [{ $ref: "#/components/parameters/PathId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ReviewRequest" }
      responses:
        "202": { $ref: "#/components/responses/Envelope" }
        "409": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/versions/{version}/manifest:
    get:
      tags: [universe]
      summary: Version'a özel Manifest (§61)
      parameters:
        - { $ref: "#/components/parameters/PathId" }
        - { $ref: "#/components/parameters/PathVersion" }
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/versions/{version}/queries:
    get:
      tags: [universe]
      summary: Version'a özel query listesi
      parameters:
        - { $ref: "#/components/parameters/PathId" }
        - { $ref: "#/components/parameters/PathVersion" }
        - { $ref: "#/components/parameters/QueryIntent" }
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/manifest:
    get:
      tags: [universe]
      summary: En son locked version'ın Manifest'i (alias)
      parameters: [{ $ref: "#/components/parameters/PathId" }]
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/queries:
    get:
      tags: [universe]
      summary: En son locked version'ın query'leri (alias)
      parameters:
        - { $ref: "#/components/parameters/PathId" }
        - { $ref: "#/components/parameters/QueryIntent" }
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/coverage:
    get:
      tags: [universe]
      summary: Intent coverage (§88, alias)
      parameters: [{ $ref: "#/components/parameters/PathId" }]
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/builds/{build_id}/health:
    get:
      tags: [universe]
      summary: Build sağlık/ilerleme özeti (§101-105, opsiyonel)
      parameters:
        - { $ref: "#/components/parameters/PathId" }
        - { $ref: "#/components/parameters/PathBuildId" }
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/builds/{build_id}/evidence-drift:
    get:
      tags: [universe]
      summary: Evidence drift bütünlük kontrolü (opsiyonel)
      parameters:
        - { $ref: "#/components/parameters/PathId" }
        - { $ref: "#/components/parameters/PathBuildId" }
      responses:
        "200": { $ref: "#/components/responses/Envelope" }
        "404": { $ref: "#/components/responses/Error" }

  /v1/universes/{id}/refresh:
    post:
      tags: [universe]
      summary: Yeni build tetikle (scheduled_review / structural_market_change)
      parameters: [{ $ref: "#/components/parameters/PathId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/RefreshRequest" }
      responses:
        "202": { $ref: "#/components/responses/BuildEnvelope" }
        "400": { $ref: "#/components/responses/Error" }

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

  parameters:
    PathId:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }
    PathBuildId:
      name: build_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    PathVersion:
      name: version
      in: path
      required: true
      schema: { type: string, pattern: '^[0-9]+\.[0-9]+\.[0-9]+$' }
    QueryIntent:
      name: intent
      in: query
      required: false
      schema:
        type: string
        enum: [informational, comparison, recommendation, transactional]

  responses:
    Envelope:
      description: Başarılı zarf
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Envelope" }
    BuildEnvelope:
      description: Build zarfı
      content:
        application/json:
          schema: { $ref: "#/components/schemas/BuildEnvelope" }
    VersionEnvelope:
      description: Version zarfı
      content:
        application/json:
          schema: { $ref: "#/components/schemas/VersionEnvelope" }
    Error:
      description: Hata zarfı
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }

  schemas:
    Envelope:
      type: object
      required: [data]
      properties:
        data: {}
        meta: { type: object }

    ErrorEnvelope:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
              enum: [validation_failed, unauthorized, forbidden, not_found, conflict, duplicate, quota_exceeded, rate_limited, unavailable, internal]
            message: { type: string }
            details: { type: object }

    CreateDatasetRequest:
      type: object
      additionalProperties: false
      required: [name]
      properties:
        name: { type: string }
        source_summary: { type: string }

    ControlPlane:
      type: object
      additionalProperties: false
      properties:
        audience_scope: { type: string }
        measurement_mode: { type: string }
        coverage_profile: { type: string }
        intent_policy: { type: string, enum: [balanced, observed_weighted] }
        generation_budget: { type: object }
        auto_lock: { type: boolean }
        evidence_dataset_ids:
          type: array
          items: { type: string, format: uuid }

    ValidationContext:
      type: object
      additionalProperties: false
      required: [brand_name]
      properties:
        brand_name: { type: string }
        aliases:
          type: array
          items: { type: string }
        domains:
          type: array
          items: { type: string }

    CreateUniverseRequest:
      type: object
      additionalProperties: false
      required: [category, market, language, requested_query_count]
      properties:
        category: { type: string }
        market: { type: string }
        language: { type: string }
        measurement_mode: { type: string }
        requested_query_count: { type: integer, minimum: 1 }
        control_plane: { $ref: "#/components/schemas/ControlPlane" }
        validation_context: { $ref: "#/components/schemas/ValidationContext" }

    RefreshRequest:
      type: object
      additionalProperties: false
      required: [refresh_trigger]
      properties:
        refresh_trigger:
          type: string
          enum: [scheduled_review, structural_market_change]

    ReviewRequest:
      type: object
      additionalProperties: false
      required: [review_type]
      properties:
        review_type: { type: string, enum: [taxonomy_edit, note] }
        candidate_id: { type: string, format: uuid }
        taxonomy_node_id: { type: string, format: uuid }
        edit_action: { type: string, enum: [remove, add, merge, change_priority, change_audience] }
        edit_payload: { type: object }
        note: { type: string }

    Build:
      type: object
      properties:
        id: { type: string, format: uuid }
        universe_id: { type: string, format: uuid }
        status: { type: string }
        refresh_trigger: { type: [string, "null"] }
        requested_query_count: { type: integer }
        error: { type: [string, "null"] }
        created_at: { type: string, format: date-time }

    BuildEnvelope:
      type: object
      required: [data]
      properties:
        data: { $ref: "#/components/schemas/Build" }

    Version:
      type: object
      properties:
        version: { type: string }
        build_id: { type: string, format: uuid }
        universe_id: { type: string, format: uuid }
        query_count: { type: integer }
        taxonomy_version: { type: string }
        baseline:
          type: object
          properties:
            compatible_with_previous: { type: [boolean, "null"] }
            reset_required: { type: boolean }
            reason: { type: [string, "null"] }
        locked_at: { type: string, format: date-time }

    VersionEnvelope:
      type: object
      required: [data]
      properties:
        data: { $ref: "#/components/schemas/Version" }

    Status:
      type: object
      properties:
        status: { type: string }
        progress:
          type: object
          properties:
            stage: { type: integer }
            total_stages: { type: integer }
            percentage: { type: integer }
        queries_target: { type: integer }
        queries_selected: { type: integer }
        candidates_generated: { type: integer }

    StatusEnvelope:
      type: object
      required: [data]
      properties:
        data: { $ref: "#/components/schemas/Status" }
