{
  "openapi": "3.0.3",
  "info": {
    "title": "Cotizador APP — API para WhatsApp e IA",
    "version": "1.0.0",
    "description": "API con clave Bearer y permisos por recurso. La URL base es el directorio /api/v1/ de tu instalación."
  },
  "servers": [{"url": "./", "description": "Directorio de la especificación en tu hosting"}],
  "security": [{"BearerAuth": []}],
  "paths": {
    "/estado.php": {
      "get": {
        "summary": "Comprobar conexión y permisos",
        "responses": {"200": {"description": "Clave válida"}, "401": {"description": "Clave inválida"}}
      }
    },
    "/productos.php": {
      "get": {
        "summary": "Buscar productos por SKU o nombre",
        "description": "Requiere productos:leer",
        "parameters": [
          {"in": "query", "name": "q", "schema": {"type": "string"}},
          {"in": "query", "name": "limit", "schema": {"type": "integer", "default": 30, "maximum": 100}},
          {"in": "query", "name": "offset", "schema": {"type": "integer", "default": 0}}
        ],
        "responses": {
          "200": {"description": "Productos encontrados", "content": {"application/json": {"schema": {"type": "object", "properties": {"ok": {"type": "boolean"}, "productos": {"type": "array", "items": {"$ref": "#/components/schemas/Producto"}}}}}}}
        }
      },
      "post": {
        "summary": "Crear producto libre con SKU opcional",
        "description": "Requiere productos:crear. Si se omite sku, el servidor genera un código consecutivo LIB-0001, etc.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["nombre", "medida", "precio"], "properties": {"sku": {"type": "string"}, "nombre": {"type": "string"}, "medida": {"type": "string"}, "precio": {"type": "number", "minimum": 0}}}, "example": {"nombre": "Tornillo galvanizado", "medida": "Unidad", "precio": 0.35}}}},
        "responses": {"201": {"description": "Producto creado"}, "422": {"description": "Validación"}}
      }
    },
    "/cotizaciones.php": {
      "get": {
        "summary": "Listar cotizaciones o consultar una por ID",
        "description": "Requiere cotizaciones:leer. Consultar un ID devuelve un enlace público nuevo válido durante 30 días.",
        "parameters": [
          {"in": "query", "name": "id", "schema": {"type": "integer", "minimum": 1}},
          {"in": "query", "name": "limit", "schema": {"type": "integer", "maximum": 100, "default": 20}},
          {"in": "query", "name": "offset", "schema": {"type": "integer", "default": 0}}
        ],
        "responses": {"200": {"description": "Listado o detalle"}, "404": {"description": "No encontrada"}}
      },
      "post": {
        "summary": "Crear cotización y generar enlace para el cliente",
        "description": "Requiere cotizaciones:crear. client_ref evita documentos duplicados al reintentar el mismo JSON.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NuevaCotizacion"}, "example": {"client_ref": "wa_mensaje_12345678", "ruc": "20123456789", "representante": "Nombre del cliente", "incluye_igv": true, "descuento_tipo": "monto", "descuento_valor": 0, "items": [{"producto_id": 1, "cantidad": 2}]}}}},
        "responses": {"201": {"description": "Creada con cotizacion.url_publica"}, "200": {"description": "Petición ya aplicada"}, "409": {"description": "Referencia reutilizada con otros datos"}, "422": {"description": "Validación"}}
      }
    },
    "/ver.php": {
      "get": {
        "summary": "Ver cotización con enlace temporal público",
        "security": [],
        "parameters": [{"in": "query", "name": "token", "required": true, "schema": {"type": "string"}}],
        "responses": {"200": {"description": "Vista HTML imprimible", "content": {"text/html": {"schema": {"type": "string"}}}}, "404": {"description": "Token vencido o inválido"}}
      }
    }
  },
  "components": {
    "securitySchemes": {"BearerAuth": {"type": "http", "scheme": "bearer"}},
    "schemas": {
      "Producto": {"type": "object", "properties": {"id": {"type": "integer"}, "sku": {"type": "string"}, "nombre": {"type": "string"}, "medida": {"type": "string"}, "precio": {"type": "number"}}},
      "ItemCotizacion": {"type": "object", "required": ["producto_id", "cantidad"], "properties": {"producto_id": {"type": "integer"}, "cantidad": {"type": "number", "minimum": 0.01}, "precio_unitario": {"type": "number", "minimum": 0, "description": "Opcional: precio negociado, no modifica el catálogo."}}},
      "NuevaCotizacion": {"type": "object", "required": ["client_ref", "ruc", "representante", "items"], "properties": {
        "client_ref": {"type": "string", "minLength": 8, "maxLength": 100},
        "ruc": {"type": "string", "description": "DNI de 8 dígitos o RUC de 11 dígitos"},
        "representante": {"type": "string", "minLength": 3},
        "incluye_igv": {"type": "boolean", "default": false},
        "descuento_tipo": {"type": "string", "enum": ["monto", "porcentaje"], "default": "monto"},
        "descuento_valor": {"type": "number", "minimum": 0, "default": 0},
        "items": {"type": "array", "minItems": 1, "maxItems": 100, "items": {"$ref": "#/components/schemas/ItemCotizacion"}}
      }}
    }
  }
}
