openapi: 3.0.3
info:
  title: Holale — nyilvános benchmark API
  description: >-
    Csak olvasható benchmark végpontok, Bearer token hitelesítéssel.
    A token a fiókbeállítások oldalon szerezhető be.
  version: 0.1.0

servers:
  - url: https://holale.hu
    description: Production

paths:
  /api/viz/aggregates:
    get:
      operationId: getVizAggregates
      summary: Viz aggregates by dimension (Phase I.1 + JD.2 G-02)
      description: >-
        Filter semantics for `q`, `tag`, and extended search parameters match `GET /api/search` (same
        `buildSearchCaseWhere` predicate on `Case`). **`dimension`** is required.
        **`totalLossHuf`** is always the global sum of `Amount.estimated_public_loss_huf` over amounts on matching cases.
      security:
        - BearerAuth: []
      parameters:
        - name: dimension
          in: query
          required: true
          description: Aggregate dimension
          schema:
            type: string
            enum:
              - company
              - settlement
              - person
              - case_type
              - time_series
        - name: time_granularity
          in: query
          required: false
          description: Required when `dimension=time_series`
          schema:
            type: string
            enum:
              - month
        - name: q
          in: query
          required: false
          description: Text match on case title, summary, and claim statement
          schema:
            type: string
        - name: tag
          in: query
          required: false
          description: Exact `Tag.label` filter
          schema:
            type: string
        - $ref: "#/components/parameters/SearchFilterCompanyId"
        - $ref: "#/components/parameters/SearchFilterPersonId"
        - $ref: "#/components/parameters/SearchFilterSettlementId"
        - $ref: "#/components/parameters/SearchFilterEventFrom"
        - $ref: "#/components/parameters/SearchFilterEventTo"
        - $ref: "#/components/parameters/SearchFilterSourceCreatedFrom"
        - $ref: "#/components/parameters/SearchFilterSourceCreatedTo"
        - $ref: "#/components/parameters/SearchFilterReviewLifecycle"
        - $ref: "#/components/parameters/SearchFilterReviewQueue"
        - $ref: "#/components/parameters/SearchFilterSourceType"
        - $ref: "#/components/parameters/SearchFilterSourceLegalStatus"
        - $ref: "#/components/parameters/SearchFilterProjectName"
        - $ref: "#/components/parameters/SearchFilterAmountHufMin"
        - $ref: "#/components/parameters/SearchFilterAmountHufMax"
        - $ref: "#/components/parameters/SearchFilterEstRealCostHufMin"
        - $ref: "#/components/parameters/SearchFilterEstRealCostHufMax"
        - $ref: "#/components/parameters/SearchFilterEstPublicLossHufMin"
        - $ref: "#/components/parameters/SearchFilterEstPublicLossHufMax"
      responses:
        "200":
          description: Viz aggregate payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VizAggregatesResponse"
        "400":
          description: Missing or unsupported dimension / bad query
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetail"
        "401":
          description: Missing or invalid bearer token
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetail"

  /api/viz/benchmarks:
    get:
      operationId: getVizBenchmarks
      summary: Viz benchmark scale comparisons (Phase I.2)
      description: >-
        Filter semantics for `q` and `tag` match `GET /api/search` and `GET /api/viz/aggregates`.
        Returns **`totalFilteredLossHuf`**: global sum of estimated public loss for matching cases.
        **`benchmarks`** lists every active **`Benchmark`** row with **`ratio`**.
      security:
        - BearerAuth: []
      parameters:
        - name: q
          in: query
          required: false
          description: Text match on case title, summary, and claim statement
          schema:
            type: string
        - name: tag
          in: query
          required: false
          description: Exact `Tag.label` filter
          schema:
            type: string
        - $ref: "#/components/parameters/SearchFilterCompanyId"
        - $ref: "#/components/parameters/SearchFilterPersonId"
        - $ref: "#/components/parameters/SearchFilterSettlementId"
        - $ref: "#/components/parameters/SearchFilterEventFrom"
        - $ref: "#/components/parameters/SearchFilterEventTo"
        - $ref: "#/components/parameters/SearchFilterSourceCreatedFrom"
        - $ref: "#/components/parameters/SearchFilterSourceCreatedTo"
        - $ref: "#/components/parameters/SearchFilterReviewLifecycle"
        - $ref: "#/components/parameters/SearchFilterReviewQueue"
        - $ref: "#/components/parameters/SearchFilterSourceType"
        - $ref: "#/components/parameters/SearchFilterSourceLegalStatus"
        - $ref: "#/components/parameters/SearchFilterProjectName"
        - $ref: "#/components/parameters/SearchFilterAmountHufMin"
        - $ref: "#/components/parameters/SearchFilterAmountHufMax"
        - $ref: "#/components/parameters/SearchFilterEstRealCostHufMin"
        - $ref: "#/components/parameters/SearchFilterEstRealCostHufMax"
        - $ref: "#/components/parameters/SearchFilterEstPublicLossHufMin"
        - $ref: "#/components/parameters/SearchFilterEstPublicLossHufMax"
      responses:
        "200":
          description: Filtered loss total and active benchmarks with ratios
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VizBenchmarksResponse"
        "401":
          description: Missing or invalid bearer token
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetail"

  /api/viz/benchmarks/from-amount:
    get:
      operationId: getVizBenchmarksFromAmount
      summary: Viz benchmark comparisons for a supplied HUF amount (S.46)
      description: >-
        Returns active **`Benchmark`** rows with **`ratio = amountHuf / costHuf`** for a caller-supplied
        **`amountHuf`** query parameter. No search filters.
      security:
        - BearerAuth: []
      parameters:
        - name: amountHuf
          in: query
          required: true
          description: Non-negative HUF amount as a decimal string
          schema:
            type: string
            minLength: 1
      responses:
        "200":
          description: Supplied amount and active benchmarks with ratios
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VizBenchmarksFromAmountResponse"
        "400":
          description: Missing or invalid amountHuf
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetail"
        "401":
          description: Missing or invalid bearer token
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetail"

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        API token issued on the Account page. Prefix `holale_`. Pass as
        `Authorization: Bearer <token>`.

  parameters:
    SearchFilterCompanyId:
      name: company_id
      in: query
      required: false
      description: Filter by linked organization id
      schema:
        type: string
    SearchFilterPersonId:
      name: person_id
      in: query
      required: false
      description: Filter by linked person id
      schema:
        type: string
    SearchFilterSettlementId:
      name: settlement_id
      in: query
      required: false
      description: Filter by linked settlement id
      schema:
        type: string
    SearchFilterEventFrom:
      name: event_from
      in: query
      required: false
      schema:
        type: string
        pattern: "^\\d{4}-\\d{2}-\\d{2}$"
    SearchFilterEventTo:
      name: event_to
      in: query
      required: false
      schema:
        type: string
        pattern: "^\\d{4}-\\d{2}-\\d{2}$"
    SearchFilterSourceCreatedFrom:
      name: source_created_from
      in: query
      required: false
      schema:
        type: string
        pattern: "^\\d{4}-\\d{2}-\\d{2}$"
    SearchFilterSourceCreatedTo:
      name: source_created_to
      in: query
      required: false
      schema:
        type: string
        pattern: "^\\d{4}-\\d{2}-\\d{2}$"
    SearchFilterReviewLifecycle:
      name: review_lifecycle
      in: query
      required: false
      schema:
        type: string
        enum:
          - pending_review
          - published
          - changes_requested
          - held
          - rejected
    SearchFilterReviewQueue:
      name: review_queue
      in: query
      required: false
      schema:
        type: string
        enum:
          - unassigned
          - mandatory
          - standard
          - sampling
    SearchFilterSourceType:
      name: source_type
      in: query
      required: false
      description: article, official_document, or user_submission
      schema:
        type: string
        enum:
          - article
          - official_document
          - user_submission
    SearchFilterSourceLegalStatus:
      name: source_legal_status
      in: query
      required: false
      schema:
        type: string
        enum:
          - active
          - under_review
    SearchFilterProjectName:
      name: project_name
      in: query
      required: false
      schema:
        type: string
    SearchFilterAmountHufMin:
      name: amount_huf_min
      in: query
      required: false
      schema:
        type: string
    SearchFilterAmountHufMax:
      name: amount_huf_max
      in: query
      required: false
      schema:
        type: string
    SearchFilterEstRealCostHufMin:
      name: estimated_real_cost_huf_min
      in: query
      required: false
      schema:
        type: string
    SearchFilterEstRealCostHufMax:
      name: estimated_real_cost_huf_max
      in: query
      required: false
      schema:
        type: string
    SearchFilterEstPublicLossHufMin:
      name: estimated_public_loss_huf_min
      in: query
      required: false
      schema:
        type: string
    SearchFilterEstPublicLossHufMax:
      name: estimated_public_loss_huf_max
      in: query
      required: false
      schema:
        type: string

  schemas:
    ProblemDetail:
      type: object
      required:
        - title
        - detail
      properties:
        title:
          type: string
        detail:
          type: string
        code:
          type: string
        requestId:
          type: string
        instance:
          type: string
        typeSlug:
          type: string

    VizCompanyAggregateItem:
      type: object
      additionalProperties: false
      required:
        - id
        - label
        - lossHuf
      properties:
        id:
          type: string
          minLength: 1
        label:
          type: string
          minLength: 1
        lossHuf:
          type: string
          minLength: 1

    VizBenchmarkItem:
      type: object
      additionalProperties: false
      required:
        - slug
        - labelHu
        - unit
        - costHuf
      properties:
        slug:
          type: string
          minLength: 1
        labelHu:
          type: string
        labelEn:
          type: string
          nullable: true
        unit:
          type: string
        costHuf:
          type: string
          minLength: 1
        sourceUrl:
          type: string
          nullable: true
        ratio:
          type: string
          minLength: 1
        ratioUnavailableReason:
          type: string
          enum:
            - BENCHMARK_COST_ZERO

    VizBenchmarksResponse:
      type: object
      additionalProperties: false
      required:
        - totalFilteredLossHuf
        - benchmarks
      properties:
        totalFilteredLossHuf:
          type: string
          minLength: 1
        benchmarks:
          type: array
          items:
            $ref: "#/components/schemas/VizBenchmarkItem"

    VizBenchmarksFromAmountResponse:
      type: object
      additionalProperties: false
      required:
        - amountHuf
        - benchmarks
      properties:
        amountHuf:
          type: string
          minLength: 1
        benchmarks:
          type: array
          items:
            $ref: "#/components/schemas/VizBenchmarkItem"

    VizAggregatesResponse:
      type: object
      additionalProperties: false
      required:
        - dimension
        - items
        - totalLossHuf
      properties:
        dimension:
          type: string
          enum:
            - company
            - settlement
            - person
            - case_type
            - time_series
        items:
          type: array
          items:
            $ref: "#/components/schemas/VizCompanyAggregateItem"
        totalLossHuf:
          type: string
          minLength: 1