{
  "openapi": "3.0.3",
  "info": {
    "title": "Proptee API",
    "version": "1.0.0",
    "description": "Express + MySQL backend for the Proptee real-estate app. Layered architecture: routes → controller → service → model.\n\n**Auth:** call `POST /api/auth/login`, then send `Authorization: Bearer <token>` on every protected endpoint. Endpoints marked **Public** need no token.\n\n**Admin seed account:** `admin@propertee.com` / `Admin123!`"
  },
  "servers": [
    { "url": "http://localhost:5000", "description": "Local dev server" }
  ],
  "tags": [
    { "name": "Auth", "description": "Admin login, session, registration" },
    { "name": "Properties", "description": "Listings CRUD and public search" },
    { "name": "Clients", "description": "CRM clients (admin only)" },
    { "name": "Stats", "description": "Dashboard analytics (admin only)" },
    { "name": "Contact", "description": "Contact form messages" },
    { "name": "Tours", "description": "Private tour requests" },
    { "name": "Uploads", "description": "Image upload (base64 data URLs)" },
    { "name": "System", "description": "Health and static assets" }
  ],
  "security": [{ "bearerAuth": [] }],
  "paths": {
    "/api/health": {
      "get": {
        "tags": ["System"],
        "operationId": "getHealth",
        "summary": "Health check",
        "description": "Public. Returns `{ success: true, status: 'ok' }`.",
        "security": [],
        "responses": {
          "200": {
            "description": "Service is up",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Health" }
              }
            }
          }
        }
      }
    },
    "/api/auth/login": {
      "post": {
        "tags": ["Auth"],
        "operationId": "login",
        "summary": "Admin login",
        "description": "Public. Verifies email + password (bcrypt) and returns a JWT valid for 12 hours.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LoginRequest" },
              "example": { "email": "admin@propertee.com", "password": "Admin123!" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login successful",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LoginResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/auth/me": {
      "get": {
        "tags": ["Auth"],
        "operationId": "getMe",
        "summary": "Current admin session",
        "description": "Protected. Validates the bearer token and returns the admin without the password.",
        "responses": {
          "200": {
            "description": "Session valid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "admin": { "$ref": "#/components/schemas/Admin" }
                  },
                  "required": ["success", "admin"]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/auth/register": {
      "post": {
        "tags": ["Auth"],
        "operationId": "registerAdmin",
        "summary": "Create another admin",
        "description": "Protected (existing admin token required). Email must be unique; password minimum 8 characters.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RegisterRequest" },
              "example": { "name": "Second Admin", "email": "second@propertee.com", "password": "StrongPass1!" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Admin created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "admin": { "$ref": "#/components/schemas/Admin" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": {
            "description": "Email already registered",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    },
    "/api/properties": {
      "get": {
        "tags": ["Properties"],
        "operationId": "listProperties",
        "summary": "List properties",
        "description": "Public. Optional filters: free-text search (title/location), tag, status. Callers typically hide `Draft` listings client-side.",
        "security": [],
        "parameters": [
          { "name": "search", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Matches title or location (case-insensitive)" },
          { "name": "tag", "in": "query", "required": false, "schema": { "type": "string", "enum": ["Featured", "New listing", "Open Sunday"] } },
          { "name": "status", "in": "query", "required": false, "schema": { "type": "string", "enum": ["Active", "Pending", "Draft"] } },
          { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "Max rows returned" }
        ],
        "responses": {
          "200": {
            "description": "Property list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "count": { "type": "integer", "example": 12 },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Property" } }
                  },
                  "required": ["success", "data"]
                },
                "example": {
                  "success": true,
                  "count": 1,
                  "data": [
                    {
                      "id": 1,
                      "title": "Luxury Waterfront Villa",
                      "location": "Miami, FL",
                      "price": 1245000,
                      "beds": 4,
                      "baths": 3,
                      "sqft": 3200,
                      "image": "http://localhost:5000/uploads/listing-1.jpg",
                      "tag": "Featured",
                      "status": "Active",
                      "views": 214,
                      "description": "Stunning waterfront villa with private dock.",
                      "owner": "Jane Cooper",
                      "created_at": "2026-09-01T10:00:00.000Z"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Properties"],
        "operationId": "createProperty",
        "summary": "Create property",
        "description": "Protected. `price` accepts formatted strings (`\"₦1,245,000\"`) and is stored as a number. `image` may be a Cloudinary URL from `POST /api/uploads` or any URL.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PropertyCreate" },
              "example": {
                "title": "Modern Downtown Loft",
                "location": "Austin, TX",
                "price": "₦489,000",
                "beds": 2,
                "baths": 2,
                "sqft": 1100,
                "tag": "New listing",
                "status": "Draft",
                "description": "Bright loft with city views."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PropertyResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/properties/{id}": {
      "parameters": [
        { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }
      ],
      "get": {
        "tags": ["Properties"],
        "operationId": "getProperty",
        "summary": "Get property by ID",
        "description": "Public. Increments the view counter.",
        "security": [],
        "responses": {
          "200": {
            "description": "Property found",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PropertyResponse" } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "put": {
        "tags": ["Properties"],
        "operationId": "updateProperty",
        "summary": "Update property",
        "description": "Protected. Partial updates allowed — only provided fields are written.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PropertyUpdate" },
              "example": { "status": "Active", "price": 1299000 }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PropertyResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "delete": {
        "tags": ["Properties"],
        "operationId": "deleteProperty",
        "summary": "Delete property",
        "description": "Protected.",
        "responses": {
          "200": {
            "description": "Deleted",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessMessage" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/clients": {
      "get": {
        "tags": ["Clients"],
        "operationId": "listClients",
        "summary": "List clients",
        "description": "Protected.",
        "parameters": [
          { "name": "search", "in": "query", "required": false, "schema": { "type": "string" } },
          { "name": "status", "in": "query", "required": false, "schema": { "type": "string", "enum": ["Active", "Prospect", "Inactive"] } }
        ],
        "responses": {
          "200": {
            "description": "Client list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "count": { "type": "integer" },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Client" } }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      },
      "post": {
        "tags": ["Clients"],
        "operationId": "createClient",
        "summary": "Create client",
        "description": "Protected.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ClientCreate" },
              "example": { "name": "Alice Morgan", "email": "alice@example.com", "phone": "+1 555 0100", "interest": "Waterfront", "status": "Prospect", "budget": "$500k – $800k" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/clients/{id}": {
      "parameters": [
        { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }
      ],
      "get": {
        "tags": ["Clients"],
        "operationId": "getClient",
        "summary": "Get client by ID",
        "description": "Protected.",
        "responses": {
          "200": {
            "description": "Client found",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "put": {
        "tags": ["Clients"],
        "operationId": "updateClient",
        "summary": "Update client",
        "description": "Protected. Partial updates allowed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ClientUpdate" },
              "example": { "status": "Active", "notes": "Ready to view properties this weekend." }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "delete": {
        "tags": ["Clients"],
        "operationId": "deleteClient",
        "summary": "Delete client",
        "description": "Protected.",
        "responses": {
          "200": {
            "description": "Deleted",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessMessage" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/stats": {
      "get": {
        "tags": ["Stats"],
        "operationId": "getStats",
        "summary": "Dashboard statistics",
        "description": "Protected. Powers the admin analytics view.",
        "responses": {
          "200": {
            "description": "Aggregated stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": { "$ref": "#/components/schemas/Stats" }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "totalListings": 12,
                    "activeListings": 8,
                    "pendingListings": 2,
                    "draftListings": 2,
                    "totalViews": 1842,
                    "avgListingPrice": 742500,
                    "totalClients": 15,
                    "activeClients": 9,
                    "inquiries": 6
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/contact": {
      "post": {
        "tags": ["Contact"],
        "operationId": "sendContact",
        "summary": "Submit contact form",
        "description": "Public. Stores an inquiry message.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactCreate" },
              "example": { "name": "Bob Stone", "email": "bob@example.com", "phone": "+1 555 0142", "subject": "Viewing request", "message": "I'd like to schedule a viewing for the downtown loft." }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Message stored",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactMessage" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      },
      "get": {
        "tags": ["Contact"],
        "operationId": "listContacts",
        "summary": "List contact messages",
        "description": "Protected.",
        "responses": {
          "200": {
            "description": "Message list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "count": { "type": "integer" },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/ContactMessage" } }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/tours": {
      "post": {
        "tags": ["Tours"],
        "operationId": "requestTour",
        "summary": "Request a private tour",
        "description": "Public. Submitted from a property page. Name, email and phone are required; `propertyId` links the request to a listing.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TourCreate" },
              "example": { "propertyId": 1, "name": "Casey Ford", "email": "casey@email.com", "phone": "+1 555 0199", "message": "Saturday morning works" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tour request stored",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TourResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": {
            "description": "propertyId does not exist",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      },
      "get": {
        "tags": ["Tours"],
        "operationId": "listTourRequests",
        "summary": "List tour requests",
        "description": "Protected. Newest first, each joined with `property_title`. Optional `?limit=N`.",
        "parameters": [
          { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 } }
        ],
        "responses": {
          "200": {
            "description": "Tour request list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "count": { "type": "integer" },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/TourRequest" } }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/uploads": {
      "post": {
        "tags": ["Uploads"],
        "operationId": "uploadImage",
        "summary": "Upload image",
        "description": "Protected. Body is JSON with a base64 data URL (not multipart). Max 5 MB; accepts jpeg/png/webp/gif. Uploads to Cloudinary (signed server-side) and returns its `secure_url`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UploadRequest" },
              "example": { "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upload stored",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadResponse" } } }
          },
          "400": {
            "description": "Invalid file type, malformed data URL, or file too large",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/uploads/{filename}": {
      "parameters": [
        { "name": "filename", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Stored filename (served by express.static)" }
      ],
      "get": {
        "tags": ["System"],
        "operationId": "getUploadedFile",
        "summary": "Download uploaded file",
        "description": "Public static file.",
        "security": [],
        "responses": {
          "200": { "description": "Image bytes", "content": { "image/*": { "schema": { "type": "string", "format": "binary" } } } },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JWT from `POST /api/auth/login`. Header: `Authorization: Bearer <token>`"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation error or missing fields",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unauthorized": {
        "description": "Missing / invalid / expired token, or bad credentials",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": false },
          "error": { "type": "string", "example": "Invalid credentials" }
        },
        "required": ["success", "error"]
      },
      "SuccessMessage": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "message": { "type": "string", "example": "Deleted successfully" }
        }
      },
      "Health": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "status": { "type": "string", "example": "ok" }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": ["email", "password"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "password": { "type": "string", "format": "password" }
        }
      },
      "LoginResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "token": { "type": "string", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." },
          "admin": { "$ref": "#/components/schemas/Admin" }
        },
        "required": ["success", "token", "admin"]
      },
      "RegisterRequest": {
        "type": "object",
        "required": ["name", "email", "password"],
        "properties": {
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "password": { "type": "string", "format": "password", "minLength": 8 }
        }
      },
      "Admin": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "example": 1 },
          "name": { "type": "string", "example": "Admin" },
          "email": { "type": "string", "format": "email", "example": "admin@propertee.com" },
          "role": { "type": "string", "example": "admin" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Property": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "example": 1 },
          "title": { "type": "string", "example": "Luxury Waterfront Villa" },
          "location": { "type": "string", "example": "Miami, FL" },
          "price": { "type": "number", "example": 1245000 },
          "beds": { "type": "integer", "nullable": true, "example": 4 },
          "baths": { "type": "number", "nullable": true, "example": 3 },
          "sqft": { "type": "integer", "nullable": true, "example": 3200 },
          "image": { "type": "string", "nullable": true, "example": "http://localhost:5000/uploads/listing-1.jpg" },
          "tag": { "type": "string", "nullable": true, "enum": ["Featured", "New listing", "Open Sunday"] },
          "status": { "type": "string", "enum": ["Active", "Pending", "Draft"], "example": "Active" },
          "views": { "type": "integer", "example": 214 },
          "description": { "type": "string", "nullable": true },
          "owner_id": { "type": "integer", "nullable": true },
          "owner": { "type": "string", "nullable": true, "example": "Jane Cooper" },
          "created_at": { "type": "string", "format": "date-time" }
        },
        "required": ["id", "title", "location", "price", "status"]
      },
      "PropertyCreate": {
        "type": "object",
        "required": ["title", "location", "price"],
        "properties": {
          "title": { "type": "string", "example": "Modern Downtown Loft" },
          "location": { "type": "string", "example": "Austin, TX" },
          "price": { "description": "Number or formatted string like \"₦1,245,000\"", "oneOf": [{ "type": "number" }, { "type": "string" }], "example": "₦489,000" },
          "beds": { "type": "integer", "nullable": true },
          "baths": { "type": "number", "nullable": true },
          "sqft": { "type": "integer", "nullable": true },
          "image": { "type": "string", "nullable": true },
          "tag": { "type": "string", "nullable": true, "enum": ["Featured", "New listing", "Open Sunday"] },
          "status": { "type": "string", "enum": ["Active", "Pending", "Draft"] },
          "description": { "type": "string", "nullable": true },
          "owner": { "type": "string", "nullable": true }
        }
      },
      "PropertyUpdate": {
        "type": "object",
        "properties": {
          "title": { "type": "string" },
          "location": { "type": "string" },
          "price": { "oneOf": [{ "type": "number" }, { "type": "string" }] },
          "beds": { "type": "integer", "nullable": true },
          "baths": { "type": "number", "nullable": true },
          "sqft": { "type": "integer", "nullable": true },
          "image": { "type": "string", "nullable": true },
          "tag": { "type": "string", "nullable": true, "enum": ["Featured", "New listing", "Open Sunday"] },
          "status": { "type": "string", "enum": ["Active", "Pending", "Draft"] },
          "description": { "type": "string", "nullable": true },
          "owner": { "type": "string", "nullable": true }
        }
      },
      "PropertyResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean" },
          "data": { "$ref": "#/components/schemas/Property" }
        }
      },
      "Client": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "example": 3 },
          "name": { "type": "string", "example": "Alice Morgan" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string", "nullable": true },
          "interest": { "type": "string", "nullable": true, "example": "Waterfront" },
          "budget": { "type": "string", "nullable": true, "example": "$500k – $800k" },
          "status": { "type": "string", "enum": ["Active", "Prospect", "Inactive"], "example": "Prospect" },
          "notes": { "type": "string", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" }
        },
        "required": ["id", "name", "email", "status"]
      },
      "ClientCreate": {
        "type": "object",
        "required": ["name", "email"],
        "properties": {
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string", "nullable": true },
          "interest": { "type": "string", "nullable": true },
          "budget": { "type": "string", "nullable": true },
          "status": { "type": "string", "enum": ["Active", "Prospect", "Inactive"] },
          "notes": { "type": "string", "nullable": true }
        }
      },
      "ClientUpdate": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string", "nullable": true },
          "interest": { "type": "string", "nullable": true },
          "budget": { "type": "string", "nullable": true },
          "status": { "type": "string", "enum": ["Active", "Prospect", "Inactive"] },
          "notes": { "type": "string", "nullable": true }
        }
      },
      "ClientResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean" },
          "data": { "$ref": "#/components/schemas/Client" }
        }
      },
      "Stats": {
        "type": "object",
        "properties": {
          "totalListings": { "type": "integer", "example": 12 },
          "activeListings": { "type": "integer", "example": 8 },
          "pendingListings": { "type": "integer", "example": 2 },
          "draftListings": { "type": "integer", "example": 2 },
          "totalViews": { "type": "integer", "example": 1842 },
          "avgListingPrice": { "type": "number", "example": 742500 },
          "totalClients": { "type": "integer", "example": 15 },
          "activeClients": { "type": "integer", "example": 9 },
          "inquiries": { "type": "integer", "example": 6 },
          "tourRequests": { "type": "integer", "example": 3 }
        }
      },
      "ContactCreate": {
        "type": "object",
        "required": ["name", "email", "message"],
        "properties": {
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string", "nullable": true },
          "subject": { "type": "string", "nullable": true },
          "message": { "type": "string" }
        }
      },
      "ContactMessage": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "example": 7 },
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string", "nullable": true },
          "subject": { "type": "string", "nullable": true },
          "message": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "UploadRequest": {
        "type": "object",
        "required": ["data"],
        "properties": {
          "data": { "type": "string", "description": "Base64 data URL, e.g. `data:image/png;base64,...`", "maxLength": 7000000 }
        }
      },
      "TourCreate": {
        "type": "object",
        "required": ["name", "email", "phone"],
        "properties": {
          "name": { "type": "string", "example": "Casey Ford" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string", "description": "7–25 chars, digits, `+`, `(`, `)`, `-`, `.`, spaces", "example": "+1 555 0199" },
          "propertyId": { "type": "integer", "nullable": true, "description": "Must exist if provided" },
          "message": { "type": "string", "nullable": true, "description": "Preferred days, times, etc." }
        }
      },
      "TourRequest": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "example": 5 },
          "property_id": { "type": "integer", "nullable": true },
          "property_title": { "type": "string", "nullable": true, "example": "Modern lakefront retreat" },
          "name": { "type": "string", "example": "Casey Ford" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string", "example": "+1 555 0199" },
          "message": { "type": "string", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" }
        },
        "required": ["id", "name", "email", "phone"]
      },
      "TourResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean" },
          "data": { "$ref": "#/components/schemas/TourRequest" }
        }
      },
      "UploadResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "url": { "type": "string", "example": "https://res.cloudinary.com/kvcyy1jg/image/upload/v1790423831/propertee/xyz.png" },
          "publicId": { "type": "string", "example": "propertee/xyz" },
          "filename": { "type": "string", "example": "propertee/xyz.png" },
          "size": { "type": "integer", "example": 70 },
          "mimeType": { "type": "string", "example": "image/png" },
          "width": { "type": "integer", "example": 400 },
          "height": { "type": "integer", "example": 300 }
        }
      }
    }
  }
}
