<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
      xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:http="http://www.mulesoft.org/schema/mule/http"
      xmlns:db="http://www.mulesoft.org/schema/mule/db"
      xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
      xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
        http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
        http://www.mulesoft.org/schema/mule/db http://www.mulesoft.org/schema/mule/db/current/mule-db.xsd
        http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd">

  <flow name="shopify-order-webhook-flow">
    <!-- 1. LISTEN FOR THE INCOMING WEBHOOK FROM SHOPIFY -->
    <http:listener config-ref="HTTP_Listener_config" path="/shopify/orders"
                   allowedMethods="POST" doc:name="Receive Shopify webhook">
      <http:response statusCode="#[vars.httpStatus default 200]">
        <http:body>#[payload]</http:body>
      </http:response>
      <http:error-response statusCode="#[vars.httpStatus default 500]">
        <http:body>#[payload]</http:body>
      </http:error-response>
    </http:listener>

    <logger level="INFO"
            message="Received order ##[payload.number default payload.order_number] from #[payload.email default 'unknown']"
            doc:name="Log incoming"/>

    <!-- 2. VALIDATE THE MINIMUM SHAPE WE NEED BEFORE TOUCHING THE DATABASE -->
    <choice doc:name="Has required fields?">
      <when expression="#[(payload.id default null) == null or (payload.email default null) == null]">
        <raise-error type="APP:VALIDATION"
                     description="Webhook payload is missing required fields (id, email)"
                     doc:name="Reject invalid payload"/>
      </when>
    </choice>

    <!-- 3. TRANSFORM SHOPIFY JSON INTO DB-SHAPED VARIABLES -->
    <ee:transform doc:name="Map order and line items">
      <ee:variables>
        <ee:set-variable variableName="orderData"><![CDATA[
%dw 2.0
output application/java
---
{
  shopify_order_id: payload.id,
  customer_email: payload.email default (payload.contact_email default ""),
  customer_name: trim((payload.customer.first_name default "") ++ " " ++ (payload.customer.last_name default "")),
  order_number: ((payload.order_number default payload.number) default 0) as Number,
  order_status: payload.status default "unknown",
  financial_status: payload.financial_status default "pending",
  total_price: (payload.total_price default 0) as Number,
  subtotal_price: (payload.subtotal_price default 0) as Number,
  total_tax: (payload.total_tax default 0) as Number,
  total_shipping: (payload.total_shipping default 0) as Number,
  currency: payload.currency default "USD",
  shopify_updated_at: if ((payload.updated_at default null) != null)
    (payload.updated_at as DateTime) as String {format: "yyyy-MM-dd HH:mm:ss"}
  else now() as String {format: "yyyy-MM-dd HH:mm:ss"}
}
        ]]></ee:set-variable>
        <ee:set-variable variableName="itemsData"><![CDATA[
%dw 2.0
output application/java
---
(payload.line_items default []) map (item) -> {
  shopify_line_item_id: item.id,
  product_id: item.product_id default 0,
  title: item.title default "Unknown Product",
  sku: item.sku default "",
  quantity: (item.quantity default 1) as Number,
  price: (item.price default 0) as Number,
  vendor: item.vendor default ""
}
        ]]></ee:set-variable>
      </ee:variables>
    </ee:transform>

    <!-- 4. PERSIST ORDER + LINE ITEMS ATOMICALLY -->
    <try transactionalAction="ALWAYS_BEGIN" doc:name="Transactional DB write">
      <db:insert config-ref="MySQL_Config" doc:name="Upsert order">
        <db:sql><![CDATA[
INSERT INTO shopify_orders
  (shopify_order_id, customer_email, customer_name, order_number,
   order_status, financial_status, total_price, subtotal_price,
   total_tax, total_shipping, currency, shopify_updated_at)
VALUES
  (:shopify_order_id, :customer_email, :customer_name, :order_number,
   :order_status, :financial_status, :total_price, :subtotal_price,
   :total_tax, :total_shipping, :currency, :shopify_updated_at)
ON DUPLICATE KEY UPDATE
  id = LAST_INSERT_ID(id),
  customer_email = VALUES(customer_email),
  customer_name = VALUES(customer_name),
  order_number = VALUES(order_number),
  order_status = VALUES(order_status),
  financial_status = VALUES(financial_status),
  total_price = VALUES(total_price),
  subtotal_price = VALUES(subtotal_price),
  total_tax = VALUES(total_tax),
  total_shipping = VALUES(total_shipping),
  currency = VALUES(currency),
  shopify_updated_at = VALUES(shopify_updated_at)
        ]]></db:sql>
        <db:input-parameters><![CDATA[#[vars.orderData]]]></db:input-parameters>
      </db:insert>

      <db:select config-ref="MySQL_Config" doc:name="Get order primary key">
        <db:sql>SELECT LAST_INSERT_ID() AS order_id</db:sql>
      </db:select>

      <ee:transform doc:name="Store order id">
        <ee:variables>
          <ee:set-variable variableName="orderId"><![CDATA[
%dw 2.0
output application/java
---
payload[0].order_id as Number
          ]]></ee:set-variable>
        </ee:variables>
      </ee:transform>

      <foreach collection="#[vars.itemsData]" doc:name="For each line item">
        <db:insert config-ref="MySQL_Config" doc:name="Upsert line item">
          <db:sql><![CDATA[
INSERT INTO shopify_order_items
  (order_id, shopify_order_id, shopify_line_item_id, product_id, title, sku, quantity, price, vendor)
VALUES
  (:order_id, :shopify_order_id, :shopify_line_item_id, :product_id, :title, :sku, :quantity, :price, :vendor)
ON DUPLICATE KEY UPDATE
  product_id = VALUES(product_id),
  title = VALUES(title),
  sku = VALUES(sku),
  quantity = VALUES(quantity),
  price = VALUES(price),
  vendor = VALUES(vendor)
          ]]></db:sql>
          <db:input-parameters><![CDATA[#[{
            order_id: vars.orderId,
            shopify_order_id: vars.orderData.shopify_order_id,
            shopify_line_item_id: payload.shopify_line_item_id,
            product_id: payload.product_id,
            title: payload.title,
            sku: payload.sku,
            quantity: payload.quantity,
            price: payload.price,
            vendor: payload.vendor
          }]]]></db:input-parameters>
        </db:insert>
      </foreach>
    </try>

    <!-- 5. BUILD SUCCESS RESPONSE -->
    <ee:transform doc:name="Build success response">
      <ee:message>
        <ee:set-payload><![CDATA[
%dw 2.0
output application/json
---
{
  success: true,
  message: "Order #" ++ (vars.orderData.order_number as String) ++ " processed successfully",
  shopify_order_id: vars.orderData.shopify_order_id,
  database_order_id: vars.orderId,
  items_count: sizeOf(vars.itemsData),
  timestamp: now()
}
        ]]></ee:set-payload>
      </ee:message>
    </ee:transform>
    <set-variable variableName="httpStatus" value="#[200]" doc:name="200 OK"/>
    <logger level="INFO"
            message="Order #[vars.orderData.order_number] (db id #[vars.orderId]) saved with #[sizeOf(vars.itemsData)] item(s)"
            doc:name="Log success"/>

    <error-handler>
      <on-error-continue type="APP:VALIDATION" doc:name="Bad request">
        <logger level="WARN" message="Rejected order webhook: #[error.description]" doc:name="Log validation error"/>
        <set-variable variableName="httpStatus" value="#[400]" doc:name="400 Bad Request"/>
        <ee:transform doc:name="Build error response">
          <ee:message><ee:set-payload><![CDATA[
%dw 2.0
output application/json
---
{ success: false, error: error.description, timestamp: now() }
          ]]></ee:set-payload></ee:message>
        </ee:transform>
      </on-error-continue>

      <on-error-continue type="DB:CONNECTIVITY" doc:name="DB unavailable">
        <logger level="ERROR" message="Database connectivity error: #[error.description]" doc:name="Log connectivity error"/>
        <set-variable variableName="httpStatus" value="#[503]" doc:name="503 Service Unavailable"/>
        <ee:transform doc:name="Build error response">
          <ee:message><ee:set-payload><![CDATA[
%dw 2.0
output application/json
---
{ success: false, error: "Database temporarily unavailable, please retry", timestamp: now() }
          ]]></ee:set-payload></ee:message>
        </ee:transform>
      </on-error-continue>

      <on-error-continue type="ANY" doc:name="Unhandled error">
        <logger level="ERROR" message="Error processing order webhook: #[error.description]" doc:name="Log error"/>
        <set-variable variableName="httpStatus" value="#[500]" doc:name="500 Internal Server Error"/>
        <ee:transform doc:name="Build error response">
          <ee:message><ee:set-payload><![CDATA[
%dw 2.0
output application/json
---
{ success: false, error: error.description, timestamp: now() }
          ]]></ee:set-payload></ee:message>
        </ee:transform>
      </on-error-continue>
    </error-handler>
  </flow>
</mule>
