openapi: 3.1.0
info:
  # Do not change the title, if the title changes, the import paths will be broken
  title: Api
  version: 0.1.0
  description: ALEMA Insights Agent API
servers:
  - url: /api
    description: Base API path
tags:
  - name: health
    description: Health operations
  - name: auth
    description: Authentication and current user
  - name: agents
    description: AI agent profiles with system instructions
  - name: editorials
    description: Editorial categories used to classify insights
  - name: sources
    description: News sources (RSS, HTML pages, PDFs)
  - name: insights
    description: AI-generated content suggestions (cards)
  - name: stats
    description: Dashboard summary metrics
paths:
  /healthz:
    get:
      operationId: healthCheck
      tags: [ health ]
      summary: Health check
      responses:
        "200":
          description: Healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthStatus"

  /users:
    get:
      operationId: listUsers
      tags: [ users ]
      summary: Listar todos os usuários (Admin apenas)
      responses:
        "200":
          description: Lista de usuários
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/AuthUser" }
    post:
      operationId: createUser
      tags: [ users ]
      summary: Criar novo usuário
      requestBody:
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateUserBody" }
      responses:
        "201":
          description: Criado

  /users/{id}:
    put:
      operationId: updateUser
      summary: Atualiza um usuário existente
      tags: [ users ]
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateUserBody"
      responses:
        "200":
          description: Usuário atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthUser"

  # ---- Auth ----
  /auth/login:
    post:
      operationId: login
      tags: [ auth ]
      summary: Authenticate with username and password
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginBody"
      responses:
        "200":
          description: Authenticated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthResponse"
        "401":
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /auth/logout:
    post:
      operationId: logout
      tags: [ auth ]
      summary: End the current session
      responses:
        "204":
          description: Logged out
  /auth/me:
    get:
      operationId: getCurrentUser
      tags: [ auth ]
      summary: Get the currently authenticated user
      responses:
        "200":
          description: Current user
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthResponse"
        "401":
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
    put:
      operationId: updateCurrentUser
      tags: [ auth ]
      summary: Atualiza nome e/ou senha do usuário atual
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string, minLength: 1 }
                newPassword: { type: string, minLength: 6 }
      responses:
        "200":
          description: Perfil atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthResponse"
        "401":
          description: Not authenticated

  # ---- Agents ----
  /agents:
    get:
      operationId: listAgents
      tags: [ agents ]
      summary: List all agents
      responses:
        "200":
          description: List of agents
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Agent" }
    post:
      operationId: createAgent
      tags: [ agents ]
      summary: Create an agent
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateAgentBody" }
      responses:
        "201":
          description: Agent created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Agent" }
  /agents/{id}:
    get:
      operationId: getAgent
      tags: [ agents ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "200":
          description: Agent
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Agent" }
        "404":
          description: Not found
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ApiError" }
    put:
      operationId: updateAgent
      tags: [ agents ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/UpdateAgentBody" }
      responses:
        "200":
          description: Updated agent
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Agent" }
        "404":
          description: Not found
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ApiError" }
    delete:
      operationId: deleteAgent
      tags: [ agents ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "204": { description: Deleted }

  # ---- Editorials ----
  /editorials:
    get:
      operationId: listEditorials
      tags: [ editorials ]
      responses:
        "200":
          description: List of editorials
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Editorial" }
    post:
      operationId: createEditorial
      tags: [ editorials ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateEditorialBody" }
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Editorial" }
  /editorials/{id}:
    get:
      operationId: getEditorial
      tags: [ editorials ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "200":
          description: Editorial
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Editorial" }
        "404":
          description: Not found
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ApiError" }
    put:
      operationId: updateEditorial
      tags: [ editorials ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/UpdateEditorialBody" }
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Editorial" }
    delete:
      operationId: deleteEditorial
      tags: [ editorials ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "204": { description: Deleted }

  # ---- Sources ----
  /sources:
    get:
      operationId: listSources
      tags: [ sources ]
      responses:
        "200":
          description: List of sources
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Source" }
    post:
      operationId: createSource
      tags: [ sources ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateSourceBody" }
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Source" }
  /sources/{id}:
    get:
      operationId: getSource
      tags: [ sources ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "200":
          description: Source
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Source" }
        "404":
          description: Not found
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ApiError" }
    put:
      operationId: updateSource
      tags: [ sources ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/UpdateSourceBody" }
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Source" }
    delete:
      operationId: deleteSource
      tags: [ sources ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "204": { description: Deleted }

  # ---- Insights ----
  /insights:
    get:
      operationId: listInsights
      tags: [ insights ]
      summary: List insights with optional filters
      parameters:
        - name: editorial
          in: query
          required: false
          schema: { type: string }
          description: Filter cards by editoria name (matches inside processed_insight JSONB)
        - name: from
          in: query
          required: false
          schema: { type: string, format: date-time }
          description: ISO datetime, lower bound for created_at
        - name: to
          in: query
          required: false
          schema: { type: string, format: date-time }
          description: ISO datetime, upper bound for created_at
        - name: agentId
          in: query
          required: false
          schema: { type: integer }
      responses:
        "200":
          description: List of insights
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Insight" }
  /insights/generate:
    post:
      operationId: generateInsight
      tags: [ insights ]
      summary: Run scraping and Gemini generation, persist a new insight
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/GenerateInsightBody" }
      responses:
        "201":
          description: Generated insight
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Insight" }
        "400":
          description: Invalid input or no sources
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ApiError" }
  /insights/{id}:
    get:
      operationId: getInsight
      tags: [ insights ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "200":
          description: Insight
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Insight" }
        "404":
          description: Not found
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ApiError" }
    delete:
      operationId: deleteInsight
      tags: [ insights ]
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "204": { description: Deleted }

  # ---- Stats ----
  /stats/dashboard:
    get:
      operationId: getDashboardStats
      tags: [ stats ]
      summary: Aggregate metrics for the dashboard
      responses:
        "200":
          description: Dashboard metrics
          content:
            application/json:
              schema: { $ref: "#/components/schemas/DashboardStats" }
  /stats/editorial-breakdown:
    get:
      operationId: getEditorialBreakdown
      tags: [ stats ]
      summary: Count of generated cards per editoria
      responses:
        "200":
          description: Editorial breakdown
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/EditorialBreakdownItem" }

components:
  schemas:
    HealthStatus:
      type: object
      properties:
        status: { type: string }
      required: [ status ]

    ApiError:
      type: object
      properties:
        error: { type: string }
      required: [ error ]

    AuthUser:
      type: object
      properties:
        id: { type: integer }
        username: { type: string }
        name: { type: string }
        role: { type: string, enum: [ admin, editor ] }
      required: [ id, username, name ]

    CreateUserBody:
      type: object
      properties:
        username: { type: string, minLength: 1 }
        password: { type: string, minLength: 6 }
        name: { type: string, minLength: 1 }
        role: { type: string, enum: [ admin, editor ] }
      required: [ username, password, name, role ]

    UpdateUserBody:
      type: object
      properties:
        name: { type: string, minLength: 1 }
        password: { type: string }
        role: { type: string, enum: [ admin, editor ] }
      required: [ name, role ]

    LoginBody:
      type: object
      properties:
        username: { type: string, minLength: 1 }
        password: { type: string, minLength: 1 }
      required: [ username, password ]

    AuthResponse:
      type: object
      properties:
        user:
          $ref: "#/components/schemas/AuthUser"
      required: [ user ]

    Agent:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        systemInstruction: { type: string }
        isActive: { type: boolean }
        createdAt: { type: string, format: date-time }
      required: [ id, name, systemInstruction, isActive, createdAt ]
    CreateAgentBody:
      type: object
      properties:
        name: { type: string, minLength: 1 }
        systemInstruction: { type: string, minLength: 1 }
        isActive: { type: boolean }
      required: [ name, systemInstruction ]
    UpdateAgentBody:
      type: object
      properties:
        name: { type: string, minLength: 1 }
        systemInstruction: { type: string, minLength: 1 }
        isActive: { type: boolean }

    Editorial:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        description: { type: string }
        isActive: { type: boolean }
        createdAt: { type: string, format: date-time }
      required: [ id, name, description, isActive, createdAt ]
    CreateEditorialBody:
      type: object
      properties:
        name: { type: string, minLength: 1 }
        description: { type: string }
        isActive: { type: boolean }
      required: [ name ]
    UpdateEditorialBody:
      type: object
      properties:
        name: { type: string, minLength: 1 }
        description: { type: string }
        isActive: { type: boolean }

    Source:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        url: { type: string }
        type:
          type: string
          enum: [ rss, html, pdf ]
        isActive: { type: boolean }
        createdAt: { type: string, format: date-time }
      required: [ id, name, url, type, isActive, createdAt ]
    CreateSourceBody:
      type: object
      properties:
        name: { type: string, minLength: 1 }
        url: { type: string, minLength: 1 }
        type:
          type: string
          enum: [ rss, html, pdf ]
        isActive: { type: boolean }
      required: [ name, url, type ]
    UpdateSourceBody:
      type: object
      properties:
        name: { type: string, minLength: 1 }
        url: { type: string, minLength: 1 }
        type:
          type: string
          enum: [ rss, html, pdf ]
        isActive: { type: boolean }

    InsightCard:
      type: object
      properties:
        titulo: { type: string }
        editoria: { type: string }
        copy: { type: string }
        visual: { type: string }
        sourceUrl: { type: string }
        publishDate: { type: string }
        aiModel: { type: string }
      required: [ titulo, editoria, copy, visual ]
    InsightProcessed:
      type: object
      properties:
        cards:
          type: array
          items: { $ref: "#/components/schemas/InsightCard" }
        sourcesUsed:
          type: array
          items: { type: string }
      required: [ cards, sourcesUsed ]
    Insight:
      type: object
      properties:
        id: { type: integer }
        agentId: { type: integer }
        agentName: { type: string }
        timeframeHours: { type: integer }
        rawContent: { type: string }
        processedInsight: { $ref: "#/components/schemas/InsightProcessed" }
        createdAt: { type: string, format: date-time }
      required: [ id, agentId, agentName, timeframeHours, rawContent, processedInsight, createdAt ]
    GenerateInsightBody:
      type: object
      properties:
        agentId: { type: integer }
        timeframeHours:
          type: integer
          enum: [ 24, 48, 168 ]
      required: [ agentId, timeframeHours ]

    DashboardStats:
      type: object
      properties:
        totalInsights: { type: integer }
        totalCards: { type: integer }
        activeAgents: { type: integer }
        activeSources: { type: integer }
        activeEditorials: { type: integer }
        insightsLast24h: { type: integer }
      required:
        - totalInsights
        - totalCards
        - activeAgents
        - activeSources
        - activeEditorials
        - insightsLast24h
    EditorialBreakdownItem:
      type: object
      properties:
        editorial: { type: string }
        count: { type: integer }
      required: [ editorial, count ]