openapi: 3.1.0
info:
  title: 未境AI Video Processing API
  version: 1.0.0
  description: 使用工作区 API Key 创建视频去字幕和去水印任务。当前 provider 为 Mock，RunningHub 尚未接入。
servers:
  - url: https://api.example.com
    description: 请替换为实际部署域名
security:
  - bearerAuth: []
paths:
  /api/v1/balance:
    get:
      operationId: getBalance
      summary: 查询工作区积分余额
      responses:
        "200":
          description: 余额
          content:
            application/json:
              schema:
                type: object
                properties:
                  wallet: { $ref: "#/components/schemas/Wallet" }
                  points_per_yuan: { type: integer, example: 1000 }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /api/v1/tasks:
    post:
      operationId: createTask
      summary: 上传视频并创建任务
      parameters:
        - in: header
          name: Idempotency-Key
          required: false
          schema: { type: string, maxLength: 255 }
          description: 建议每次逻辑上传使用唯一值，避免重复创建。
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [product_code, file]
              properties:
                product_code:
                  type: string
                  enum: [remove_subtitles, remove_watermark]
                file:
                  type: string
                  format: binary
      responses:
        "202":
          description: 任务已创建并冻结预计积分
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Task" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402":
          description: 积分不足
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "422":
          description: 无法读取媒体时长
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
  /api/v1/tasks/{task_id}:
    get:
      operationId: getTask
      summary: 查询任务状态
      parameters:
        - $ref: "#/components/parameters/TaskID"
      responses:
        "200":
          description: 任务详情
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Task" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
  /api/v1/tasks/{task_id}/download:
    get:
      operationId: downloadTask
      summary: 下载处理结果
      parameters:
        - $ref: "#/components/parameters/TaskID"
      responses:
        "200":
          description: 视频文件
          content:
            application/octet-stream:
              schema: { type: string, format: binary }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: wj_live API Key
  parameters:
    TaskID:
      in: path
      name: task_id
      required: true
      schema: { type: string, format: uuid }
  schemas:
    Wallet:
      type: object
      required: [workspace_id, available_points, held_points]
      properties:
        workspace_id: { type: string, format: uuid }
        available_points: { type: integer, format: int64 }
        held_points: { type: integer, format: int64 }
    Task:
      type: object
      required: [id, product_code, product_name, source, status, billing_status, input_filename, duration_ms, billable_seconds, pricing_mode, price_snapshot, reserved_points, settled_points, provider, output_ready, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        product_code: { type: string }
        product_name: { type: string }
        source: { type: string, enum: [web, api] }
        status: { type: string, enum: [queued, processing, succeeded, failed] }
        billing_status: { type: string, enum: [held, settled, released] }
        input_filename: { type: string }
        duration_ms: { type: integer, format: int64 }
        billable_seconds: { type: integer, format: int64 }
        pricing_mode: { type: string, enum: [per_call, per_duration] }
        price_snapshot: { type: integer, format: int64 }
        reserved_points: { type: integer, format: int64 }
        settled_points: { type: integer, format: int64 }
        provider: { type: string, example: mock }
        output_ready: { type: boolean }
        error_message: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code: { type: string }
            message: { type: string }
  responses:
    Unauthorized:
      description: API Key 缺失、无效或已撤销
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    BadRequest:
      description: 请求参数或上传内容无效
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: 资源不存在或结果尚未生成
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
