openapi: 3.1.0
info:
  title: Popcast Ambient API
  version: 0.1.0
  description: |
    Ambient layer of The Conscience Stack. Current-moment per-FIPS
    environmental context: weather (NOAA), air quality (PurpleAir),
    daylight, mobility (Motionworks digital-twin — currently stubbed),
    calendar context. Refreshed hourly.
  contact: { name: Motionworks AI, url: https://docs.mworks.com }

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

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

paths:
  /v2/popcast/ambient/{fips}:
    get:
      operationId: getAmbientSignal
      summary: Current ambient conditions
      description: Weather + AQI + daylight + mobility + calendar. 3-day forecast.
      tags: [ambient]
      x-motionworks-status: roadmap
      x-motionworks-data-maturity: synthetic-only
      x-credit-cost: 1
      parameters:
        - $ref: '#/components/parameters/FipsParam'
      responses:
        '200':
          description: Ambient signal
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AmbientSignalResponse' }
        '404': { $ref: '#/components/responses/NotFound' }
        '400': { $ref: '#/components/responses/ValidationError' }

  /v2/popcast/ambient/{fips}/history:
    get:
      operationId: getAmbientHistory
      summary: Ambient history rollup
      tags: [ambient]
      x-motionworks-status: roadmap
      x-motionworks-data-maturity: synthetic-only
      x-credit-cost: 3
      parameters:
        - $ref: '#/components/parameters/FipsParam'
        - name: window
          in: query
          schema: { type: integer, enum: [7, 30], default: 7 }
        - { name: cursor, in: query, schema: { type: string } }
        - { name: limit, in: query, schema: { type: integer, maximum: 90 } }
      responses:
        '200':
          description: Ambient history
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AmbientHistoryResponse' }

  /v2/popcast/ambient/batch:
    post:
      operationId: batchAmbientSignals
      summary: Batch ambient lookup
      tags: [ambient]
      x-motionworks-status: roadmap
      x-motionworks-data-maturity: synthetic-only
      x-credit-cost: 100
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AmbientBatchRequest' }
      responses:
        '200':
          description: Batch results
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AmbientBatchResponse' }

components:
  parameters:
    FipsParam:
      name: fips
      in: path
      required: true
      schema: { type: string, pattern: '^\d{12}$' }

  schemas:
    WeatherForecastDay:
      type: object
      properties:
        date: { type: string }
        high_f: { type: number }
        low_f: { type: number }
        condition: { type: string, enum: [clear, partly-cloudy, cloudy, rain, thunderstorm, snow, fog, windy, haze, unknown] }
        precipitation_chance_pct: { type: number }

    WeatherSnapshot:
      type: object
      properties:
        current_temp_f: { type: number }
        condition: { type: string }
        humidity_pct: { type: number }
        wind_mph: { type: number }
        forecast:
          type: array
          items: { $ref: '#/components/schemas/WeatherForecastDay' }
        source: { type: string }

    AirQualitySnapshot:
      type: object
      properties:
        aqi: { type: integer }
        category: { type: string, enum: [good, moderate, unhealthy-sensitive, unhealthy, very-unhealthy, hazardous] }
        pm25_ugm3: { type: number, nullable: true }
        pm10_ugm3: { type: number, nullable: true }
        dominant_pollutant: { type: string, nullable: true }
        source: { type: string }

    DaylightSnapshot:
      type: object
      properties:
        date: { type: string }
        sunrise_local: { type: string }
        sunset_local: { type: string }
        solar_noon_local: { type: string }
        daylight_minutes: { type: integer }
        timezone: { type: string }

    MobilitySnapshot:
      type: object
      description: |
        Motionworks digital-twin snapshot. Indexes are 0-500 where 100
        equals the block group's own historical mean. `source` prefixes
        `motionworks-digital-twin-stub` until the real adapter lands.
      properties:
        pedestrian_index: { type: number }
        vehicular_index: { type: number }
        dwell_index: { type: number }
        inbound_flow_index: { type: number }
        outbound_flow_index: { type: number }
        sample_size: { type: integer }
        source: { type: string }

    CalendarContext:
      type: object
      properties:
        is_federal_holiday: { type: boolean }
        federal_holiday_name: { type: string, nullable: true }
        is_school_day: { type: boolean }
        school_district_name: { type: string, nullable: true }
        is_weekend: { type: boolean }
        day_of_week: { type: string, enum: [mon, tue, wed, thu, fri, sat, sun] }

    AmbientPayload:
      type: object
      properties:
        weather: { $ref: '#/components/schemas/WeatherSnapshot' }
        air_quality: { $ref: '#/components/schemas/AirQualitySnapshot' }
        daylight: { $ref: '#/components/schemas/DaylightSnapshot' }
        mobility: { $ref: '#/components/schemas/MobilitySnapshot' }
        calendar: { $ref: '#/components/schemas/CalendarContext' }

    AmbientSignal:
      type: object
      required: [fips, signal_layer, period_end, refreshed_at, payload]
      properties:
        fips: { type: string }
        signal_layer: { type: string, enum: [ambient] }
        period_end: { type: string }
        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/AmbientPayload' }

    Meta:
      type: object
      properties:
        request_id: { type: string }
        credits_used: { type: integer }
        product: { type: string }
        version: { type: string }

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

    AmbientHistorySnapshot:
      type: object
      properties:
        fips: { type: string }
        period_start: { type: string }
        period_end: { type: string }
        window_days: { type: integer }
        avg_temp_f: { type: number }
        avg_aqi: { type: integer }
        rainy_days: { type: integer }
        avg_pedestrian_index: { type: number }
        avg_vehicular_index: { type: number }
        data_quality: { type: string }

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

    AmbientBatchRequest:
      type: object
      required: [fips_codes]
      properties:
        fips_codes:
          type: array
          maxItems: 100
          items: { type: string, pattern: '^\d{12}$' }

    AmbientBatchResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/AmbientSignal' }
        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: Not found
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ApiError' }
    ValidationError:
      description: Validation error
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ApiError' }
