openapi: 3.1.0
info:
  title: Popcast Reporting API
  version: 0.1.0
  description: |
    Reporting layer of The Conscience Stack. Per-block-group rolling
    headline windows sourced from local newspaper RSS, TV station RSS,
    Patch, Substack local publications, Wikipedia pageview surges, and
    GNews (credential-gated). Every response follows the standard
    `{data, meta}` envelope with provenance per ADR-003.

    Data maturity: `synthetic-only` at launch; promotes to `production`
    once ingest has run against the feed registry for 7 days.
  contact:
    name: Motionworks AI
    url: https://docs.mworks.com

servers:
  - url: https://api.mworks.com
    description: Production

tags:
  - name: reporting
    description: Block group reporting signal endpoints

paths:
  /v2/popcast/reporting/{fips}:
    get:
      operationId: getReportingSignal
      summary: Current reporting signal for a block group
      description: Returns the current rolling headline window for one BG.
      tags: [reporting]
      x-motionworks-status: roadmap
      x-motionworks-data-maturity: synthetic-only
      x-credit-cost: 1
      parameters:
        - $ref: '#/components/parameters/FipsParam'
        - name: window
          in: query
          schema: { type: integer, enum: [7, 14, 30], default: 7 }
      responses:
        '200':
          description: Reporting signal
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReportingSignalResponse' }
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/ValidationError'

  /v2/popcast/reporting/{fips}/history:
    get:
      operationId: getReportingHistory
      summary: Historical reporting snapshots
      description: Returns up to 26 weekly snapshots for a block group.
      tags: [reporting]
      x-motionworks-status: roadmap
      x-motionworks-data-maturity: synthetic-only
      x-credit-cost: 3
      parameters:
        - $ref: '#/components/parameters/FipsParam'
        - { name: from, in: query, schema: { type: string, format: date } }
        - { name: to, in: query, schema: { type: string, format: date } }
        - { name: cursor, in: query, schema: { type: string } }
        - { name: limit, in: query, schema: { type: integer, maximum: 26 } }
      responses:
        '200':
          description: History snapshots
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReportingHistoryResponse' }

  /v2/popcast/reporting/batch:
    post:
      operationId: batchReportingSignals
      summary: Batch reporting signal lookup
      description: Up to 100 FIPS per call. One credit per FIPS.
      tags: [reporting]
      x-motionworks-status: roadmap
      x-motionworks-data-maturity: synthetic-only
      x-credit-cost: 100
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ReportingBatchRequest' }
      responses:
        '200':
          description: Batch results
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReportingBatchResponse' }

components:
  parameters:
    FipsParam:
      name: fips
      in: path
      required: true
      schema: { type: string, pattern: '^\d{12}$' }
      description: 12-digit US Census block group GEOID

  schemas:
    Headline:
      type: object
      required: [id, title, source_type, source_name, published_at]
      properties:
        id: { type: string }
        title: { type: string }
        source_type:
          type: string
          enum: [news-api, patch, tv-rss, substack, local-gov, wikipedia, newspaper-rss]
        source_name: { type: string }
        url: { type: string, format: uri }
        published_at: { type: string, format: date-time }
        sentiment: { type: string, enum: [positive, neutral, negative] }
        topics: { type: array, items: { type: string } }

    ReportingPayload:
      type: object
      properties:
        headlines: { type: array, items: { $ref: '#/components/schemas/Headline' } }
        topics: { type: array, items: { type: string } }
        sentiment:
          type: object
          properties:
            positive_pct: { type: number }
            neutral_pct: { type: number }
            negative_pct: { type: number }
        sources:
          type: object
          additionalProperties: { type: integer }

    ReportingSignal:
      type: object
      required: [fips, signal_layer, period_end, window_days, refreshed_at, data_quality, source_breakdown, payload]
      properties:
        fips: { type: string }
        signal_layer: { type: string, enum: [reporting] }
        period_end: { type: string }
        window_days: { type: integer, enum: [7, 14, 30] }
        refreshed_at: { type: string, format: date-time }
        data_quality: { type: string, enum: [high, medium, low] }
        source_breakdown:
          type: object
          additionalProperties: { type: integer }
        payload: { $ref: '#/components/schemas/ReportingPayload' }

    Meta:
      type: object
      properties:
        request_id: { type: string }
        credits_used: { type: integer }
        credits_remaining: { type: integer }
        product: { type: string }
        version: { type: string }
        provenance: { $ref: '#/components/schemas/Provenance' }

    Provenance:
      type: object
      properties:
        source: { type: string }
        source_doc: { type: string, format: uri }
        methodology_version: { type: string }
        data_vintage: { type: string }
        data_freshness: { type: string }
        data_latency_days: { type: integer }
        data_maturity: { type: string }

    ReportingSignalResponse:
      type: object
      properties:
        data: { $ref: '#/components/schemas/ReportingSignal' }
        meta: { $ref: '#/components/schemas/Meta' }

    ReportingHistorySnapshot:
      type: object
      properties:
        fips: { type: string }
        period_start: { type: string }
        period_end: { type: string }
        window_days: { type: integer }
        headline_count: { type: integer }
        topics: { type: array, items: { type: string } }
        sentiment:
          type: object
          properties:
            positive_pct: { type: number }
            neutral_pct: { type: number }
            negative_pct: { type: number }
        data_quality: { type: string, enum: [high, medium, low] }

    ReportingHistoryResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/ReportingHistorySnapshot' }
        pagination:
          type: object
          properties:
            cursor: { type: string, nullable: true }
            has_more: { type: boolean }
            total: { type: integer }
        meta: { $ref: '#/components/schemas/Meta' }

    ReportingBatchRequest:
      type: object
      required: [fips_codes]
      properties:
        fips_codes:
          type: array
          maxItems: 100
          items: { type: string, pattern: '^\d{12}$' }
        window: { type: integer, enum: [7, 14, 30] }

    ReportingBatchResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/ReportingSignal' }
        meta: { $ref: '#/components/schemas/Meta' }

    ApiError:
      type: object
      properties:
        error:
          type: object
          properties:
            code: { type: string }
            message: { type: string }
            status: { type: integer }
            request_id: { type: string }

  responses:
    NotFound:
      description: No data for block group
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ApiError' }
    ValidationError:
      description: Validation error
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ApiError' }
