openapi: 3.1.0
info:
  title: Api
  version: 0.1.0
  description: Huasheng Exports E-Commerce API
servers:
  - url: /api
    description: Base API path
tags:
  - name: health
    description: Health operations
  - name: products
    description: Product catalog
  - name: categories
    description: Product categories
  - name: cart
    description: Shopping cart
  - name: orders
    description: Orders
  - name: reviews
    description: Product reviews
  - name: wishlist
    description: Wishlist
  - name: admin
    description: Admin operations
  - name: stats
    description: Store statistics

paths:
  /healthz:
    get:
      operationId: healthCheck
      tags: [health]
      summary: Health check
      responses:
        "200":
          description: Healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthStatus"

  # ─── Products ────────────────────────────────────────────────────────────────
  /products:
    get:
      operationId: listProducts
      tags: [products]
      summary: List products with optional filters
      parameters:
        - name: categoryId
          in: query
          schema: { type: ["integer", "null"] }
        - name: search
          in: query
          schema: { type: ["string", "null"] }
        - name: minPrice
          in: query
          schema: { type: ["number", "null"] }
        - name: maxPrice
          in: query
          schema: { type: ["number", "null"] }
        - name: inStock
          in: query
          schema: { type: ["boolean", "null"] }
        - name: sort
          in: query
          schema:
            type: ["string", "null"]
            enum: ["price_asc", "price_desc", "newest", "popular", null]
        - name: page
          in: query
          schema: { type: ["integer", "null"] }
        - name: limit
          in: query
          schema: { type: ["integer", "null"] }
      responses:
        "200":
          description: Product list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductListResponse"
    post:
      operationId: createProduct
      tags: [products]
      summary: Create a product (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductInput"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"

  /products/featured:
    get:
      operationId: getFeaturedProducts
      tags: [products]
      summary: Get featured / hero products for the homepage
      responses:
        "200":
          description: Featured products
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Product"

  /products/{id}:
    get:
      operationId: getProduct
      tags: [products]
      summary: Get a single product by id
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Product
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    patch:
      operationId: updateProduct
      tags: [products]
      summary: Update a product (admin)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductUpdate"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"
    delete:
      operationId: deleteProduct
      tags: [products]
      summary: Delete a product (admin)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "204":
          description: Deleted

  # ─── Categories ──────────────────────────────────────────────────────────────
  /categories:
    get:
      operationId: listCategories
      tags: [categories]
      summary: List all categories
      responses:
        "200":
          description: Categories
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Category"
    post:
      operationId: createCategory
      tags: [categories]
      summary: Create a category (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CategoryInput"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Category"

  # ─── Cart ────────────────────────────────────────────────────────────────────
  /cart/{sessionId}:
    get:
      operationId: getCart
      tags: [cart]
      summary: Get current session cart
      parameters:
        - name: sessionId
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Cart
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Cart"

  /cart/{sessionId}/items:
    post:
      operationId: addToCart
      tags: [cart]
      summary: Add item to cart
      parameters:
        - name: sessionId
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CartItemInput"
      responses:
        "200":
          description: Updated cart
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Cart"

  /cart/{sessionId}/items/{productId}:
    patch:
      operationId: updateCartItem
      tags: [cart]
      summary: Update quantity of a cart item
      parameters:
        - name: sessionId
          in: path
          required: true
          schema: { type: string }
        - name: productId
          in: path
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CartItemUpdate"
      responses:
        "200":
          description: Updated cart
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Cart"
    delete:
      operationId: removeFromCart
      tags: [cart]
      summary: Remove item from cart
      parameters:
        - name: sessionId
          in: path
          required: true
          schema: { type: string }
        - name: productId
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Updated cart
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Cart"

  # ─── Orders ──────────────────────────────────────────────────────────────────
  /orders:
    get:
      operationId: listOrders
      tags: [orders]
      summary: List orders (admin sees all, guests see by email)
      parameters:
        - name: email
          in: query
          schema: { type: ["string", "null"] }
        - name: status
          in: query
          schema: { type: ["string", "null"] }
        - name: page
          in: query
          schema: { type: ["integer", "null"] }
      responses:
        "200":
          description: Orders
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Order"
    post:
      operationId: createOrder
      tags: [orders]
      summary: Place a new order (checkout)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrderInput"
      responses:
        "201":
          description: Order placed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"

  /orders/{id}:
    get:
      operationId: getOrder
      tags: [orders]
      summary: Get order by id
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Order
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
    patch:
      operationId: updateOrderStatus
      tags: [orders]
      summary: Update order status (admin)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrderStatusUpdate"
      responses:
        "200":
          description: Updated order
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"

  # ─── Reviews ─────────────────────────────────────────────────────────────────
  /products/{id}/reviews:
    get:
      operationId: listProductReviews
      tags: [reviews]
      summary: List reviews for a product
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Reviews
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Review"
    post:
      operationId: createReview
      tags: [reviews]
      summary: Submit a review for a product
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReviewInput"
      responses:
        "201":
          description: Review created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Review"

  # ─── Wishlist ─────────────────────────────────────────────────────────────────
  /wishlist:
    get:
      operationId: getWishlist
      tags: [wishlist]
      summary: Get wishlist for a session
      parameters:
        - name: sessionId
          in: query
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Wishlist
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Product"

  /wishlist/toggle:
    post:
      operationId: toggleWishlist
      tags: [wishlist]
      summary: Add or remove a product from wishlist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WishlistToggleInput"
      responses:
        "200":
          description: Updated state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WishlistToggleResult"

  # ─── Stats / Dashboard ────────────────────────────────────────────────────────
  /stats/summary:
    get:
      operationId: getStoreSummary
      tags: [stats]
      summary: Homepage hero stats (total products, categories, orders fulfilled)
      responses:
        "200":
          description: Store summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StoreSummary"

  /stats/admin:
    get:
      operationId: getAdminStats
      tags: [stats]
      summary: Admin dashboard statistics
      responses:
        "200":
          description: Admin stats
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminStats"

  /stats/top-products:
    get:
      operationId: getTopProducts
      tags: [stats]
      summary: Top selling products for admin dashboard
      responses:
        "200":
          description: Top products
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/TopProduct"

  /stats/recent-orders:
    get:
      operationId: getRecentOrders
      tags: [stats]
      summary: Most recent orders for admin dashboard
      responses:
        "200":
          description: Recent orders
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Order"

components:
  schemas:
    HealthStatus:
      type: object
      required: [status]
      properties:
        status: { type: string }

    ErrorResponse:
      type: object
      required: [error]
      properties:
        error: { type: string }

    # ─── Category ─────────────────────────────────────────────────────────────
    Category:
      type: object
      required: [id, name, slug, productCount]
      properties:
        id: { type: integer }
        name: { type: string }
        slug: { type: string }
        description: { type: ["string", "null"] }
        imageUrl: { type: ["string", "null"] }
        productCount: { type: integer }

    CategoryInput:
      type: object
      required: [name, slug]
      properties:
        name: { type: string }
        slug: { type: string }
        description: { type: string }
        imageUrl: { type: string }

    # ─── Product ──────────────────────────────────────────────────────────────
    Product:
      type: object
      required: [id, name, slug, price, categoryId, categoryName, stock, imageUrls, isFeatured, createdAt]
      properties:
        id: { type: integer }
        name: { type: string }
        slug: { type: string }
        description: { type: ["string", "null"] }
        price: { type: number }
        compareAtPrice: { type: ["number", "null"] }
        categoryId: { type: integer }
        categoryName: { type: string }
        stock: { type: integer }
        imageUrls:
          type: array
          items: { type: string }
        isFeatured: { type: boolean }
        rating: { type: ["number", "null"] }
        reviewCount: { type: integer }
        brand: { type: ["string", "null"] }
        specs:
          type: ["object", "null"]
          additionalProperties: true
        createdAt: { type: string }

    ProductInput:
      type: object
      required: [name, slug, price, categoryId, stock]
      properties:
        name: { type: string }
        slug: { type: string }
        description: { type: string }
        price: { type: number }
        compareAtPrice: { type: number }
        categoryId: { type: integer }
        stock: { type: integer }
        imageUrls:
          type: array
          items: { type: string }
        isFeatured: { type: boolean }
        brand: { type: string }
        specs:
          type: object
          additionalProperties: true

    ProductUpdate:
      type: object
      properties:
        name: { type: string }
        description: { type: string }
        price: { type: number }
        compareAtPrice: { type: ["number", "null"] }
        categoryId: { type: integer }
        stock: { type: integer }
        imageUrls:
          type: array
          items: { type: string }
        isFeatured: { type: boolean }
        brand: { type: string }
        specs:
          type: object
          additionalProperties: true

    ProductListResponse:
      type: object
      required: [products, total, page, limit]
      properties:
        products:
          type: array
          items:
            $ref: "#/components/schemas/Product"
        total: { type: integer }
        page: { type: integer }
        limit: { type: integer }

    # ─── Cart ─────────────────────────────────────────────────────────────────
    CartItem:
      type: object
      required: [productId, name, price, quantity, imageUrl, stock]
      properties:
        productId: { type: integer }
        name: { type: string }
        price: { type: number }
        compareAtPrice: { type: ["number", "null"] }
        quantity: { type: integer }
        imageUrl: { type: ["string", "null"] }
        stock: { type: integer }

    Cart:
      type: object
      required: [sessionId, items, subtotal, itemCount]
      properties:
        sessionId: { type: string }
        items:
          type: array
          items:
            $ref: "#/components/schemas/CartItem"
        subtotal: { type: number }
        itemCount: { type: integer }

    CartItemInput:
      type: object
      required: [productId, quantity]
      properties:
        productId: { type: integer }
        quantity: { type: integer }

    CartItemUpdate:
      type: object
      required: [quantity]
      properties:
        quantity: { type: integer }

    # ─── Order ────────────────────────────────────────────────────────────────
    OrderItem:
      type: object
      required: [productId, name, price, quantity, imageUrl]
      properties:
        productId: { type: integer }
        name: { type: string }
        price: { type: number }
        quantity: { type: integer }
        imageUrl: { type: ["string", "null"] }

    Order:
      type: object
      required: [id, customerName, customerEmail, status, total, items, shippingAddress, createdAt]
      properties:
        id: { type: integer }
        customerName: { type: string }
        customerEmail: { type: string }
        customerPhone: { type: ["string", "null"] }
        status:
          type: string
          enum: [pending, confirmed, processing, shipped, delivered, cancelled]
        total: { type: number }
        shippingAddress: { type: string }
        trackingNumber: { type: ["string", "null"] }
        notes: { type: ["string", "null"] }
        items:
          type: array
          items:
            $ref: "#/components/schemas/OrderItem"
        createdAt: { type: string }
        updatedAt: { type: string }

    OrderInput:
      type: object
      required: [customerName, customerEmail, shippingAddress, sessionId]
      properties:
        customerName: { type: string }
        customerEmail: { type: string }
        customerPhone: { type: string }
        shippingAddress: { type: string }
        notes: { type: string }
        sessionId: { type: string }

    OrderStatusUpdate:
      type: object
      required: [status]
      properties:
        status:
          type: string
          enum: [pending, confirmed, processing, shipped, delivered, cancelled]
        trackingNumber: { type: string }
        notes: { type: string }

    # ─── Review ───────────────────────────────────────────────────────────────
    Review:
      type: object
      required: [id, productId, reviewerName, rating, createdAt]
      properties:
        id: { type: integer }
        productId: { type: integer }
        reviewerName: { type: string }
        rating: { type: integer }
        title: { type: ["string", "null"] }
        body: { type: ["string", "null"] }
        verified: { type: boolean }
        createdAt: { type: string }

    ReviewInput:
      type: object
      required: [reviewerName, rating]
      properties:
        reviewerName: { type: string }
        rating: { type: integer }
        title: { type: string }
        body: { type: string }

    # ─── Wishlist ─────────────────────────────────────────────────────────────
    WishlistToggleInput:
      type: object
      required: [sessionId, productId]
      properties:
        sessionId: { type: string }
        productId: { type: integer }

    WishlistToggleResult:
      type: object
      required: [added, productId]
      properties:
        added: { type: boolean }
        productId: { type: integer }

    # ─── Stats ────────────────────────────────────────────────────────────────
    StoreSummary:
      type: object
      required: [totalProducts, totalCategories, ordersDelivered, avgRating]
      properties:
        totalProducts: { type: integer }
        totalCategories: { type: integer }
        ordersDelivered: { type: integer }
        avgRating: { type: number }

    AdminStats:
      type: object
      required: [totalRevenue, totalOrders, pendingOrders, totalProducts, lowStockCount, revenueByCategory]
      properties:
        totalRevenue: { type: number }
        totalOrders: { type: integer }
        pendingOrders: { type: integer }
        totalProducts: { type: integer }
        lowStockCount: { type: integer }
        revenueByCategory:
          type: array
          items:
            $ref: "#/components/schemas/CategoryRevenue"

    CategoryRevenue:
      type: object
      required: [categoryName, revenue, orderCount]
      properties:
        categoryName: { type: string }
        revenue: { type: number }
        orderCount: { type: integer }

    TopProduct:
      type: object
      required: [id, name, imageUrl, totalSold, revenue]
      properties:
        id: { type: integer }
        name: { type: string }
        imageUrl: { type: ["string", "null"] }
        totalSold: { type: integer }
        revenue: { type: number }
