{
  "openapi": "3.1.0",
  "info": {
    "title": "Puente móvil de seguros de Banco Plata",
    "version": "1.0.0",
    "description": "Contrato de referencia para la autenticación con Firebase, el inicio confiable de sesiones M2M, el envío de cotizaciones mediante Cloudflare Worker y la consulta de resultados privados de aseguradoras."
  },
  "tags": [
    { "name": "Autenticación Firebase", "description": "Intercambios de tokens con Firebase Identity Toolkit." },
    { "name": "Puente de sesiones", "description": "Functions de Firebase que crean y reparan sesiones independientes del cliente." },
    { "name": "API del Worker", "description": "Rutas del Worker de Cloudflare autenticadas con el token opaco de sesión de API." }
  ],
  "paths": {
    "/v1/accounts:signInWithPassword": {
      "post": {
        "tags": ["Autenticación Firebase"],
        "summary": "Autenticar al usuario dedicado de integración",
        "description": "Solo entre servidores. Intercambia el correo y la contraseña de integración por un token de ID de Firebase de corta duración.",
        "servers": [{ "url": "https://identitytoolkit.googleapis.com" }],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "description": "Clave pública de API web de Firebase para el proyecto bancoplata-88155.",
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PasswordSignInRequest" },
              "example": {
                "email": "integration@example.com",
                "password": "server-held-password",
                "returnSecureToken": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Usuario de integración autenticado.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FirebasePasswordSession" } } }
          },
          "400": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/sessionBootstrap": {
      "post": {
        "tags": ["Puente de sesiones"],
        "summary": "Crear las sesiones del cliente en Firebase y el Worker",
        "description": "Operación del backend bancario confiable. El token bearer debe pertenecer al UID de integración configurado.",
        "servers": [{ "url": "https://us-central1-bancoplata-88155.cloudfunctions.net" }],
        "security": [{ "integrationFirebaseBearer": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SessionBootstrapRequest" },
              "example": { "endUserId": "bank-user-123", "expiresInSeconds": 300 }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credenciales independientes de Firebase y del Worker creadas.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SessionBootstrapResponse" } } }
          },
          "400": { "$ref": "#/components/responses/ErrorResponse" },
          "401": { "$ref": "#/components/responses/ErrorResponse" },
          "403": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/refreshApiSession": {
      "post": {
        "tags": ["Puente de sesiones"],
        "summary": "Reemplazar una sesión vencida de API del Worker",
        "description": "Usa el token de ID de Firebase activo del usuario final para crear un token opaco de reemplazo para el Worker.",
        "servers": [{ "url": "https://us-central1-bancoplata-88155.cloudfunctions.net" }],
        "security": [{ "endUserFirebaseBearer": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SessionRefreshRequest" },
              "example": { "expiresInSeconds": 300 }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credencial de reemplazo del Worker creada.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiSessionResponse" } } }
          },
          "400": { "$ref": "#/components/responses/ErrorResponse" },
          "401": { "$ref": "#/components/responses/M2mRequiredResponse" },
          "403": { "$ref": "#/components/responses/M2mRequiredResponse" }
        }
      }
    },
    "/session/probe": {
      "get": {
        "tags": ["API del Worker"],
        "summary": "Validar la sesión opaca del Worker",
        "servers": [{ "url": "/worker", "description": "Proxy de demostración al Worker en el mismo origen" }],
        "security": [{ "workerApiBearer": [] }],
        "responses": {
          "200": {
            "description": "La sesión en KV y la autorización respaldada por Firebase están activas.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProbeResponse" } } }
          },
          "401": { "$ref": "#/components/responses/WorkerUnauthorizedResponse" }
        }
      }
    },
    "/quote": {
      "post": {
        "tags": ["API del Worker"],
        "summary": "Iniciar una cotización de auto",
        "servers": [{ "url": "/worker", "description": "Proxy de demostración al Worker en el mismo origen" }],
        "security": [{ "workerApiBearer": [] }],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Identificador único de la solicitud. Debe reutilizarse únicamente al reintentar la misma cotización.",
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/QuoteRequest" },
              "example": { "VehiculoID": "01010100304", "Modelo": "2022", "CP": "64000", "Edad": 18, "Sexo": 2 }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Cotización aceptada.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuoteAcceptedResponse" } } }
          },
          "400": { "$ref": "#/components/responses/ErrorResponse" },
          "401": { "$ref": "#/components/responses/WorkerUnauthorizedResponse" }
        }
      }
    },
    "/quote/{resultId}": {
      "get": {
        "tags": ["API del Worker"],
        "summary": "Consultar un resultado privado de aseguradora",
        "description": "El Worker determina la propiedad a partir de la sesión de API autenticada; nunca acepta el UID enviado por el cliente.",
        "servers": [{ "url": "/worker", "description": "Proxy de demostración al Worker en el mismo origen" }],
        "security": [{ "workerApiBearer": [] }],
        "parameters": [
          {
            "name": "resultId",
            "in": "path",
            "required": true,
            "description": "ID del registro de aseguradora referenciado por el mapa global de aseguradoras en RTDB.",
            "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{1,128}$" },
            "example": "global-id-axa"
          }
        ],
        "responses": {
          "200": {
            "description": "JSON privado de la aseguradora leído desde R2.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InsurerResult" } } }
          },
          "400": { "$ref": "#/components/responses/ErrorResponse" },
          "401": { "$ref": "#/components/responses/WorkerUnauthorizedResponse" },
          "404": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "integrationFirebaseBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Token de ID de Firebase",
        "description": "Token de ID de Firebase para el único UID dedicado de integración. Solo debe usarse en el servidor."
      },
      "endUserFirebaseBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Token de ID de Firebase",
        "description": "Token de ID de Firebase del usuario final que contiene los claims de cotización y emisión."
      },
      "workerApiBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Token aleatorio opaco",
        "description": "Token opaco de API del Worker devuelto al iniciar la sesión y almacenado únicamente en la memoria del cliente."
      }
    },
    "schemas": {
      "PasswordSignInRequest": {
        "type": "object",
        "required": ["email", "password", "returnSecureToken"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "password": { "type": "string", "format": "password" },
          "returnSecureToken": { "type": "boolean", "const": true }
        }
      },
      "FirebasePasswordSession": {
        "type": "object",
        "required": ["idToken", "refreshToken", "expiresIn", "localId"],
        "properties": {
          "idToken": { "type": "string", "description": "Token de ID de Firebase de corta duración del usuario de integración." },
          "refreshToken": { "type": "string" },
          "expiresIn": { "type": "string", "example": "3600" },
          "localId": { "type": "string", "example": "integration-user" }
        }
      },
      "SessionBootstrapRequest": {
        "type": "object",
        "required": ["endUserId"],
        "properties": {
          "endUserId": { "type": "string", "minLength": 1, "maxLength": 128 },
          "expiresInSeconds": { "type": "integer", "minimum": 120, "example": 300 }
        }
      },
      "SessionBootstrapResponse": {
        "type": "object",
        "required": ["firebaseCustomToken", "apiSessionToken", "firebaseExpiresAt", "apiExpiresAt"],
        "properties": {
          "firebaseCustomToken": { "type": "string", "description": "Token de un solo uso que el cliente intercambia con Firebase Auth." },
          "apiSessionToken": { "type": "string", "description": "Bearer opaco usado únicamente con las API del Worker." },
          "firebaseExpiresAt": { "type": "integer", "format": "int64", "description": "Vencimiento del token de ID de Firebase en milisegundos Unix." },
          "apiExpiresAt": { "type": "integer", "format": "int64", "description": "Vencimiento de la sesión del Worker en milisegundos Unix." }
        }
      },
      "SessionRefreshRequest": {
        "type": "object",
        "properties": { "expiresInSeconds": { "type": "integer", "minimum": 120, "example": 300 } }
      },
      "ApiSessionResponse": {
        "type": "object",
        "required": ["apiSessionToken", "expiresAt"],
        "properties": {
          "apiSessionToken": { "type": "string" },
          "expiresAt": { "type": "integer", "format": "int64" }
        }
      },
      "ProbeResponse": {
        "type": "object",
        "required": ["authenticated"],
        "properties": { "authenticated": { "type": "boolean", "const": true } }
      },
      "QuoteRequest": {
        "type": "object",
        "required": ["VehiculoID", "Modelo", "CP", "Edad", "Sexo"],
        "properties": {
          "VehiculoID": { "type": "string" },
          "Modelo": { "type": "string" },
          "CP": { "type": "string" },
          "Edad": { "type": "integer", "minimum": 18 },
          "Sexo": { "type": "integer", "enum": [1, 2] }
        },
        "additionalProperties": false
      },
      "QuoteAcceptedResponse": {
        "type": "object",
        "required": ["globalId"],
        "properties": { "globalId": { "type": "string" } }
      },
      "InsurerResult": {
        "type": "object",
        "required": ["Insurance", "PlanName", "StatusQuote", "totalPremium", "JsonResponse"],
        "properties": {
          "Insurance": { "type": "string", "example": "AXA" },
          "PlanName": { "type": "string", "example": "Amplia" },
          "StatusQuote": { "type": "string", "example": "ready" },
          "totalPremium": { "type": "object", "additionalProperties": true },
          "JsonResponse": { "type": "object", "additionalProperties": true }
        },
        "additionalProperties": true
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": { "error": { "type": "string" } }
      }
    },
    "responses": {
      "ErrorResponse": {
        "description": "Solicitud rechazada.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "M2mRequiredResponse": {
        "description": "Debe repetirse el inicio M2M confiable.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "m2m_required" } } }
      },
      "WorkerUnauthorizedResponse": {
        "description": "La sesión opaca de API del Worker no existe, no es válida, venció o ya no está respaldada por una sesión activa de Firebase.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "Unauthorized" } } }
      }
    }
  }
}
