{
  "openapi": "3.1.0",
  "info": {
    "title": "Airport IATA finder",
    "version": "2.0.0",
    "description": "Map filenames / short strings to IATA airport codes. Exact aliases (city, airport name, keywords, nicknames) first, then typo-tolerant matching, then bare IATA codes as a last resort."
  },
  "servers": [{ "url": "/airports" }],
  "security": [{ "ApiKey": [] }, { "Bearer": [] }],
  "paths": {
    "/health": {
      "get": {
        "summary": "Health and index size",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Health" } } }
          }
        }
      }
    },
    "/api/v1/extract": {
      "post": {
        "summary": "Extract the IATA code from one string",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ExtractIn" },
              "example": { "text": "flying_into_charle_de_gaul_next_week.jpg" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result (iata_code is null when nothing matched)",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtractOut" } } }
          },
          "401": { "description": "Missing / invalid API key" },
          "422": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidationError" } } } },
          "429": { "description": "Rate limited (see Retry-After)" }
        }
      }
    },
    "/api/v1/extract/batch": {
      "post": {
        "summary": "Extract IATA codes from many strings (max 20000)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BatchIn" },
              "example": { "texts": ["heathrow_terminal5.jpg", "IMG_2041_barcelona.jpg", "wedding_party.jpg"] }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Results in input order",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchOut" } } }
          },
          "401": { "description": "Missing / invalid API key" },
          "413": { "description": "Too many texts / body too large" },
          "422": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidationError" } } } },
          "429": { "description": "Rate limited (see Retry-After)" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": { "type": "apiKey", "in": "header", "name": "X-API-Key" },
      "Bearer": { "type": "http", "scheme": "bearer" }
    },
    "schemas": {
      "Health": {
        "type": "object",
        "properties": { "status": { "type": "string" }, "airports": { "type": "integer" }, "aliases": { "type": "integer" } }
      },
      "ExtractIn": {
        "type": "object", "required": ["text"],
        "properties": { "text": { "type": "string", "minLength": 1, "maxLength": 500 } }
      },
      "BatchIn": {
        "type": "object", "required": ["texts"],
        "properties": { "texts": { "type": "array", "maxItems": 20000, "items": { "type": "string" } } }
      },
      "Candidate": {
        "type": "object",
        "properties": {
          "iata": { "type": "string", "example": "CDG" },
          "confidence": { "type": "number", "example": 0.78 },
          "match_type": { "type": "string", "enum": ["manual", "name", "city", "keyword", "token", "fuzzy", "code"] },
          "matched_phrase": { "type": "string" },
          "alias": { "type": "string" },
          "airport": { "type": "string" }
        }
      },
      "Result": {
        "type": "object",
        "properties": {
          "text": { "type": "string" },
          "iata_code": { "type": ["string", "null"], "example": "CDG" },
          "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
          "match_type": { "type": ["string", "null"] },
          "match": { "type": ["string", "null"], "description": "airport name" },
          "matched_phrase": { "type": ["string", "null"] },
          "candidates": { "type": "array", "maxItems": 3, "items": { "$ref": "#/components/schemas/Candidate" } }
        }
      },
      "Timing": {
        "type": "object",
        "properties": {
          "parse": { "type": "number" }, "lookup": { "type": "number" },
          "processing": { "type": "number" }, "lookup_avg": { "type": "number" }
        }
      },
      "ExtractOut": {
        "allOf": [{ "$ref": "#/components/schemas/Result" },
                  { "type": "object", "properties": { "timing_ms": { "$ref": "#/components/schemas/Timing" } } }]
      },
      "BatchOut": {
        "type": "object",
        "properties": {
          "count": { "type": "integer" },
          "matched": { "type": "integer" },
          "timing_ms": { "$ref": "#/components/schemas/Timing" },
          "results": {
            "type": "array",
            "items": { "allOf": [{ "$ref": "#/components/schemas/Result" },
                                 { "type": "object", "properties": { "lookup_ms": { "type": "number" } } }] }
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "properties": { "detail": { "type": "array", "items": { "type": "object" } } }
      }
    }
  }
}
