{
  "openapi": "3.1.0",
  "info": {
    "title": "Noctalia OCR Engine API",
    "description": "Motor de OCR de alto rendimiento desarrollado en Rust utilizando Arquitectura Hexagonal, Axum y modelos PP-OCRv6 Small ONNX optimizados para CPU en VPS.",
    "version": "1.0.0",
    "contact": {
      "name": "Noctalia AI Team",
      "url": "https://github.com"
    }
  },
  "servers": [
    {
      "url": "/",
      "description": "Servidor actual"
    }
  ],
  "tags": [
    {
      "name": "OCR",
      "description": "Operaciones de extracción de texto y detección de caracteres"
    },
    {
      "name": "System",
      "description": "Estado y monitoreo del servicio"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": ["System"],
        "summary": "Health check del servicio",
        "description": "Retorna el estado de operatividad del motor OCR y la versión activa.",
        "responses": {
          "200": {
            "description": "Servicio saludable y listo para recibir peticiones",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                },
                "example": {
                  "status": "healthy",
                  "service": "ocr-engine-hexagonal",
                  "version": "0.1.0"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ocr": {
      "post": {
        "tags": ["OCR"],
        "summary": "Extraer texto desde archivo (Multipart Form)",
        "description": "Sube una imagen (PNG, JPEG, WebP, BMP) mediante `multipart/form-data` con el campo `image` o `file`. El motor procesa detección DBNet, corrección de perspectiva por PCA y reconocimiento SVTR-LCNet.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Archivo de imagen a procesar"
                  }
                },
                "required": ["image"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracción completada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OcrResponse"
                },
                "example": {
                  "full_text": "Paracetamol 500mg\nTomar 1 cada 8 horas",
                  "block_count": 2,
                  "duration_ms": 142,
                  "blocks": [
                    {
                      "text": "Paracetamol 500mg",
                      "confidence": 0.9854,
                      "bbox": [
                        [12.0, 34.0],
                        [210.0, 36.0],
                        [209.0, 68.0],
                        [11.0, 66.0]
                      ]
                    },
                    {
                      "text": "Tomar 1 cada 8 horas",
                      "confidence": 0.9621,
                      "bbox": [
                        [14.0, 75.0],
                        [245.0, 78.0],
                        [244.0, 105.0],
                        [13.0, 102.0]
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Cuerpo vacío o error al decodificar la imagen",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Fallo interno en el motor de inferencia ONNX",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ocr/raw": {
      "post": {
        "tags": ["OCR"],
        "summary": "Extraer texto desde bytes crudos (Binary Body)",
        "description": "Envía los bytes binarios de la imagen directamente en el cuerpo HTTP con cabecera `Content-Type: application/octet-stream` o `image/png` / `image/jpeg`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracción completada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OcrResponse"
                }
              }
            }
          },
          "400": {
            "description": "Payload vacío o datos de imagen inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error del motor ONNX",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "healthy"
          },
          "service": {
            "type": "string",
            "example": "ocr-engine-hexagonal"
          },
          "version": {
            "type": "string",
            "example": "0.1.0"
          }
        },
        "required": ["status", "service", "version"]
      },
      "OcrResponse": {
        "type": "object",
        "properties": {
          "full_text": {
            "type": "string",
            "description": "Texto unificado respetando saltos de línea y orden de lectura",
            "example": "Texto extraído completo..."
          },
          "block_count": {
            "type": "integer",
            "description": "Cantidad de bloques o líneas de texto detectadas",
            "example": 15
          },
          "duration_ms": {
            "type": "integer",
            "description": "Tiempo total de inferencia y postprocesado en milisegundos",
            "example": 240
          },
          "blocks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OcrBlock"
            }
          }
        },
        "required": ["full_text", "block_count", "duration_ms", "blocks"]
      },
      "OcrBlock": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "description": "Texto decodificado para este bloque",
            "example": "Total: $128.50"
          },
          "confidence": {
            "type": "number",
            "format": "float",
            "description": "Puntaje de confianza promedio (0.0 a 1.0)",
            "example": 0.984
          },
          "bbox": {
            "type": "array",
            "description": "Polígono orientado de 4 vértices [[x1, y1], [x2, y2], [x3, y3], [x4, y4]]",
            "items": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "float"
              },
              "minItems": 2,
              "maxItems": 2
            },
            "minItems": 4,
            "maxItems": 4,
            "example": [
              [100.0, 200.0],
              [350.0, 202.0],
              [349.5, 235.0],
              [99.5, 233.0]
            ]
          }
        },
        "required": ["text", "confidence", "bbox"]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "Error decoding image: invalid image format"
          },
          "status": {
            "type": "integer",
            "example": 400
          }
        },
        "required": ["error", "status"]
      }
    }
  }
}
