openapi: 3.1.0
info:
  title: csv-doctor
  version: 1.0.0
  description: |
    Проверка и очистка CSV. Файлы разбираются в памяти и не сохраняются.
    Тело запроса передаётся как есть (не multipart), максимум 5 МиБ.
  contact: {name: Ewsir, url: "https://github.com/ewsir247"}
servers:
  - url: http://localhost:8116
paths:
  /api/analyze:
    post:
      summary: Профиль колонок и список проблем
      parameters:
        - {name: encoding, in: query, schema: {type: string, enum: [utf-8, utf-8-sig, cp1251, koi8-r, utf-16]}, description: Если не задана, определяется автоматически}
        - {name: delimiter, in: query, schema: {type: string, enum: [comma, semicolon, tab, pipe]}, description: Если не задан, определяется автоматически}
        - {name: filename, in: query, schema: {type: string, maxLength: 80}, description: Имя файла, только для эха в ответе}
      requestBody:
        required: true
        content:
          text/csv: {schema: {type: string, format: binary}}
          application/octet-stream: {schema: {type: string, format: binary}}
      responses:
        "200":
          description: Отчёт
          content:
            application/json:
              schema: {$ref: "#/components/schemas/Report"}
        "413": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
        "429": {$ref: "#/components/responses/Error"}
  /api/clean:
    post:
      summary: Очищенный CSV (UTF-8)
      description: |
        Обрезает пробелы, чинит невидимые символы и кракозябры, приводит числа (1 234,5 -> 1234.5)
        и даты (-> ГГГГ-ММ-ДД), ставит апостроф перед ячейками-формулами (= + - @), выравнивает число полей.
      parameters:
        - {name: encoding, in: query, schema: {type: string, enum: [utf-8, utf-8-sig, cp1251, koi8-r, utf-16]}}
        - {name: delimiter, in: query, schema: {type: string, enum: [comma, semicolon, tab, pipe]}}
        - {name: out_delimiter, in: query, schema: {type: string, enum: [comma, semicolon, tab, pipe]}, description: Разделитель результата, по умолчанию запятая}
        - {name: dedupe, in: query, schema: {type: boolean, default: false}, description: Убрать полностью одинаковые строки}
        - {name: na, in: query, schema: {type: boolean, default: false}, description: "Заглушки (N/A, null, -) заменить пустыми ячейками"}
        - {name: bom, in: query, schema: {type: boolean, default: false}, description: Добавить BOM (для Excel)}
      requestBody:
        required: true
        content:
          text/csv: {schema: {type: string, format: binary}}
      responses:
        "200":
          description: Очищенный файл
          content:
            text/csv: {schema: {type: string}}
        "413": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
        "429": {$ref: "#/components/responses/Error"}
  /api/demo:
    get:
      summary: Демо-файл (cp1251, разделитель ;), помечен «демо»
      responses:
        "200":
          description: CSV
          content:
            text/csv: {schema: {type: string}}
  /healthz:
    get:
      summary: Проверка живости
      responses:
        "200": {description: ok}
components:
  responses:
    Error:
      description: Ошибка
      content:
        application/json:
          schema: {$ref: "#/components/schemas/Error"}
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties: {code: {type: string}, message: {type: string}}
    Issue:
      type: object
      properties:
        code: {type: string, example: decimal_comma}
        severity: {type: string, enum: [error, warning, info]}
        message: {type: string}
        hint: {type: string}
        column: {type: [string, "null"]}
        count: {type: integer}
        rows: {type: array, items: {type: integer}, description: Номера строк файла (не больше пяти)}
        examples: {type: array, items: {type: string}}
        fixable: {type: boolean, description: Исправляется /api/clean}
    Report:
      type: object
      properties:
        filename: {type: [string, "null"]}
        encoding: {type: string}
        delimiter: {type: string, enum: [comma, semicolon, tab, pipe]}
        bytes: {type: integer}
        rows: {type: integer}
        columns: {type: integer}
        summary:
          type: object
          properties: {errors: {type: integer}, warnings: {type: integer}, infos: {type: integer}}
        profile:
          type: array
          items:
            type: object
            properties:
              index: {type: integer}
              name: {type: string}
              type: {type: string, enum: [integer, number, date, boolean, text, empty]}
              non_empty: {type: integer}
              missing: {type: integer}
              placeholders: {type: integer}
              unique: {type: integer}
              examples: {type: array, items: {type: string}}
              stats: {type: [object, "null"]}
              outliers: {type: [object, "null"]}
              date_formats: {type: [object, "null"]}
        issues:
          type: array
          items: {$ref: "#/components/schemas/Issue"}
        preview:
          type: object
          properties: {header: {type: array, items: {type: string}}, rows: {type: array, items: {type: array, items: {type: string}}}}
