openapi: 3.1.0
info:
  title: GPS Tracking API
  version: 1.0.0
  description: |
    API REST para integrar datos de flota — posiciones, eventos, mantenimiento
    y conductores — en cualquier sistema externo.

    **Token demo:** usa `demo_gps2024` para probar sin cuenta real.
  contact:
    name: GPS Tracking Developers
    url: https://developers.gpssoftwarenumberone.com
servers:
  - url: https://developers.gpssoftwarenumberone.com/api
    description: Produccion

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: Tu API Key (gps_live_...) o tu user_api_hash de la plataforma GPS. Usa 'demo_gps2024' para datos de prueba.

  schemas:
    Error:
      type: object
      properties:
        error:   { type: string, example: "not_found" }
        message: { type: string, example: "Dispositivo 999 no encontrado." }
        docs:    { type: string, format: uri }

    Device:
      type: object
      properties:
        id:         { type: integer, example: 1001 }
        name:       { type: string,  example: "Camión Bogotá-01" }
        status:     { type: string,  enum: [online, offline, moving] }
        protocol:   { type: string,  example: "gt06", nullable: true }
        lat:        { type: number,  format: float, example: 4.7110, nullable: true }
        lng:        { type: number,  format: float, example: -74.0721, nullable: true }
        speed:      { type: number,  example: 68, nullable: true }
        course:     { type: number,  example: 45, nullable: true }
        altitude:   { type: number,  example: 2600, nullable: true }
        last_seen:  { type: string,  format: date-time, nullable: true }
        driver:     { type: string,  nullable: true }
        address:    { type: string,  nullable: true }
        group:      { type: string,  nullable: true }
        sensors:
          type: array
          items:
            type: object
            properties:
              type:  { type: string }
              name:  { type: string }
              value: {}

    Location:
      type: object
      properties:
        object:     { type: string, example: "location" }
        device_id:  { type: integer }
        lat:        { type: number }
        lng:        { type: number }
        speed:      { type: number, nullable: true }
        course:     { type: number, nullable: true }
        altitude:   { type: number, nullable: true }
        status:     { type: string }
        address:    { type: string, nullable: true }
        last_seen:  { type: string, format: date-time, nullable: true }

    Geofence:
      type: object
      properties:
        id:         { type: integer, example: 10 }
        name:       { type: string,  example: "Zona Bogotá Centro" }
        type:       { type: string,  enum: [polygon, circle, polyline] }
        area:       { type: string,  nullable: true }
        active:     { type: boolean }
        color:      { type: string,  example: "#0066ff", nullable: true }
        created_at: { type: string,  nullable: true }

    Event:
      type: object
      properties:
        id:        { type: integer }
        type:      { type: string,  example: "overspeed" }
        message:   { type: string,  example: "Exceso de velocidad (95 km/h)", nullable: true }
        device_id: { type: integer, nullable: true }
        device:    { type: string,  nullable: true }
        lat:       { type: number,  nullable: true }
        lng:       { type: number,  nullable: true }
        timestamp: { type: string,  format: date-time, nullable: true }

    Position:
      type: object
      properties:
        time:     { type: string, format: date-time }
        lat:      { type: number }
        lng:      { type: number }
        speed:    { type: number, nullable: true }
        course:   { type: number, nullable: true }
        address:  { type: string, nullable: true }
        distance: { type: number, nullable: true }

    HistorySummary:
      type: object
      properties:
        object:        { type: string, example: "history" }
        device_id:     { type: integer }
        device_name:   { type: string, nullable: true }
        distance_km:   { type: number, nullable: true }
        top_speed:     { type: number, nullable: true }
        move_duration: { type: string, nullable: true }
        stop_duration: { type: string, nullable: true }
        positions:
          type: array
          items:
            $ref: "#/components/schemas/Position"

    Service:
      type: object
      properties:
        id:           { type: integer, example: 1 }
        device_id:    { type: integer }
        device_name:  { type: string, nullable: true }
        name:         { type: string, example: "Cambio de aceite" }
        expiration_by: { type: string, enum: [odometer, engine_hours, days] }
        interval:     { type: number, example: 5000 }
        last_service: { type: number, nullable: true }
        expires_at:   { type: number, nullable: true }
        expires_date: { type: string, nullable: true }
        remaining:
          type: number
          nullable: true
          description: "Negativo si ya vencio"
        expired:      { type: boolean }
        description:  { type: string, nullable: true }
        notify_email: { type: string, nullable: true }

    Driver:
      type: object
      properties:
        id:                 { type: integer, example: 1 }
        name:               { type: string,  example: "Carlos Gómez" }
        phone:              { type: string,  nullable: true }
        email:              { type: string,  nullable: true }
        rfid:               { type: string,  nullable: true }
        assigned_device_id: { type: integer, nullable: true }
        assigned_device:    { type: string,  nullable: true }

    Alert:
      type: object
      properties:
        id:           { type: integer }
        name:         { type: string, example: "Exceso de velocidad" }
        type:         { type: string, example: "overspeed" }
        active:       { type: boolean }
        devices:      { type: array, items: { type: integer } }
        notify_email: { type: string, nullable: true }
        notify_sms:   { type: string, nullable: true }

    ApiKey:
      type: object
      properties:
        id:             { type: string, format: uuid }
        key:            { type: string, example: "gps_live_a1b2c3d4..." }
        label:          { type: string, nullable: true }
        created_at:     { type: integer, description: "Unix timestamp ms" }
        expires_at:     { type: integer, nullable: true }
        platform_email: { type: string }
        message:        { type: string }

paths:

  /health:
    get:
      summary: Estado del servicio
      description: Verifica que la API esta operacional. No requiere autenticacion.
      security: []
      responses:
        "200":
          description: Servicio operacional
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:  { type: string, example: "ok" }
                  service: { type: string, example: "gps-tracking-api" }
                  version: { type: string, example: "1.0.0" }

  /v1/keys:
    post:
      summary: Generar API Key
      description: Crea una nueva API Key vinculada a tu cuenta. Requiere tu user_api_hash en el body. La key solo se muestra completa una vez.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [platform_hash]
              properties:
                platform_hash:   { type: string, description: "Tu user_api_hash de la plataforma GPS" }
                label:           { type: string, description: "Nombre descriptivo (opcional)" }
                expires_in_days: { type: integer, default: 365, description: "Dias de vigencia (1-3650)" }
      responses:
        "201":
          description: API Key creada
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ApiKey" }
        "401":
          description: platform_hash invalido

    get:
      summary: Listar API Keys
      description: Lista todas las keys de tu cuenta. Autenticar con tu platform_hash en Bearer. Los valores de keys se muestran truncados.
      responses:
        "200":
          description: Lista de keys
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  count:  { type: integer }
                  data:   { type: array, items: { type: object } }

  /v1/keys/{id}:
    delete:
      summary: Revocar API Key
      description: Revoca una key de forma inmediata e irreversible. Autenticar con tu platform_hash en Bearer.
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: Key revocada
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:  { type: string }
                  id:      { type: string }
                  revoked: { type: boolean }
        "404":
          description: Key no encontrada o no pertenece a tu cuenta

  /v1/devices:
    get:
      summary: Listar dispositivos
      description: Retorna todos los dispositivos de la flota con su posicion y estado actuales.
      parameters:
        - name: page
          in: query
          schema: { type: integer, default: 1 }
        - name: limit
          in: query
          schema: { type: integer, default: 100 }
      responses:
        "200":
          description: Lista de dispositivos
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string, example: "list" }
                  count:  { type: integer }
                  data:   { type: array, items: { $ref: "#/components/schemas/Device" } }
        "401": { description: No autenticado }

  /v1/devices/{id}:
    get:
      summary: Detalle de un dispositivo
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Dispositivo encontrado
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  data:   { $ref: "#/components/schemas/Device" }
        "404": { description: No encontrado }

  /v1/devices/{id}/location:
    get:
      summary: Ultima posicion de un dispositivo
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Posicion actual
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Location" }
        "404": { description: Dispositivo sin posicion registrada }

  /v1/history/{device_id}:
    get:
      summary: Historial de recorrido
      description: Retorna el recorrido de un dispositivo en un rango de fechas con resumen de kilometros, velocidad maxima y duraciones.
      parameters:
        - name: device_id
          in: path
          required: true
          schema: { type: integer }
        - name: from
          in: query
          required: true
          schema: { type: string, example: "2026-06-01" }
          description: Fecha inicio YYYY-MM-DD
        - name: to
          in: query
          required: true
          schema: { type: string, example: "2026-06-01" }
          description: Fecha fin YYYY-MM-DD
      responses:
        "200":
          description: Historial del dispositivo
          content:
            application/json:
              schema: { $ref: "#/components/schemas/HistorySummary" }
        "400": { description: Parametros from/to requeridos }

  /v1/geofences:
    get:
      summary: Listar geocercas
      responses:
        "200":
          description: Lista de geocercas
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  count:  { type: integer }
                  data:   { type: array, items: { $ref: "#/components/schemas/Geofence" } }

  /v1/geofences/{id}:
    get:
      summary: Detalle de una geocerca
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Geocerca encontrada
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  data:   { $ref: "#/components/schemas/Geofence" }
        "404": { description: No encontrada }

  /v1/events:
    get:
      summary: Eventos de la flota
      description: Retorna eventos recientes (overspeed, geofence_in/out, ignition_on/off, sos, etc.)
      parameters:
        - name: device_id
          in: query
          schema: { type: integer }
          description: Filtrar por dispositivo (opcional)
        - name: limit
          in: query
          schema: { type: integer, default: 50 }
      responses:
        "200":
          description: Lista de eventos
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  count:  { type: integer }
                  data:   { type: array, items: { $ref: "#/components/schemas/Event" } }

  /v1/alerts:
    get:
      summary: Alertas configuradas
      description: Lista todas las alertas activas e inactivas con sus dispositivos asignados.
      responses:
        "200":
          description: Lista de alertas
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  count:  { type: integer }
                  data:   { type: array, items: { $ref: "#/components/schemas/Alert" } }

  /v1/services:
    get:
      summary: Servicios de mantenimiento
      description: Lista recordatorios de mantenimiento por odometro, horas de motor o dias. Integra con tu CMMS o ERP.
      parameters:
        - name: device_id
          in: query
          schema: { type: integer }
          description: Filtrar por dispositivo (opcional)
      responses:
        "200":
          description: Lista de servicios
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  count:  { type: integer }
                  data:   { type: array, items: { $ref: "#/components/schemas/Service" } }

  /v1/services/{id}:
    get:
      summary: Detalle de un servicio
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Servicio encontrado
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  data:   { $ref: "#/components/schemas/Service" }
        "404": { description: No encontrado }

  /v1/drivers:
    get:
      summary: Conductores
      description: Lista todos los conductores registrados con su vehiculo asignado actualmente.
      responses:
        "200":
          description: Lista de conductores
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  count:  { type: integer }
                  data:   { type: array, items: { $ref: "#/components/schemas/Driver" } }

  /v1/drivers/{id}:
    get:
      summary: Detalle de un conductor
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Conductor encontrado
          content:
            application/json:
              schema:
                type: object
                properties:
                  object: { type: string }
                  data:   { $ref: "#/components/schemas/Driver" }
        "404": { description: No encontrado }
