//! Python bindings for StateSet Embedded Commerce
//!
//! Provides a local-first commerce library with SQLite storage.
//!
//! ```python
//! from stateset_embedded import Commerce
//!
//! commerce = Commerce("./store.db")
//! customer = commerce.customers.create(
//!     email="alice@example.com",
//!     first_name="Alice",
//!     last_name="Smith"
//! )
//! ```

use pyo3::prelude::*;
use pyo3::exceptions::{PyRuntimeError, PyValueError};
use rust_decimal::Decimal;
// Use :: prefix to refer to the external crate, not the pymodule
use ::stateset_embedded::Commerce as RustCommerce;
use std::sync::{Arc, Mutex};

fn to_f64_or_nan<T>(value: T) -> f64
where
    T: TryInto<f64>,
    <T as TryInto<f64>>::Error: std::fmt::Display,
{
    match value.try_into() {
        Ok(converted) => converted,
        Err(err) => {
            eprintln!("stateset-embedded: failed to convert to f64: {}", err);
            f64::NAN
        }
    }
}

// ============================================================================
// Commerce
// ============================================================================

/// Main Commerce instance for local commerce operations.
///
/// Example:
///     commerce = Commerce("./store.db")
///     commerce = Commerce(":memory:")  # In-memory database
#[pyclass]
pub struct Commerce {
    inner: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Commerce {
    /// Create a new Commerce instance with a database path.
    ///
    /// Args:
    ///     db_path: Path to SQLite database file, or ":memory:" for in-memory.
    #[new]
    fn new(db_path: String) -> PyResult<Self> {
        let commerce = RustCommerce::new(&db_path)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to initialize commerce: {}", e)))?;

        Ok(Self {
            inner: Arc::new(Mutex::new(commerce)),
        })
    }

    /// Get the customers API.
    #[getter]
    fn customers(&self) -> Customers {
        Customers {
            commerce: self.inner.clone(),
        }
    }

    /// Get the orders API.
    #[getter]
    fn orders(&self) -> Orders {
        Orders {
            commerce: self.inner.clone(),
        }
    }

    /// Get the products API.
    #[getter]
    fn products(&self) -> Products {
        Products {
            commerce: self.inner.clone(),
        }
    }

    /// Get the inventory API.
    #[getter]
    fn inventory(&self) -> Inventory {
        Inventory {
            commerce: self.inner.clone(),
        }
    }

    /// Get the returns API.
    #[getter]
    fn returns(&self) -> Returns {
        Returns {
            commerce: self.inner.clone(),
        }
    }

    /// Get the payments API.
    #[getter]
    fn payments(&self) -> Payments {
        Payments {
            commerce: self.inner.clone(),
        }
    }

    /// Get the shipments API.
    #[getter]
    fn shipments(&self) -> Shipments {
        Shipments {
            commerce: self.inner.clone(),
        }
    }

    /// Get the warranties API.
    #[getter]
    fn warranties(&self) -> Warranties {
        Warranties {
            commerce: self.inner.clone(),
        }
    }

    /// Get the purchase orders API.
    #[getter]
    fn purchase_orders(&self) -> PurchaseOrders {
        PurchaseOrders {
            commerce: self.inner.clone(),
        }
    }

    /// Get the invoices API.
    #[getter]
    fn invoices(&self) -> Invoices {
        Invoices {
            commerce: self.inner.clone(),
        }
    }

    /// Get the bill of materials API.
    #[getter]
    fn bom(&self) -> BomApi {
        BomApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the work orders API.
    #[getter]
    fn work_orders(&self) -> WorkOrders {
        WorkOrders {
            commerce: self.inner.clone(),
        }
    }

    /// Get the carts API.
    #[getter]
    fn carts(&self) -> Carts {
        Carts {
            commerce: self.inner.clone(),
        }
    }

    /// Get the analytics API.
    #[getter]
    fn analytics(&self) -> Analytics {
        Analytics {
            commerce: self.inner.clone(),
        }
    }

    /// Get the currency API.
    #[getter]
    fn currency(&self) -> CurrencyOperations {
        CurrencyOperations {
            commerce: self.inner.clone(),
        }
    }

    /// Get the subscriptions API.
    #[getter]
    fn subscriptions(&self) -> Subscriptions {
        Subscriptions {
            commerce: self.inner.clone(),
        }
    }

    /// Get the promotions API.
    #[getter]
    fn promotions(&self) -> PromotionsApi {
        PromotionsApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the tax API.
    #[getter]
    fn tax(&self) -> TaxApi {
        TaxApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the quality control API.
    #[getter]
    fn quality(&self) -> QualityApi {
        QualityApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the lots/batch tracking API.
    #[getter]
    fn lots(&self) -> LotsApi {
        LotsApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the serial numbers API.
    #[getter]
    fn serials(&self) -> SerialsApi {
        SerialsApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the warehouse API.
    #[getter]
    fn warehouse(&self) -> WarehouseApi {
        WarehouseApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the receiving API.
    #[getter]
    fn receiving(&self) -> ReceivingApi {
        ReceivingApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the fulfillment API.
    #[getter]
    fn fulfillment(&self) -> FulfillmentApi {
        FulfillmentApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the accounts payable API.
    #[getter]
    fn accounts_payable(&self) -> AccountsPayableApi {
        AccountsPayableApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the accounts receivable API.
    #[getter]
    fn accounts_receivable(&self) -> AccountsReceivableApi {
        AccountsReceivableApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the cost accounting API.
    #[getter]
    fn cost_accounting(&self) -> CostAccountingApi {
        CostAccountingApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the credit management API.
    #[getter]
    fn credit(&self) -> CreditApi {
        CreditApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the backorder management API.
    #[getter]
    fn backorder(&self) -> BackorderApi {
        BackorderApi {
            commerce: self.inner.clone(),
        }
    }

    /// Get the general ledger API.
    #[getter]
    fn general_ledger(&self) -> GeneralLedgerApi {
        GeneralLedgerApi {
            commerce: self.inner.clone(),
        }
    }
}

// ============================================================================
// Customer Types
// ============================================================================

/// Customer data returned from operations.
#[pyclass]
#[derive(Clone)]
pub struct Customer {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    email: String,
    #[pyo3(get)]
    first_name: String,
    #[pyo3(get)]
    last_name: String,
    #[pyo3(get)]
    phone: Option<String>,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    accepts_marketing: bool,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

#[pymethods]
impl Customer {
    fn __repr__(&self) -> String {
        format!(
            "Customer(id='{}', email='{}', name='{} {}')",
            self.id, self.email, self.first_name, self.last_name
        )
    }

    /// Get the full name.
    #[getter]
    fn full_name(&self) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

impl From<stateset_core::Customer> for Customer {
    fn from(c: stateset_core::Customer) -> Self {
        Self {
            id: c.id.to_string(),
            email: c.email,
            first_name: c.first_name,
            last_name: c.last_name,
            phone: c.phone,
            status: format!("{}", c.status),
            accepts_marketing: c.accepts_marketing,
            created_at: c.created_at.to_rfc3339(),
            updated_at: c.updated_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Customers API
// ============================================================================

/// Customer management operations.
#[pyclass]
pub struct Customers {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Customers {
    /// Create a new customer.
    ///
    /// Args:
    ///     email: Customer email address (required)
    ///     first_name: First name (required)
    ///     last_name: Last name (required)
    ///     phone: Phone number (optional)
    ///     accepts_marketing: Marketing opt-in (optional, default False)
    ///
    /// Returns:
    ///     Customer: The created customer
    #[pyo3(signature = (email, first_name, last_name, phone=None, accepts_marketing=None))]
    fn create(
        &self,
        email: String,
        first_name: String,
        last_name: String,
        phone: Option<String>,
        accepts_marketing: Option<bool>,
    ) -> PyResult<Customer> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let customer = commerce
            .customers()
            .create(stateset_core::CreateCustomer {
                email,
                first_name,
                last_name,
                phone,
                accepts_marketing,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create customer: {}", e)))?;

        Ok(customer.into())
    }

    /// Get a customer by ID.
    ///
    /// Args:
    ///     id: Customer UUID
    ///
    /// Returns:
    ///     Customer or None if not found
    fn get(&self, id: String) -> PyResult<Option<Customer>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let customer = commerce
            .customers()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get customer: {}", e)))?;

        Ok(customer.map(|c| c.into()))
    }

    /// Get a customer by email.
    ///
    /// Args:
    ///     email: Customer email address
    ///
    /// Returns:
    ///     Customer or None if not found
    fn get_by_email(&self, email: String) -> PyResult<Option<Customer>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let customer = commerce
            .customers()
            .get_by_email(&email)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get customer: {}", e)))?;

        Ok(customer.map(|c| c.into()))
    }

    /// List all customers.
    ///
    /// Returns:
    ///     List[Customer]: All customers
    fn list(&self) -> PyResult<Vec<Customer>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let customers = commerce
            .customers()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list customers: {}", e)))?;

        Ok(customers.into_iter().map(|c| c.into()).collect())
    }

    /// Count customers.
    ///
    /// Returns:
    ///     int: Number of customers
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .customers()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count customers: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Order Types
// ============================================================================

/// Order line item.
#[pyclass]
#[derive(Clone)]
pub struct OrderItem {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    quantity: i32,
    #[pyo3(get)]
    unit_price: f64,
    #[pyo3(get)]
    total: f64,
}

#[pymethods]
impl OrderItem {
    fn __repr__(&self) -> String {
        format!("OrderItem(sku='{}', qty={}, price={})", self.sku, self.quantity, self.unit_price)
    }
}

/// Order data returned from operations.
#[pyclass]
#[derive(Clone)]
pub struct Order {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    order_number: String,
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    total_amount: f64,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    payment_status: String,
    #[pyo3(get)]
    fulfillment_status: String,
    #[pyo3(get)]
    tracking_number: Option<String>,
    #[pyo3(get)]
    items: Vec<OrderItem>,
    #[pyo3(get)]
    version: i32,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

#[pymethods]
impl Order {
    fn __repr__(&self) -> String {
        format!(
            "Order(number='{}', status='{}', total={} {})",
            self.order_number, self.status, self.total_amount, self.currency
        )
    }

    /// Get the number of items in the order.
    #[getter]
    fn item_count(&self) -> usize {
        self.items.len()
    }
}

impl From<stateset_core::Order> for Order {
    fn from(o: stateset_core::Order) -> Self {
        Self {
            id: o.id.to_string(),
            order_number: o.order_number,
            customer_id: o.customer_id.to_string(),
            status: format!("{}", o.status),
            total_amount: to_f64_or_nan(o.total_amount),
            currency: o.currency,
            payment_status: format!("{}", o.payment_status),
            fulfillment_status: format!("{}", o.fulfillment_status),
            tracking_number: o.tracking_number,
            items: o
                .items
                .into_iter()
                .map(|i| OrderItem {
                    id: i.id.to_string(),
                    sku: i.sku,
                    name: i.name,
                    quantity: i.quantity,
                    unit_price: to_f64_or_nan(i.unit_price),
                    total: to_f64_or_nan(i.total),
                })
                .collect(),
            version: o.version,
            created_at: o.created_at.to_rfc3339(),
            updated_at: o.updated_at.to_rfc3339(),
        }
    }
}

/// Input for creating an order item.
#[pyclass]
#[derive(Clone)]
pub struct CreateOrderItemInput {
    #[pyo3(get, set)]
    sku: String,
    #[pyo3(get, set)]
    name: String,
    #[pyo3(get, set)]
    quantity: i32,
    #[pyo3(get, set)]
    unit_price: f64,
    #[pyo3(get, set)]
    product_id: Option<String>,
    #[pyo3(get, set)]
    variant_id: Option<String>,
}

#[pymethods]
impl CreateOrderItemInput {
    #[new]
    #[pyo3(signature = (sku, name, quantity, unit_price, product_id=None, variant_id=None))]
    fn new(
        sku: String,
        name: String,
        quantity: i32,
        unit_price: f64,
        product_id: Option<String>,
        variant_id: Option<String>,
    ) -> Self {
        Self {
            sku,
            name,
            quantity,
            unit_price,
            product_id,
            variant_id,
        }
    }
}

// ============================================================================
// Orders API
// ============================================================================

/// Order management operations.
#[pyclass]
pub struct Orders {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Orders {
    /// Create a new order.
    ///
    /// Args:
    ///     customer_id: Customer UUID
    ///     items: List of CreateOrderItemInput
    ///     currency: Currency code (default "USD")
    ///     notes: Order notes (optional)
    ///
    /// Returns:
    ///     Order: The created order
    #[pyo3(signature = (customer_id, items, currency=None, notes=None))]
    fn create(
        &self,
        customer_id: String,
        items: Vec<CreateOrderItemInput>,
        currency: Option<String>,
        notes: Option<String>,
    ) -> PyResult<Order> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let cust_uuid = customer_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;

        let order_items: Vec<stateset_core::CreateOrderItem> = items
            .into_iter()
            .map(|i| {
                let product_id = i.product_id.and_then(|s| s.parse().ok()).unwrap_or_default();
                let variant_id = i.variant_id.and_then(|s| s.parse().ok());

                stateset_core::CreateOrderItem {
                    product_id,
                    variant_id,
                    sku: i.sku,
                    name: i.name,
                    quantity: i.quantity,
                    unit_price: Decimal::from_f64_retain(i.unit_price).unwrap_or_default(),
                    ..Default::default()
                }
            })
            .collect();

        let order = commerce
            .orders()
            .create(stateset_core::CreateOrder {
                customer_id: cust_uuid,
                items: order_items,
                currency,
                notes,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create order: {}", e)))?;

        Ok(order.into())
    }

    /// Get an order by ID.
    ///
    /// Args:
    ///     id: Order UUID
    ///
    /// Returns:
    ///     Order or None if not found
    fn get(&self, id: String) -> PyResult<Option<Order>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let order = commerce
            .orders()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get order: {}", e)))?;

        Ok(order.map(|o| o.into()))
    }

    /// List all orders.
    ///
    /// Returns:
    ///     List[Order]: All orders
    fn list(&self) -> PyResult<Vec<Order>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let orders = commerce
            .orders()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list orders: {}", e)))?;

        Ok(orders.into_iter().map(|o| o.into()).collect())
    }

    /// Update order status.
    ///
    /// Args:
    ///     id: Order UUID
    ///     status: New status (pending, confirmed, processing, shipped, delivered, cancelled, refunded)
    ///
    /// Returns:
    ///     Order: The updated order
    fn update_status(&self, id: String, status: String) -> PyResult<Order> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let order_status = match status.to_lowercase().as_str() {
            "pending" => stateset_core::OrderStatus::Pending,
            "confirmed" => stateset_core::OrderStatus::Confirmed,
            "processing" => stateset_core::OrderStatus::Processing,
            "shipped" => stateset_core::OrderStatus::Shipped,
            "delivered" => stateset_core::OrderStatus::Delivered,
            "cancelled" => stateset_core::OrderStatus::Cancelled,
            "refunded" => stateset_core::OrderStatus::Refunded,
            _ => return Err(PyValueError::new_err(format!("Invalid status: {}", status))),
        };

        let order = commerce
            .orders()
            .update_status(uuid, order_status)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to update order: {}", e)))?;

        Ok(order.into())
    }

    /// Ship an order.
    ///
    /// Args:
    ///     id: Order UUID
    ///     tracking_number: Tracking number (optional)
    ///
    /// Returns:
    ///     Order: The shipped order
    #[pyo3(signature = (id, tracking_number=None))]
    fn ship(&self, id: String, tracking_number: Option<String>) -> PyResult<Order> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let order = commerce
            .orders()
            .ship(uuid, tracking_number.as_deref())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to ship order: {}", e)))?;

        Ok(order.into())
    }

    /// Cancel an order.
    ///
    /// Args:
    ///     id: Order UUID
    ///
    /// Returns:
    ///     Order: The cancelled order
    fn cancel(&self, id: String) -> PyResult<Order> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let order = commerce
            .orders()
            .cancel(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to cancel order: {}", e)))?;

        Ok(order.into())
    }

    /// Count orders.
    ///
    /// Returns:
    ///     int: Number of orders
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .orders()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count orders: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Product Types
// ============================================================================

/// Product data returned from operations.
#[pyclass]
#[derive(Clone)]
pub struct Product {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    slug: String,
    #[pyo3(get)]
    description: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

#[pymethods]
impl Product {
    fn __repr__(&self) -> String {
        format!("Product(name='{}', slug='{}', status='{}')", self.name, self.slug, self.status)
    }
}

impl From<stateset_core::Product> for Product {
    fn from(p: stateset_core::Product) -> Self {
        Self {
            id: p.id.to_string(),
            name: p.name,
            slug: p.slug,
            description: p.description,
            status: format!("{}", p.status),
            created_at: p.created_at.to_rfc3339(),
            updated_at: p.updated_at.to_rfc3339(),
        }
    }
}

/// Product variant data.
#[pyclass]
#[derive(Clone)]
pub struct ProductVariant {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    product_id: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    price: f64,
    #[pyo3(get)]
    compare_at_price: Option<f64>,
    #[pyo3(get)]
    is_default: bool,
}

#[pymethods]
impl ProductVariant {
    fn __repr__(&self) -> String {
        format!("ProductVariant(sku='{}', price={})", self.sku, self.price)
    }
}

impl From<stateset_core::ProductVariant> for ProductVariant {
    fn from(v: stateset_core::ProductVariant) -> Self {
        Self {
            id: v.id.to_string(),
            product_id: v.product_id.to_string(),
            sku: v.sku,
            name: v.name,
            price: to_f64_or_nan(v.price),
            compare_at_price: v.compare_at_price.map(|d| to_f64_or_nan(d)),
            is_default: v.is_default,
        }
    }
}

/// Input for creating a product variant.
#[pyclass]
#[derive(Clone)]
pub struct CreateProductVariantInput {
    #[pyo3(get, set)]
    sku: String,
    #[pyo3(get, set)]
    name: Option<String>,
    #[pyo3(get, set)]
    price: f64,
    #[pyo3(get, set)]
    compare_at_price: Option<f64>,
}

#[pymethods]
impl CreateProductVariantInput {
    #[new]
    #[pyo3(signature = (sku, price, name=None, compare_at_price=None))]
    fn new(sku: String, price: f64, name: Option<String>, compare_at_price: Option<f64>) -> Self {
        Self {
            sku,
            name,
            price,
            compare_at_price,
        }
    }
}

// ============================================================================
// Products API
// ============================================================================

/// Product catalog operations.
#[pyclass]
pub struct Products {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Products {
    /// Create a new product.
    ///
    /// Args:
    ///     name: Product name
    ///     description: Product description (optional)
    ///     variants: List of CreateProductVariantInput (optional)
    ///
    /// Returns:
    ///     Product: The created product
    #[pyo3(signature = (name, description=None, variants=None))]
    fn create(
        &self,
        name: String,
        description: Option<String>,
        variants: Option<Vec<CreateProductVariantInput>>,
    ) -> PyResult<Product> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let variant_inputs = variants.map(|vs| {
            vs.into_iter()
                .map(|v| stateset_core::CreateProductVariant {
                    sku: v.sku,
                    name: v.name,
                    price: Decimal::from_f64_retain(v.price).unwrap_or_default(),
                    compare_at_price: v.compare_at_price.and_then(|p| Decimal::from_f64_retain(p)),
                    ..Default::default()
                })
                .collect()
        });

        let product = commerce
            .products()
            .create(stateset_core::CreateProduct {
                name,
                description,
                variants: variant_inputs,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create product: {}", e)))?;

        Ok(product.into())
    }

    /// Get a product by ID.
    ///
    /// Args:
    ///     id: Product UUID
    ///
    /// Returns:
    ///     Product or None if not found
    fn get(&self, id: String) -> PyResult<Option<Product>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let product = commerce
            .products()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get product: {}", e)))?;

        Ok(product.map(|p| p.into()))
    }

    /// Get a product variant by SKU.
    ///
    /// Args:
    ///     sku: Product variant SKU
    ///
    /// Returns:
    ///     ProductVariant or None if not found
    fn get_variant_by_sku(&self, sku: String) -> PyResult<Option<ProductVariant>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let variant = commerce
            .products()
            .get_variant_by_sku(&sku)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get variant: {}", e)))?;

        Ok(variant.map(|v| v.into()))
    }

    /// List all products.
    ///
    /// Returns:
    ///     List[Product]: All products
    fn list(&self) -> PyResult<Vec<Product>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let products = commerce
            .products()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list products: {}", e)))?;

        Ok(products.into_iter().map(|p| p.into()).collect())
    }

    /// Count products.
    ///
    /// Returns:
    ///     int: Number of products
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .products()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count products: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Inventory Types
// ============================================================================

/// Inventory item data.
#[pyclass]
#[derive(Clone)]
pub struct InventoryItem {
    #[pyo3(get)]
    id: i64,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    description: Option<String>,
    #[pyo3(get)]
    unit_of_measure: String,
    #[pyo3(get)]
    is_active: bool,
}

#[pymethods]
impl InventoryItem {
    fn __repr__(&self) -> String {
        format!("InventoryItem(sku='{}', name='{}')", self.sku, self.name)
    }
}

impl From<stateset_core::InventoryItem> for InventoryItem {
    fn from(i: stateset_core::InventoryItem) -> Self {
        Self {
            id: i.id,
            sku: i.sku,
            name: i.name,
            description: i.description,
            unit_of_measure: i.unit_of_measure,
            is_active: i.is_active,
        }
    }
}

/// Stock level information.
#[pyclass]
#[derive(Clone)]
pub struct StockLevel {
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    total_on_hand: f64,
    #[pyo3(get)]
    total_allocated: f64,
    #[pyo3(get)]
    total_available: f64,
}

#[pymethods]
impl StockLevel {
    fn __repr__(&self) -> String {
        format!(
            "StockLevel(sku='{}', available={})",
            self.sku, self.total_available
        )
    }
}

impl From<stateset_core::StockLevel> for StockLevel {
    fn from(s: stateset_core::StockLevel) -> Self {
        Self {
            sku: s.sku,
            name: s.name,
            total_on_hand: to_f64_or_nan(s.total_on_hand),
            total_allocated: to_f64_or_nan(s.total_allocated),
            total_available: to_f64_or_nan(s.total_available),
        }
    }
}

/// Inventory reservation.
#[pyclass]
#[derive(Clone)]
pub struct Reservation {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    item_id: i64,
    #[pyo3(get)]
    quantity: f64,
    #[pyo3(get)]
    status: String,
}

#[pymethods]
impl Reservation {
    fn __repr__(&self) -> String {
        format!("Reservation(id='{}', qty={}, status='{}')", self.id, self.quantity, self.status)
    }
}

impl From<stateset_core::InventoryReservation> for Reservation {
    fn from(r: stateset_core::InventoryReservation) -> Self {
        Self {
            id: r.id.to_string(),
            item_id: r.item_id,
            quantity: to_f64_or_nan(r.quantity),
            status: format!("{}", r.status),
        }
    }
}

// ============================================================================
// Inventory API
// ============================================================================

/// Inventory management operations.
#[pyclass]
pub struct Inventory {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Inventory {
    /// Create a new inventory item.
    ///
    /// Args:
    ///     sku: Stock keeping unit
    ///     name: Item name
    ///     description: Item description (optional)
    ///     initial_quantity: Starting quantity (optional, default 0)
    ///     reorder_point: Reorder alert threshold (optional)
    ///
    /// Returns:
    ///     InventoryItem: The created item
    #[pyo3(signature = (sku, name, description=None, initial_quantity=None, reorder_point=None))]
    fn create_item(
        &self,
        sku: String,
        name: String,
        description: Option<String>,
        initial_quantity: Option<f64>,
        reorder_point: Option<f64>,
    ) -> PyResult<InventoryItem> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let item = commerce
            .inventory()
            .create_item(stateset_core::CreateInventoryItem {
                sku,
                name,
                description,
                initial_quantity: initial_quantity.and_then(|q| Decimal::from_f64_retain(q)),
                reorder_point: reorder_point.and_then(|r| Decimal::from_f64_retain(r)),
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create inventory item: {}", e)))?;

        Ok(item.into())
    }

    /// Get stock level for a SKU.
    ///
    /// Args:
    ///     sku: Stock keeping unit
    ///
    /// Returns:
    ///     StockLevel or None if not found
    fn get_stock(&self, sku: String) -> PyResult<Option<StockLevel>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let stock = commerce
            .inventory()
            .get_stock(&sku)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get stock: {}", e)))?;

        Ok(stock.map(|s| s.into()))
    }

    /// Adjust inventory quantity.
    ///
    /// Args:
    ///     sku: Stock keeping unit
    ///     quantity: Quantity to add (positive) or remove (negative)
    ///     reason: Reason for adjustment
    fn adjust(&self, sku: String, quantity: f64, reason: String) -> PyResult<()> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let qty = Decimal::from_f64_retain(quantity)
            .ok_or_else(|| PyValueError::new_err("Invalid quantity"))?;

        commerce
            .inventory()
            .adjust(&sku, qty, &reason)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to adjust inventory: {}", e)))?;

        Ok(())
    }

    /// Reserve inventory for an order.
    ///
    /// Args:
    ///     sku: Stock keeping unit
    ///     quantity: Quantity to reserve
    ///     reference_type: Type of reference (e.g., "order")
    ///     reference_id: Reference identifier
    ///     expires_in_seconds: Reservation expiry time (optional)
    ///
    /// Returns:
    ///     Reservation: The created reservation
    #[pyo3(signature = (sku, quantity, reference_type, reference_id, expires_in_seconds=None))]
    fn reserve(
        &self,
        sku: String,
        quantity: f64,
        reference_type: String,
        reference_id: String,
        expires_in_seconds: Option<i64>,
    ) -> PyResult<Reservation> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let qty = Decimal::from_f64_retain(quantity)
            .ok_or_else(|| PyValueError::new_err("Invalid quantity"))?;

        let reservation = commerce
            .inventory()
            .reserve(&sku, qty, &reference_type, &reference_id, expires_in_seconds)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to reserve inventory: {}", e)))?;

        Ok(reservation.into())
    }

    /// Confirm a reservation (deducts from on-hand).
    ///
    /// Args:
    ///     reservation_id: Reservation UUID
    fn confirm_reservation(&self, reservation_id: String) -> PyResult<()> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = reservation_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        commerce
            .inventory()
            .confirm_reservation(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to confirm reservation: {}", e)))?;

        Ok(())
    }

    /// Release a reservation (returns to available).
    ///
    /// Args:
    ///     reservation_id: Reservation UUID
    fn release_reservation(&self, reservation_id: String) -> PyResult<()> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = reservation_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        commerce
            .inventory()
            .release_reservation(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to release reservation: {}", e)))?;

        Ok(())
    }
}

// ============================================================================
// Return Types
// ============================================================================

/// Return request data.
#[pyclass]
#[derive(Clone)]
pub struct Return {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    order_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    reason: String,
    #[pyo3(get)]
    version: i32,
    #[pyo3(get)]
    created_at: String,
}

#[pymethods]
impl Return {
    fn __repr__(&self) -> String {
        format!("Return(id='{}', status='{}', reason='{}')", self.id, self.status, self.reason)
    }
}

impl From<stateset_core::Return> for Return {
    fn from(r: stateset_core::Return) -> Self {
        Self {
            id: r.id.to_string(),
            order_id: r.order_id.to_string(),
            status: format!("{}", r.status),
            reason: format!("{}", r.reason),
            version: r.version,
            created_at: r.created_at.to_rfc3339(),
        }
    }
}

/// Input for creating a return item.
#[pyclass]
#[derive(Clone)]
pub struct CreateReturnItemInput {
    #[pyo3(get, set)]
    order_item_id: String,
    #[pyo3(get, set)]
    quantity: i32,
}

#[pymethods]
impl CreateReturnItemInput {
    #[new]
    fn new(order_item_id: String, quantity: i32) -> Self {
        Self {
            order_item_id,
            quantity,
        }
    }
}

// ============================================================================
// Returns API
// ============================================================================

/// Return processing operations.
#[pyclass]
pub struct Returns {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Returns {
    /// Create a new return request.
    ///
    /// Args:
    ///     order_id: Order UUID
    ///     reason: Return reason (defective, not_as_described, wrong_item, etc.)
    ///     items: List of CreateReturnItemInput
    ///     reason_details: Additional details (optional)
    ///
    /// Returns:
    ///     Return: The created return
    #[pyo3(signature = (order_id, reason, items, reason_details=None))]
    fn create(
        &self,
        order_id: String,
        reason: String,
        items: Vec<CreateReturnItemInput>,
        reason_details: Option<String>,
    ) -> PyResult<Return> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let ord_uuid = order_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid order UUID"))?;

        let return_reason = match reason.to_lowercase().as_str() {
            "defective" => stateset_core::ReturnReason::Defective,
            "not_as_described" => stateset_core::ReturnReason::NotAsDescribed,
            "wrong_item" => stateset_core::ReturnReason::WrongItem,
            "no_longer_needed" => stateset_core::ReturnReason::NoLongerNeeded,
            "changed_mind" => stateset_core::ReturnReason::ChangedMind,
            "better_price_found" => stateset_core::ReturnReason::BetterPriceFound,
            "damaged" => stateset_core::ReturnReason::Damaged,
            _ => stateset_core::ReturnReason::Other,
        };

        let return_items: Vec<stateset_core::CreateReturnItem> = items
            .into_iter()
            .map(|i| {
                let order_item_id = i.order_item_id.parse().unwrap_or_default();
                stateset_core::CreateReturnItem {
                    order_item_id,
                    quantity: i.quantity,
                    ..Default::default()
                }
            })
            .collect();

        let ret = commerce
            .returns()
            .create(stateset_core::CreateReturn {
                order_id: ord_uuid,
                reason: return_reason,
                reason_details,
                items: return_items,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create return: {}", e)))?;

        Ok(ret.into())
    }

    /// Get a return by ID.
    ///
    /// Args:
    ///     id: Return UUID
    ///
    /// Returns:
    ///     Return or None if not found
    fn get(&self, id: String) -> PyResult<Option<Return>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let ret = commerce
            .returns()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get return: {}", e)))?;

        Ok(ret.map(|r| r.into()))
    }

    /// Approve a return request.
    ///
    /// Args:
    ///     id: Return UUID
    ///
    /// Returns:
    ///     Return: The approved return
    fn approve(&self, id: String) -> PyResult<Return> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let ret = commerce
            .returns()
            .approve(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to approve return: {}", e)))?;

        Ok(ret.into())
    }

    /// Reject a return request.
    ///
    /// Args:
    ///     id: Return UUID
    ///     reason: Rejection reason
    ///
    /// Returns:
    ///     Return: The rejected return
    fn reject(&self, id: String, reason: String) -> PyResult<Return> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let ret = commerce
            .returns()
            .reject(uuid, &reason)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to reject return: {}", e)))?;

        Ok(ret.into())
    }

    /// List all returns.
    ///
    /// Returns:
    ///     List[Return]: All returns
    fn list(&self) -> PyResult<Vec<Return>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let returns = commerce
            .returns()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list returns: {}", e)))?;

        Ok(returns.into_iter().map(|r| r.into()).collect())
    }

    /// Count returns.
    ///
    /// Returns:
    ///     int: Number of returns
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .returns()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count returns: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Payment Types
// ============================================================================

/// Payment data returned from operations.
#[pyclass]
#[derive(Clone)]
pub struct Payment {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    payment_number: String,
    #[pyo3(get)]
    order_id: Option<String>,
    #[pyo3(get)]
    invoice_id: Option<String>,
    #[pyo3(get)]
    customer_id: Option<String>,
    #[pyo3(get)]
    amount: f64,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    payment_method: String,
    #[pyo3(get)]
    version: i32,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

#[pymethods]
impl Payment {
    fn __repr__(&self) -> String {
        format!(
            "Payment(number='{}', amount={} {}, status='{}')",
            self.payment_number, self.amount, self.currency, self.status
        )
    }
}

impl From<stateset_core::Payment> for Payment {
    fn from(p: stateset_core::Payment) -> Self {
        Self {
            id: p.id.to_string(),
            payment_number: p.payment_number,
            order_id: p.order_id.map(|id| id.to_string()),
            invoice_id: p.invoice_id.map(|id| id.to_string()),
            customer_id: p.customer_id.map(|id| id.to_string()),
            amount: to_f64_or_nan(p.amount),
            currency: p.currency,
            status: format!("{}", p.status),
            payment_method: format!("{}", p.payment_method),
            version: p.version,
            created_at: p.created_at.to_rfc3339(),
            updated_at: p.updated_at.to_rfc3339(),
        }
    }
}

/// Refund data returned from operations.
#[pyclass]
#[derive(Clone)]
pub struct Refund {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    payment_id: String,
    #[pyo3(get)]
    amount: f64,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    reason: Option<String>,
    #[pyo3(get)]
    created_at: String,
}

#[pymethods]
impl Refund {
    fn __repr__(&self) -> String {
        format!("Refund(id='{}', amount={}, status='{}')", self.id, self.amount, self.status)
    }
}

impl From<stateset_core::Refund> for Refund {
    fn from(r: stateset_core::Refund) -> Self {
        Self {
            id: r.id.to_string(),
            payment_id: r.payment_id.to_string(),
            amount: to_f64_or_nan(r.amount),
            status: format!("{}", r.status),
            reason: r.reason,
            created_at: r.created_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Payments API
// ============================================================================

/// Payment processing operations.
#[pyclass]
pub struct Payments {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Payments {
    /// Create a new payment.
    ///
    /// Args:
    ///     amount: Payment amount
    ///     currency: Currency code (default "USD")
    ///     order_id: Associated order UUID (optional)
    ///     customer_id: Customer UUID (optional)
    ///     payment_method: Payment method type (optional)
    ///
    /// Returns:
    ///     Payment: The created payment
    #[pyo3(signature = (amount, currency=None, order_id=None, customer_id=None, payment_method=None))]
    fn create(
        &self,
        amount: f64,
        currency: Option<String>,
        order_id: Option<String>,
        customer_id: Option<String>,
        payment_method: Option<String>,
    ) -> PyResult<Payment> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let order_uuid = order_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid order UUID"))?;

        let customer_uuid = customer_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;

        let method = payment_method.map(|m| match m.to_lowercase().as_str() {
            "credit_card" => stateset_core::PaymentMethodType::CreditCard,
            "debit_card" => stateset_core::PaymentMethodType::DebitCard,
            "bank_transfer" => stateset_core::PaymentMethodType::BankTransfer,
            "paypal" => stateset_core::PaymentMethodType::PayPal,
            "crypto" => stateset_core::PaymentMethodType::Crypto,
            _ => stateset_core::PaymentMethodType::CreditCard,
        }).unwrap_or(stateset_core::PaymentMethodType::CreditCard);

        let payment = commerce
            .payments()
            .create(stateset_core::CreatePayment {
                order_id: order_uuid,
                customer_id: customer_uuid,
                amount: Decimal::from_f64_retain(amount).unwrap_or_default(),
                currency,
                payment_method: method,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create payment: {}", e)))?;

        Ok(payment.into())
    }

    /// Get a payment by ID.
    fn get(&self, id: String) -> PyResult<Option<Payment>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let payment = commerce
            .payments()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get payment: {}", e)))?;

        Ok(payment.map(|p| p.into()))
    }

    /// List all payments.
    fn list(&self) -> PyResult<Vec<Payment>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let payments = commerce
            .payments()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list payments: {}", e)))?;

        Ok(payments.into_iter().map(|p| p.into()).collect())
    }

    /// Mark payment as completed.
    fn complete(&self, id: String) -> PyResult<Payment> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let payment = commerce
            .payments()
            .mark_completed(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to complete payment: {}", e)))?;

        Ok(payment.into())
    }

    /// Mark payment as failed.
    #[pyo3(signature = (id, reason, code=None))]
    fn mark_failed(&self, id: String, reason: String, code: Option<String>) -> PyResult<Payment> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let payment = commerce
            .payments()
            .mark_failed(uuid, &reason, code.as_deref())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to fail payment: {}", e)))?;

        Ok(payment.into())
    }

    /// Create a refund for a payment.
    #[pyo3(signature = (payment_id, amount, reason=None))]
    fn create_refund(&self, payment_id: String, amount: f64, reason: Option<String>) -> PyResult<Refund> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = payment_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid payment UUID"))?;

        let refund = commerce
            .payments()
            .create_refund(stateset_core::CreateRefund {
                payment_id: uuid,
                amount: Some(Decimal::from_f64_retain(amount).unwrap_or_default()),
                reason,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create refund: {}", e)))?;

        Ok(refund.into())
    }

    /// Count payments.
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .payments()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count payments: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Shipment Types
// ============================================================================

/// Shipment data returned from operations.
#[pyclass]
#[derive(Clone)]
pub struct Shipment {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    shipment_number: String,
    #[pyo3(get)]
    order_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    carrier: String,
    #[pyo3(get)]
    shipping_method: String,
    #[pyo3(get)]
    tracking_number: Option<String>,
    #[pyo3(get)]
    tracking_url: Option<String>,
    #[pyo3(get)]
    recipient_name: String,
    #[pyo3(get)]
    shipping_address: String,
    #[pyo3(get)]
    version: i32,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

#[pymethods]
impl Shipment {
    fn __repr__(&self) -> String {
        format!(
            "Shipment(number='{}', status='{}', carrier='{}')",
            self.shipment_number, self.status, self.carrier
        )
    }
}

impl From<stateset_core::Shipment> for Shipment {
    fn from(s: stateset_core::Shipment) -> Self {
        Self {
            id: s.id.to_string(),
            shipment_number: s.shipment_number,
            order_id: s.order_id.to_string(),
            status: format!("{}", s.status),
            carrier: format!("{}", s.carrier),
            shipping_method: format!("{}", s.shipping_method),
            tracking_number: s.tracking_number,
            tracking_url: s.tracking_url,
            recipient_name: s.recipient_name,
            shipping_address: s.shipping_address,
            version: s.version,
            created_at: s.created_at.to_rfc3339(),
            updated_at: s.updated_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Shipments API
// ============================================================================

/// Shipment management operations.
#[pyclass]
pub struct Shipments {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Shipments {
    /// Create a new shipment.
    #[pyo3(signature = (order_id, recipient_name, shipping_address, carrier=None, shipping_method=None, tracking_number=None))]
    fn create(
        &self,
        order_id: String,
        recipient_name: String,
        shipping_address: String,
        carrier: Option<String>,
        shipping_method: Option<String>,
        tracking_number: Option<String>,
    ) -> PyResult<Shipment> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let order_uuid = order_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid order UUID"))?;

        let carrier_type = carrier.and_then(|c| match c.to_lowercase().as_str() {
            "ups" => Some(stateset_core::ShippingCarrier::Ups),
            "fedex" => Some(stateset_core::ShippingCarrier::FedEx),
            "usps" => Some(stateset_core::ShippingCarrier::Usps),
            "dhl" => Some(stateset_core::ShippingCarrier::Dhl),
            _ => Some(stateset_core::ShippingCarrier::Other),
        });

        let method = shipping_method.and_then(|m| match m.to_lowercase().as_str() {
            "standard" => Some(stateset_core::ShippingMethod::Standard),
            "express" => Some(stateset_core::ShippingMethod::Express),
            "overnight" => Some(stateset_core::ShippingMethod::Overnight),
            "ground" => Some(stateset_core::ShippingMethod::Ground),
            _ => Some(stateset_core::ShippingMethod::Standard),
        });

        let shipment = commerce
            .shipments()
            .create(stateset_core::CreateShipment {
                order_id: order_uuid,
                recipient_name,
                shipping_address,
                carrier: carrier_type,
                shipping_method: method,
                tracking_number,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create shipment: {}", e)))?;

        Ok(shipment.into())
    }

    /// Get a shipment by ID.
    fn get(&self, id: String) -> PyResult<Option<Shipment>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let shipment = commerce
            .shipments()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get shipment: {}", e)))?;

        Ok(shipment.map(|s| s.into()))
    }

    /// List all shipments.
    fn list(&self) -> PyResult<Vec<Shipment>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let shipments = commerce
            .shipments()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list shipments: {}", e)))?;

        Ok(shipments.into_iter().map(|s| s.into()).collect())
    }

    /// Ship a shipment with optional tracking number.
    #[pyo3(signature = (id, tracking_number=None))]
    fn ship(&self, id: String, tracking_number: Option<String>) -> PyResult<Shipment> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let shipment = commerce
            .shipments()
            .ship(uuid, tracking_number)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to ship: {}", e)))?;

        Ok(shipment.into())
    }

    /// Mark shipment as delivered.
    fn mark_delivered(&self, id: String) -> PyResult<Shipment> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let shipment = commerce
            .shipments()
            .mark_delivered(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to deliver: {}", e)))?;

        Ok(shipment.into())
    }

    /// Cancel a shipment.
    fn cancel(&self, id: String) -> PyResult<Shipment> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let shipment = commerce
            .shipments()
            .cancel(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to cancel shipment: {}", e)))?;

        Ok(shipment.into())
    }

    /// Count shipments.
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .shipments()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count shipments: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Warranty Types
// ============================================================================

/// Warranty data returned from operations.
#[pyclass]
#[derive(Clone)]
pub struct Warranty {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    warranty_number: String,
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    product_id: Option<String>,
    #[pyo3(get)]
    order_id: Option<String>,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    warranty_type: String,
    #[pyo3(get)]
    start_date: String,
    #[pyo3(get)]
    end_date: String,
    #[pyo3(get)]
    created_at: String,
}

#[pymethods]
impl Warranty {
    fn __repr__(&self) -> String {
        format!(
            "Warranty(number='{}', status='{}', type='{}')",
            self.warranty_number, self.status, self.warranty_type
        )
    }
}

impl From<stateset_core::Warranty> for Warranty {
    fn from(w: stateset_core::Warranty) -> Self {
        Self {
            id: w.id.to_string(),
            warranty_number: w.warranty_number,
            customer_id: w.customer_id.to_string(),
            product_id: w.product_id.map(|id| id.to_string()),
            order_id: w.order_id.map(|id| id.to_string()),
            status: format!("{}", w.status),
            warranty_type: format!("{}", w.warranty_type),
            start_date: w.start_date.to_rfc3339(),
            end_date: w.end_date.map(|d| d.to_rfc3339()).unwrap_or_default(),
            created_at: w.created_at.to_rfc3339(),
        }
    }
}

/// Warranty claim data.
#[pyclass]
#[derive(Clone)]
pub struct WarrantyClaim {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    claim_number: String,
    #[pyo3(get)]
    warranty_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    issue_description: String,
    #[pyo3(get)]
    resolution: String,
    #[pyo3(get)]
    created_at: String,
}

#[pymethods]
impl WarrantyClaim {
    fn __repr__(&self) -> String {
        format!("WarrantyClaim(number='{}', status='{}')", self.claim_number, self.status)
    }
}

impl From<stateset_core::WarrantyClaim> for WarrantyClaim {
    fn from(c: stateset_core::WarrantyClaim) -> Self {
        Self {
            id: c.id.to_string(),
            claim_number: c.claim_number,
            warranty_id: c.warranty_id.to_string(),
            status: format!("{}", c.status),
            issue_description: c.issue_description,
            resolution: format!("{}", c.resolution),
            created_at: c.created_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Warranties API
// ============================================================================

/// Warranty management operations.
#[pyclass]
pub struct Warranties {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Warranties {
    /// Register a new warranty.
    #[pyo3(signature = (customer_id, product_id=None, order_id=None, duration_months=None))]
    fn create(
        &self,
        customer_id: String,
        product_id: Option<String>,
        order_id: Option<String>,
        duration_months: Option<i32>,
    ) -> PyResult<Warranty> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let cust_uuid = customer_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;

        let prod_uuid = product_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid product UUID"))?;

        let order_uuid = order_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid order UUID"))?;

        let warranty = commerce
            .warranties()
            .create(stateset_core::CreateWarranty {
                customer_id: cust_uuid,
                product_id: prod_uuid,
                order_id: order_uuid,
                duration_months,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create warranty: {}", e)))?;

        Ok(warranty.into())
    }

    /// Get a warranty by ID.
    fn get(&self, id: String) -> PyResult<Option<Warranty>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let warranty = commerce
            .warranties()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get warranty: {}", e)))?;

        Ok(warranty.map(|w| w.into()))
    }

    /// List all warranties.
    fn list(&self) -> PyResult<Vec<Warranty>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let warranties = commerce
            .warranties()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list warranties: {}", e)))?;

        Ok(warranties.into_iter().map(|w| w.into()).collect())
    }

    /// File a warranty claim.
    #[pyo3(signature = (warranty_id, issue_description, contact_email=None))]
    fn create_claim(
        &self,
        warranty_id: String,
        issue_description: String,
        contact_email: Option<String>,
    ) -> PyResult<WarrantyClaim> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let warranty_uuid = warranty_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid warranty UUID"))?;

        let claim = commerce
            .warranties()
            .create_claim(stateset_core::CreateWarrantyClaim {
                warranty_id: warranty_uuid,
                issue_description,
                contact_email,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create claim: {}", e)))?;

        Ok(claim.into())
    }

    /// Approve a warranty claim.
    fn approve_claim(&self, id: String) -> PyResult<WarrantyClaim> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let claim = commerce
            .warranties()
            .approve_claim(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to approve claim: {}", e)))?;

        Ok(claim.into())
    }

    /// Deny a warranty claim.
    fn deny_claim(&self, id: String, reason: String) -> PyResult<WarrantyClaim> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let claim = commerce
            .warranties()
            .deny_claim(uuid, &reason)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to deny claim: {}", e)))?;

        Ok(claim.into())
    }

    /// Complete a warranty claim with resolution.
    fn complete_claim(&self, id: String, resolution: String) -> PyResult<WarrantyClaim> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let res = match resolution.to_lowercase().as_str() {
            "repair" => stateset_core::ClaimResolution::Repair,
            "replacement" => stateset_core::ClaimResolution::Replacement,
            "refund" => stateset_core::ClaimResolution::Refund,
            "store_credit" => stateset_core::ClaimResolution::StoreCredit,
            "denied" => stateset_core::ClaimResolution::Denied,
            _ => stateset_core::ClaimResolution::None,
        };

        let claim = commerce
            .warranties()
            .complete_claim(uuid, res)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to complete claim: {}", e)))?;

        Ok(claim.into())
    }

    /// Count warranties.
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .warranties()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count warranties: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Purchase Order Types
// ============================================================================

/// Supplier data.
#[pyclass]
#[derive(Clone)]
pub struct Supplier {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    supplier_code: String,
    #[pyo3(get)]
    email: Option<String>,
    #[pyo3(get)]
    phone: Option<String>,
    #[pyo3(get)]
    is_active: bool,
    #[pyo3(get)]
    created_at: String,
}

#[pymethods]
impl Supplier {
    fn __repr__(&self) -> String {
        format!("Supplier(name='{}', code='{}')", self.name, self.supplier_code)
    }
}

impl From<stateset_core::Supplier> for Supplier {
    fn from(s: stateset_core::Supplier) -> Self {
        Self {
            id: s.id.to_string(),
            name: s.name,
            supplier_code: s.supplier_code,
            email: s.email,
            phone: s.phone,
            is_active: s.is_active,
            created_at: s.created_at.to_rfc3339(),
        }
    }
}

/// Purchase order data.
#[pyclass]
#[derive(Clone)]
pub struct PurchaseOrder {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    po_number: String,
    #[pyo3(get)]
    supplier_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    total_amount: f64,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

#[pymethods]
impl PurchaseOrder {
    fn __repr__(&self) -> String {
        format!("PurchaseOrder(number='{}', status='{}', total={})", self.po_number, self.status, self.total_amount)
    }
}

impl From<stateset_core::PurchaseOrder> for PurchaseOrder {
    fn from(po: stateset_core::PurchaseOrder) -> Self {
        Self {
            id: po.id.to_string(),
            po_number: po.po_number,
            supplier_id: po.supplier_id.to_string(),
            status: format!("{}", po.status),
            total_amount: to_f64_or_nan(po.total),
            created_at: po.created_at.to_rfc3339(),
            updated_at: po.updated_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Purchase Orders API
// ============================================================================

/// Purchase order management operations.
#[pyclass]
pub struct PurchaseOrders {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl PurchaseOrders {
    /// Create a new supplier.
    #[pyo3(signature = (name, email=None, phone=None))]
    fn create_supplier(&self, name: String, email: Option<String>, phone: Option<String>) -> PyResult<Supplier> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let supplier = commerce
            .purchase_orders()
            .create_supplier(stateset_core::CreateSupplier {
                name,
                email,
                phone,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create supplier: {}", e)))?;

        Ok(supplier.into())
    }

    /// Get a supplier by ID.
    fn get_supplier(&self, id: String) -> PyResult<Option<Supplier>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let supplier = commerce
            .purchase_orders()
            .get_supplier(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get supplier: {}", e)))?;

        Ok(supplier.map(|s| s.into()))
    }

    /// List all suppliers.
    fn list_suppliers(&self) -> PyResult<Vec<Supplier>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let suppliers = commerce
            .purchase_orders()
            .list_suppliers(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list suppliers: {}", e)))?;

        Ok(suppliers.into_iter().map(|s| s.into()).collect())
    }

    /// Create a new purchase order.
    fn create(&self, supplier_id: String) -> PyResult<PurchaseOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let supp_uuid = supplier_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid supplier UUID"))?;

        let po = commerce
            .purchase_orders()
            .create(stateset_core::CreatePurchaseOrder {
                supplier_id: supp_uuid,
                items: vec![],
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create PO: {}", e)))?;

        Ok(po.into())
    }

    /// Get a purchase order by ID.
    fn get(&self, id: String) -> PyResult<Option<PurchaseOrder>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let po = commerce
            .purchase_orders()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get PO: {}", e)))?;

        Ok(po.map(|p| p.into()))
    }

    /// List all purchase orders.
    fn list(&self) -> PyResult<Vec<PurchaseOrder>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let pos = commerce
            .purchase_orders()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list POs: {}", e)))?;

        Ok(pos.into_iter().map(|p| p.into()).collect())
    }

    /// Submit PO for approval.
    fn submit(&self, id: String) -> PyResult<PurchaseOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let po = commerce
            .purchase_orders()
            .submit(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to submit PO: {}", e)))?;

        Ok(po.into())
    }

    /// Approve a purchase order.
    fn approve(&self, id: String, approved_by: String) -> PyResult<PurchaseOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let po = commerce
            .purchase_orders()
            .approve(uuid, &approved_by)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to approve PO: {}", e)))?;

        Ok(po.into())
    }

    /// Send PO to supplier.
    fn send(&self, id: String) -> PyResult<PurchaseOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let po = commerce
            .purchase_orders()
            .send(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to send PO: {}", e)))?;

        Ok(po.into())
    }

    /// Cancel a purchase order.
    fn cancel(&self, id: String) -> PyResult<PurchaseOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let po = commerce
            .purchase_orders()
            .cancel(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to cancel PO: {}", e)))?;

        Ok(po.into())
    }

    /// Count purchase orders.
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .purchase_orders()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count POs: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Invoice Types
// ============================================================================

/// Invoice data.
#[pyclass]
#[derive(Clone)]
pub struct Invoice {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    invoice_number: String,
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    order_id: Option<String>,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    subtotal: f64,
    #[pyo3(get)]
    tax_amount: f64,
    #[pyo3(get)]
    total: f64,
    #[pyo3(get)]
    amount_paid: f64,
    #[pyo3(get)]
    due_date: String,
    #[pyo3(get)]
    created_at: String,
}

#[pymethods]
impl Invoice {
    fn __repr__(&self) -> String {
        format!("Invoice(number='{}', status='{}', total={})", self.invoice_number, self.status, self.total)
    }

    #[getter]
    fn balance_due(&self) -> f64 {
        self.total - self.amount_paid
    }
}

impl From<stateset_core::Invoice> for Invoice {
    fn from(inv: stateset_core::Invoice) -> Self {
        Self {
            id: inv.id.to_string(),
            invoice_number: inv.invoice_number,
            customer_id: inv.customer_id.to_string(),
            order_id: inv.order_id.map(|id| id.to_string()),
            status: format!("{}", inv.status),
            subtotal: to_f64_or_nan(inv.subtotal),
            tax_amount: to_f64_or_nan(inv.tax_amount),
            total: to_f64_or_nan(inv.total),
            amount_paid: to_f64_or_nan(inv.amount_paid),
            due_date: inv.due_date.to_rfc3339(),
            created_at: inv.created_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Invoices API
// ============================================================================

/// Invoice management operations.
#[pyclass]
pub struct Invoices {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Invoices {
    /// Create a new invoice.
    #[pyo3(signature = (customer_id, order_id=None, billing_email=None))]
    fn create(
        &self,
        customer_id: String,
        order_id: Option<String>,
        billing_email: Option<String>,
    ) -> PyResult<Invoice> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let cust_uuid = customer_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;

        let order_uuid = order_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid order UUID"))?;

        let invoice = commerce
            .invoices()
            .create(stateset_core::CreateInvoice {
                customer_id: cust_uuid,
                order_id: order_uuid,
                billing_email,
                items: vec![],
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create invoice: {}", e)))?;

        Ok(invoice.into())
    }

    /// Get an invoice by ID.
    fn get(&self, id: String) -> PyResult<Option<Invoice>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let invoice = commerce
            .invoices()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get invoice: {}", e)))?;

        Ok(invoice.map(|i| i.into()))
    }

    /// List all invoices.
    fn list(&self) -> PyResult<Vec<Invoice>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let invoices = commerce
            .invoices()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list invoices: {}", e)))?;

        Ok(invoices.into_iter().map(|i| i.into()).collect())
    }

    /// Send an invoice.
    fn send(&self, id: String) -> PyResult<Invoice> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let invoice = commerce
            .invoices()
            .send(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to send invoice: {}", e)))?;

        Ok(invoice.into())
    }

    /// Record a payment against an invoice.
    #[pyo3(signature = (id, amount, payment_method=None, reference=None))]
    fn record_payment(
        &self,
        id: String,
        amount: f64,
        payment_method: Option<String>,
        reference: Option<String>,
    ) -> PyResult<Invoice> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let invoice = commerce
            .invoices()
            .record_payment(uuid, stateset_core::RecordInvoicePayment {
                amount: Decimal::from_f64_retain(amount).unwrap_or_default(),
                payment_method,
                reference,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to record payment: {}", e)))?;

        Ok(invoice.into())
    }

    /// Void an invoice.
    fn void(&self, id: String) -> PyResult<Invoice> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let invoice = commerce
            .invoices()
            .void(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to void invoice: {}", e)))?;

        Ok(invoice.into())
    }

    /// Get overdue invoices.
    fn get_overdue(&self) -> PyResult<Vec<Invoice>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let invoices = commerce
            .invoices()
            .get_overdue()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get overdue invoices: {}", e)))?;

        Ok(invoices.into_iter().map(|i| i.into()).collect())
    }

    /// Count invoices.
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .invoices()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count invoices: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// BOM Types
// ============================================================================

/// Bill of Materials data.
#[pyclass]
#[derive(Clone)]
pub struct Bom {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    bom_number: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    product_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    revision: String,
    #[pyo3(get)]
    created_at: String,
}

#[pymethods]
impl Bom {
    fn __repr__(&self) -> String {
        format!("Bom(number='{}', name='{}', status='{}')", self.bom_number, self.name, self.status)
    }
}

impl From<stateset_core::BillOfMaterials> for Bom {
    fn from(bom: stateset_core::BillOfMaterials) -> Self {
        Self {
            id: bom.id.to_string(),
            bom_number: bom.bom_number,
            name: bom.name,
            product_id: bom.product_id.to_string(),
            status: format!("{}", bom.status),
            revision: bom.revision,
            created_at: bom.created_at.to_rfc3339(),
        }
    }
}

/// BOM component data.
#[pyclass]
#[derive(Clone)]
pub struct BomComponent {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    bom_id: String,
    #[pyo3(get)]
    component_sku: Option<String>,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    quantity: f64,
    #[pyo3(get)]
    unit_of_measure: String,
}

#[pymethods]
impl BomComponent {
    fn __repr__(&self) -> String {
        format!("BomComponent(name='{}', qty={})", self.name, self.quantity)
    }
}

impl From<stateset_core::BomComponent> for BomComponent {
    fn from(c: stateset_core::BomComponent) -> Self {
        Self {
            id: c.id.to_string(),
            bom_id: c.bom_id.to_string(),
            component_sku: c.component_sku,
            name: c.name,
            quantity: to_f64_or_nan(c.quantity),
            unit_of_measure: c.unit_of_measure,
        }
    }
}

// ============================================================================
// BOM API
// ============================================================================

/// Bill of Materials management operations.
#[pyclass]
pub struct BomApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl BomApi {
    /// Create a new BOM.
    #[pyo3(signature = (name, product_id, description=None, revision=None))]
    fn create(
        &self,
        name: String,
        product_id: String,
        description: Option<String>,
        revision: Option<String>,
    ) -> PyResult<Bom> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let prod_uuid = product_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid product UUID"))?;

        let bom = commerce
            .bom()
            .create(stateset_core::CreateBom {
                name,
                product_id: prod_uuid,
                description,
                revision,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create BOM: {}", e)))?;

        Ok(bom.into())
    }

    /// Get a BOM by ID.
    fn get(&self, id: String) -> PyResult<Option<Bom>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let bom = commerce
            .bom()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get BOM: {}", e)))?;

        Ok(bom.map(|b| b.into()))
    }

    /// List all BOMs.
    fn list(&self) -> PyResult<Vec<Bom>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let boms = commerce
            .bom()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list BOMs: {}", e)))?;

        Ok(boms.into_iter().map(|b| b.into()).collect())
    }

    /// Add a component to a BOM.
    #[pyo3(signature = (bom_id, name, quantity, component_sku=None, unit_of_measure=None))]
    fn add_component(
        &self,
        bom_id: String,
        name: String,
        quantity: f64,
        component_sku: Option<String>,
        unit_of_measure: Option<String>,
    ) -> PyResult<BomComponent> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = bom_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid BOM UUID"))?;

        let component = commerce
            .bom()
            .add_component(uuid, stateset_core::CreateBomComponent {
                component_sku,
                name,
                quantity: Decimal::from_f64_retain(quantity).unwrap_or_default(),
                unit_of_measure,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to add component: {}", e)))?;

        Ok(component.into())
    }

    /// Get components for a BOM.
    fn get_components(&self, bom_id: String) -> PyResult<Vec<BomComponent>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = bom_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid BOM UUID"))?;

        let components = commerce
            .bom()
            .get_components(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get components: {}", e)))?;

        Ok(components.into_iter().map(|c| c.into()).collect())
    }

    /// Activate a BOM.
    fn activate(&self, id: String) -> PyResult<Bom> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let bom = commerce
            .bom()
            .activate(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to activate BOM: {}", e)))?;

        Ok(bom.into())
    }

    /// Count BOMs.
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .bom()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count BOMs: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Work Order Types
// ============================================================================

/// Work order data.
#[pyclass]
#[derive(Clone)]
pub struct WorkOrder {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    work_order_number: String,
    #[pyo3(get)]
    product_id: String,
    #[pyo3(get)]
    bom_id: Option<String>,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    priority: String,
    #[pyo3(get)]
    quantity_to_build: f64,
    #[pyo3(get)]
    quantity_completed: f64,
    #[pyo3(get)]
    version: i32,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

#[pymethods]
impl WorkOrder {
    fn __repr__(&self) -> String {
        format!(
            "WorkOrder(number='{}', status='{}', qty={}/{})",
            self.work_order_number, self.status, self.quantity_completed, self.quantity_to_build
        )
    }
}

impl From<stateset_core::WorkOrder> for WorkOrder {
    fn from(wo: stateset_core::WorkOrder) -> Self {
        Self {
            id: wo.id.to_string(),
            work_order_number: wo.work_order_number,
            product_id: wo.product_id.to_string(),
            bom_id: wo.bom_id.map(|id| id.to_string()),
            status: format!("{}", wo.status),
            priority: format!("{}", wo.priority),
            quantity_to_build: to_f64_or_nan(wo.quantity_to_build),
            quantity_completed: to_f64_or_nan(wo.quantity_completed),
            version: wo.version,
            created_at: wo.created_at.to_rfc3339(),
            updated_at: wo.updated_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Work Orders API
// ============================================================================

/// Work order management operations.
#[pyclass]
pub struct WorkOrders {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl WorkOrders {
    /// Create a new work order.
    #[pyo3(signature = (product_id, quantity_to_build, bom_id=None, priority=None, notes=None))]
    fn create(
        &self,
        product_id: String,
        quantity_to_build: f64,
        bom_id: Option<String>,
        priority: Option<String>,
        notes: Option<String>,
    ) -> PyResult<WorkOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let prod_uuid = product_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid product UUID"))?;

        let bom_uuid = bom_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid BOM UUID"))?;

        let prio = priority.and_then(|p| match p.to_lowercase().as_str() {
            "low" => Some(stateset_core::WorkOrderPriority::Low),
            "normal" => Some(stateset_core::WorkOrderPriority::Normal),
            "high" => Some(stateset_core::WorkOrderPriority::High),
            "urgent" => Some(stateset_core::WorkOrderPriority::Urgent),
            _ => None,
        });

        let wo = commerce
            .work_orders()
            .create(stateset_core::CreateWorkOrder {
                product_id: prod_uuid,
                bom_id: bom_uuid,
                quantity_to_build: Decimal::from_f64_retain(quantity_to_build).unwrap_or_default(),
                priority: prio,
                notes,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create work order: {}", e)))?;

        Ok(wo.into())
    }

    /// Get a work order by ID.
    fn get(&self, id: String) -> PyResult<Option<WorkOrder>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let wo = commerce
            .work_orders()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get work order: {}", e)))?;

        Ok(wo.map(|w| w.into()))
    }

    /// List all work orders.
    fn list(&self) -> PyResult<Vec<WorkOrder>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let wos = commerce
            .work_orders()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list work orders: {}", e)))?;

        Ok(wos.into_iter().map(|w| w.into()).collect())
    }

    /// Start a work order.
    fn start(&self, id: String) -> PyResult<WorkOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let wo = commerce
            .work_orders()
            .start(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to start work order: {}", e)))?;

        Ok(wo.into())
    }

    /// Complete a work order.
    fn complete(&self, id: String, quantity_completed: f64) -> PyResult<WorkOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let wo = commerce
            .work_orders()
            .complete(uuid, Decimal::from_f64_retain(quantity_completed).unwrap_or_default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to complete work order: {}", e)))?;

        Ok(wo.into())
    }

    /// Cancel a work order.
    fn cancel(&self, id: String) -> PyResult<WorkOrder> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let wo = commerce
            .work_orders()
            .cancel(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to cancel work order: {}", e)))?;

        Ok(wo.into())
    }

    /// Count work orders.
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .work_orders()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count work orders: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Cart Types
// ============================================================================

/// Cart address data.
#[pyclass]
#[derive(Clone)]
pub struct CartAddress {
    #[pyo3(get)]
    first_name: String,
    #[pyo3(get)]
    last_name: String,
    #[pyo3(get)]
    company: Option<String>,
    #[pyo3(get)]
    line1: String,
    #[pyo3(get)]
    line2: Option<String>,
    #[pyo3(get)]
    city: String,
    #[pyo3(get)]
    state: Option<String>,
    #[pyo3(get)]
    postal_code: String,
    #[pyo3(get)]
    country: String,
    #[pyo3(get)]
    phone: Option<String>,
    #[pyo3(get)]
    email: Option<String>,
}

#[pymethods]
impl CartAddress {
    #[new]
    #[pyo3(signature = (first_name, last_name, line1, city, postal_code, country, company=None, line2=None, state=None, phone=None, email=None))]
    fn new(
        first_name: String,
        last_name: String,
        line1: String,
        city: String,
        postal_code: String,
        country: String,
        company: Option<String>,
        line2: Option<String>,
        state: Option<String>,
        phone: Option<String>,
        email: Option<String>,
    ) -> Self {
        Self {
            first_name,
            last_name,
            company,
            line1,
            line2,
            city,
            state,
            postal_code,
            country,
            phone,
            email,
        }
    }

    fn __repr__(&self) -> String {
        format!(
            "CartAddress(name='{} {}', city='{}')",
            self.first_name, self.last_name, self.city
        )
    }
}

impl From<stateset_core::CartAddress> for CartAddress {
    fn from(a: stateset_core::CartAddress) -> Self {
        Self {
            first_name: a.first_name,
            last_name: a.last_name,
            company: a.company,
            line1: a.line1,
            line2: a.line2,
            city: a.city,
            state: a.state,
            postal_code: a.postal_code,
            country: a.country,
            phone: a.phone,
            email: a.email,
        }
    }
}

impl From<&CartAddress> for stateset_core::CartAddress {
    fn from(a: &CartAddress) -> Self {
        Self {
            first_name: a.first_name.clone(),
            last_name: a.last_name.clone(),
            company: a.company.clone(),
            line1: a.line1.clone(),
            line2: a.line2.clone(),
            city: a.city.clone(),
            state: a.state.clone(),
            postal_code: a.postal_code.clone(),
            country: a.country.clone(),
            phone: a.phone.clone(),
            email: a.email.clone(),
        }
    }
}

/// Cart item data.
#[pyclass]
#[derive(Clone)]
pub struct CartItem {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    cart_id: String,
    #[pyo3(get)]
    product_id: Option<String>,
    #[pyo3(get)]
    variant_id: Option<String>,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    description: Option<String>,
    #[pyo3(get)]
    image_url: Option<String>,
    #[pyo3(get)]
    quantity: i32,
    #[pyo3(get)]
    unit_price: f64,
    #[pyo3(get)]
    original_price: Option<f64>,
    #[pyo3(get)]
    discount_amount: f64,
    #[pyo3(get)]
    tax_amount: f64,
    #[pyo3(get)]
    total: f64,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

#[pymethods]
impl CartItem {
    fn __repr__(&self) -> String {
        format!(
            "CartItem(sku='{}', qty={}, total={})",
            self.sku, self.quantity, self.total
        )
    }
}

impl From<stateset_core::CartItem> for CartItem {
    fn from(i: stateset_core::CartItem) -> Self {
        Self {
            id: i.id.to_string(),
            cart_id: i.cart_id.to_string(),
            product_id: i.product_id.map(|id| id.to_string()),
            variant_id: i.variant_id.map(|id| id.to_string()),
            sku: i.sku,
            name: i.name,
            description: i.description,
            image_url: i.image_url,
            quantity: i.quantity,
            unit_price: to_f64_or_nan(i.unit_price),
            original_price: i.original_price.map(|p| to_f64_or_nan(p)),
            discount_amount: to_f64_or_nan(i.discount_amount),
            tax_amount: to_f64_or_nan(i.tax_amount),
            total: to_f64_or_nan(i.total),
            created_at: i.created_at.to_rfc3339(),
            updated_at: i.updated_at.to_rfc3339(),
        }
    }
}

/// Shipping rate option.
#[pyclass]
#[derive(Clone)]
pub struct ShippingRate {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    carrier: String,
    #[pyo3(get)]
    service: String,
    #[pyo3(get)]
    description: Option<String>,
    #[pyo3(get)]
    price: f64,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    estimated_days: Option<i32>,
    #[pyo3(get)]
    estimated_delivery: Option<String>,
}

#[pymethods]
impl ShippingRate {
    fn __repr__(&self) -> String {
        format!(
            "ShippingRate(carrier='{}', service='{}', price={})",
            self.carrier, self.service, self.price
        )
    }
}

impl From<stateset_core::ShippingRate> for ShippingRate {
    fn from(r: stateset_core::ShippingRate) -> Self {
        Self {
            id: r.id,
            carrier: r.carrier,
            service: r.service,
            description: r.description,
            price: to_f64_or_nan(r.price),
            currency: r.currency,
            estimated_days: r.estimated_days,
            estimated_delivery: r.estimated_delivery.map(|d| d.to_rfc3339()),
        }
    }
}

/// Checkout result returned when completing a cart.
#[pyclass]
#[derive(Clone)]
pub struct CheckoutResult {
    #[pyo3(get)]
    order_id: String,
    #[pyo3(get)]
    order_number: String,
    #[pyo3(get)]
    cart_id: String,
    #[pyo3(get)]
    payment_id: Option<String>,
    #[pyo3(get)]
    total_charged: f64,
    #[pyo3(get)]
    currency: String,
}

#[pymethods]
impl CheckoutResult {
    fn __repr__(&self) -> String {
        format!(
            "CheckoutResult(order='{}', total={} {})",
            self.order_number, self.total_charged, self.currency
        )
    }
}

impl From<stateset_core::CheckoutResult> for CheckoutResult {
    fn from(r: stateset_core::CheckoutResult) -> Self {
        Self {
            order_id: r.order_id.to_string(),
            order_number: r.order_number,
            cart_id: r.cart_id.to_string(),
            payment_id: r.payment_id.map(|id| id.to_string()),
            total_charged: to_f64_or_nan(r.total_charged),
            currency: r.currency,
        }
    }
}

/// Cart data returned from operations.
#[pyclass]
#[derive(Clone)]
pub struct Cart {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    cart_number: String,
    #[pyo3(get)]
    customer_id: Option<String>,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    subtotal: f64,
    #[pyo3(get)]
    tax_amount: f64,
    #[pyo3(get)]
    shipping_amount: f64,
    #[pyo3(get)]
    discount_amount: f64,
    #[pyo3(get)]
    grand_total: f64,
    #[pyo3(get)]
    customer_email: Option<String>,
    #[pyo3(get)]
    customer_name: Option<String>,
    #[pyo3(get)]
    payment_method: Option<String>,
    #[pyo3(get)]
    payment_status: String,
    #[pyo3(get)]
    fulfillment_type: String,
    #[pyo3(get)]
    shipping_method: Option<String>,
    #[pyo3(get)]
    coupon_code: Option<String>,
    #[pyo3(get)]
    notes: Option<String>,
    #[pyo3(get)]
    item_count: i32,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
    #[pyo3(get)]
    expires_at: Option<String>,
    // Store items separately
    _items: Vec<CartItem>,
    _shipping_address: Option<CartAddress>,
    _billing_address: Option<CartAddress>,
}

#[pymethods]
impl Cart {
    fn __repr__(&self) -> String {
        format!(
            "Cart(number='{}', status='{}', total={} {})",
            self.cart_number, self.status, self.grand_total, self.currency
        )
    }

    /// Get cart items.
    #[getter]
    fn items(&self) -> Vec<CartItem> {
        self._items.clone()
    }

    /// Get the shipping address.
    #[getter]
    fn shipping_address(&self) -> Option<CartAddress> {
        self._shipping_address.clone()
    }

    /// Get the billing address.
    #[getter]
    fn billing_address(&self) -> Option<CartAddress> {
        self._billing_address.clone()
    }
}

impl From<stateset_core::Cart> for Cart {
    fn from(c: stateset_core::Cart) -> Self {
        let item_count = c.items.len() as i32;
        Self {
            id: c.id.to_string(),
            cart_number: c.cart_number,
            customer_id: c.customer_id.map(|id| id.to_string()),
            status: format!("{}", c.status),
            currency: c.currency,
            subtotal: to_f64_or_nan(c.subtotal),
            tax_amount: to_f64_or_nan(c.tax_amount),
            shipping_amount: to_f64_or_nan(c.shipping_amount),
            discount_amount: to_f64_or_nan(c.discount_amount),
            grand_total: to_f64_or_nan(c.grand_total),
            customer_email: c.customer_email,
            customer_name: c.customer_name,
            payment_method: c.payment_method,
            payment_status: format!("{}", c.payment_status),
            fulfillment_type: c.fulfillment_type.map(|ft| format!("{}", ft)).unwrap_or_else(|| "Shipping".to_string()),
            shipping_method: c.shipping_method,
            coupon_code: c.coupon_code,
            notes: c.notes,
            item_count,
            created_at: c.created_at.to_rfc3339(),
            updated_at: c.updated_at.to_rfc3339(),
            expires_at: c.expires_at.map(|d| d.to_rfc3339()),
            _items: c.items.into_iter().map(|i| i.into()).collect(),
            _shipping_address: c.shipping_address.map(|a| a.into()),
            _billing_address: c.billing_address.map(|a| a.into()),
        }
    }
}

/// Input for adding a cart item.
#[pyclass]
#[derive(Clone)]
pub struct AddCartItemInput {
    #[pyo3(get, set)]
    sku: String,
    #[pyo3(get, set)]
    name: String,
    #[pyo3(get, set)]
    quantity: i32,
    #[pyo3(get, set)]
    unit_price: f64,
    #[pyo3(get, set)]
    product_id: Option<String>,
    #[pyo3(get, set)]
    variant_id: Option<String>,
    #[pyo3(get, set)]
    description: Option<String>,
    #[pyo3(get, set)]
    image_url: Option<String>,
    #[pyo3(get, set)]
    original_price: Option<f64>,
    #[pyo3(get, set)]
    weight: Option<f64>,
    #[pyo3(get, set)]
    requires_shipping: Option<bool>,
}

#[pymethods]
impl AddCartItemInput {
    #[new]
    #[pyo3(signature = (sku, name, quantity, unit_price, product_id=None, variant_id=None, description=None, image_url=None, original_price=None, weight=None, requires_shipping=None))]
    fn new(
        sku: String,
        name: String,
        quantity: i32,
        unit_price: f64,
        product_id: Option<String>,
        variant_id: Option<String>,
        description: Option<String>,
        image_url: Option<String>,
        original_price: Option<f64>,
        weight: Option<f64>,
        requires_shipping: Option<bool>,
    ) -> Self {
        Self {
            sku,
            name,
            quantity,
            unit_price,
            product_id,
            variant_id,
            description,
            image_url,
            original_price,
            weight,
            requires_shipping,
        }
    }
}

// ============================================================================
// Carts API
// ============================================================================

/// Cart and checkout management operations.
#[pyclass]
pub struct Carts {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Carts {
    /// Create a new cart.
    ///
    /// Args:
    ///     customer_id: Customer UUID (optional for guest checkout)
    ///     customer_email: Customer email (optional)
    ///     customer_name: Customer name (optional)
    ///     currency: Currency code (default "USD")
    ///     expires_in_minutes: Cart expiration time (optional)
    ///
    /// Returns:
    ///     Cart: The created cart
    #[pyo3(signature = (customer_id=None, customer_email=None, customer_name=None, currency=None, expires_in_minutes=None))]
    fn create(
        &self,
        customer_id: Option<String>,
        customer_email: Option<String>,
        customer_name: Option<String>,
        currency: Option<String>,
        expires_in_minutes: Option<i64>,
    ) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let cust_uuid = customer_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;

        let cart = commerce
            .carts()
            .create(stateset_core::CreateCart {
                customer_id: cust_uuid,
                customer_email,
                customer_name,
                currency,
                expires_in_minutes,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create cart: {}", e)))?;

        Ok(cart.into())
    }

    /// Get a cart by ID.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart or None if not found
    fn get(&self, id: String) -> PyResult<Option<Cart>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get cart: {}", e)))?;

        Ok(cart.map(|c| c.into()))
    }

    /// Get a cart by cart number.
    ///
    /// Args:
    ///     cart_number: Cart number string
    ///
    /// Returns:
    ///     Cart or None if not found
    fn get_by_number(&self, cart_number: String) -> PyResult<Option<Cart>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let cart = commerce
            .carts()
            .get_by_number(&cart_number)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get cart: {}", e)))?;

        Ok(cart.map(|c| c.into()))
    }

    /// Update a cart.
    ///
    /// Args:
    ///     id: Cart UUID
    ///     customer_email: Customer email (optional)
    ///     customer_phone: Customer phone (optional)
    ///     customer_name: Customer name (optional)
    ///     shipping_method: Shipping method string (optional)
    ///     coupon_code: Coupon code (optional)
    ///     notes: Notes (optional)
    ///
    /// Returns:
    ///     Cart: Updated cart
    #[pyo3(signature = (id, customer_email=None, customer_phone=None, customer_name=None, shipping_method=None, coupon_code=None, notes=None))]
    fn update(
        &self,
        id: String,
        customer_email: Option<String>,
        customer_phone: Option<String>,
        customer_name: Option<String>,
        shipping_method: Option<String>,
        coupon_code: Option<String>,
        notes: Option<String>,
    ) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .update(uuid, stateset_core::UpdateCart {
                customer_email,
                customer_phone,
                customer_name,
                shipping_method,
                coupon_code,
                notes,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to update cart: {}", e)))?;

        Ok(cart.into())
    }

    /// List all carts.
    ///
    /// Returns:
    ///     List[Cart]: All carts
    fn list(&self) -> PyResult<Vec<Cart>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let carts = commerce
            .carts()
            .list(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list carts: {}", e)))?;

        Ok(carts.into_iter().map(|c| c.into()).collect())
    }

    /// Get all carts for a customer.
    ///
    /// Args:
    ///     customer_id: Customer UUID
    ///
    /// Returns:
    ///     List[Cart]: Customer's carts
    fn for_customer(&self, customer_id: String) -> PyResult<Vec<Cart>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = customer_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let carts = commerce
            .carts()
            .for_customer(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get customer carts: {}", e)))?;

        Ok(carts.into_iter().map(|c| c.into()).collect())
    }

    /// Delete a cart.
    ///
    /// Args:
    ///     id: Cart UUID
    fn delete(&self, id: String) -> PyResult<()> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        commerce
            .carts()
            .delete(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to delete cart: {}", e)))?;

        Ok(())
    }

    // === Item Operations ===

    /// Add an item to the cart.
    ///
    /// Args:
    ///     cart_id: Cart UUID
    ///     item: AddCartItemInput
    ///
    /// Returns:
    ///     CartItem: The added item
    fn add_item(&self, cart_id: String, item: AddCartItemInput) -> PyResult<CartItem> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = cart_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let prod_uuid = item.product_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid product UUID"))?;

        let var_uuid = item.variant_id
            .map(|id| id.parse())
            .transpose()
            .map_err(|_| PyValueError::new_err("Invalid variant UUID"))?;

        let cart_item = commerce
            .carts()
            .add_item(uuid, stateset_core::AddCartItem {
                product_id: prod_uuid,
                variant_id: var_uuid,
                sku: item.sku,
                name: item.name,
                description: item.description,
                image_url: item.image_url,
                quantity: item.quantity,
                unit_price: Decimal::from_str_exact(&item.unit_price.to_string()).unwrap_or_default(),
                original_price: item.original_price.map(|p| Decimal::from_str_exact(&p.to_string()).unwrap_or_default()),
                weight: item.weight.map(|w| Decimal::from_str_exact(&w.to_string()).unwrap_or_default()),
                requires_shipping: item.requires_shipping,
                metadata: None,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to add item: {}", e)))?;

        Ok(cart_item.into())
    }

    /// Update a cart item.
    ///
    /// Args:
    ///     item_id: Cart item UUID
    ///     quantity: New quantity (optional)
    ///
    /// Returns:
    ///     CartItem: The updated item
    #[pyo3(signature = (item_id, quantity=None))]
    fn update_item(&self, item_id: String, quantity: Option<i32>) -> PyResult<CartItem> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = item_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart_item = commerce
            .carts()
            .update_item(uuid, stateset_core::UpdateCartItem {
                quantity,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to update item: {}", e)))?;

        Ok(cart_item.into())
    }

    /// Remove an item from the cart.
    ///
    /// Args:
    ///     item_id: Cart item UUID
    fn remove_item(&self, item_id: String) -> PyResult<()> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = item_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        commerce
            .carts()
            .remove_item(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to remove item: {}", e)))?;

        Ok(())
    }

    /// Get all items in a cart.
    ///
    /// Args:
    ///     cart_id: Cart UUID
    ///
    /// Returns:
    ///     List[CartItem]: Cart items
    fn get_items(&self, cart_id: String) -> PyResult<Vec<CartItem>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = cart_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let items = commerce
            .carts()
            .get_items(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get items: {}", e)))?;

        Ok(items.into_iter().map(|i| i.into()).collect())
    }

    /// Clear all items from a cart.
    ///
    /// Args:
    ///     cart_id: Cart UUID
    fn clear_items(&self, cart_id: String) -> PyResult<()> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = cart_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        commerce
            .carts()
            .clear_items(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to clear items: {}", e)))?;

        Ok(())
    }

    // === Address Operations ===

    /// Set the shipping address.
    ///
    /// Args:
    ///     id: Cart UUID
    ///     address: CartAddress
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn set_shipping_address(&self, id: String, address: CartAddress) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .set_shipping_address(uuid, (&address).into())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to set shipping address: {}", e)))?;

        Ok(cart.into())
    }

    /// Set the billing address.
    ///
    /// Args:
    ///     id: Cart UUID
    ///     address: CartAddress
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn set_billing_address(&self, id: String, address: CartAddress) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .set_billing_address(uuid, (&address).into())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to set billing address: {}", e)))?;

        Ok(cart.into())
    }

    // === Shipping Operations ===

    /// Set shipping selection (address + method/carrier/amount).
    ///
    /// Args:
    ///     id: Cart UUID
    ///     address: CartAddress
    ///     shipping_method: Shipping method (optional)
    ///     shipping_carrier: Shipping carrier (optional)
    ///     shipping_amount: Shipping amount (optional)
    ///
    /// Returns:
    ///     Cart: Updated cart
    #[pyo3(signature = (id, address, shipping_method=None, shipping_carrier=None, shipping_amount=None))]
    fn set_shipping(
        &self,
        id: String,
        address: CartAddress,
        shipping_method: Option<String>,
        shipping_carrier: Option<String>,
        shipping_amount: Option<f64>,
    ) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let amount_dec = match shipping_amount {
            Some(v) => Some(
                Decimal::from_f64_retain(v)
                    .ok_or_else(|| PyValueError::new_err("Invalid shipping amount"))?,
            ),
            None => None,
        };

        let cart = commerce
            .carts()
            .set_shipping(uuid, stateset_core::SetCartShipping {
                shipping_address: (&address).into(),
                shipping_method,
                shipping_carrier,
                shipping_amount: amount_dec,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to set shipping: {}", e)))?;

        Ok(cart.into())
    }

    /// Get available shipping rates for the cart.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     List[ShippingRate]: Available shipping options
    fn get_shipping_rates(&self, id: String) -> PyResult<Vec<ShippingRate>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let rates = commerce
            .carts()
            .get_shipping_rates(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get shipping rates: {}", e)))?;

        Ok(rates.into_iter().map(|r| r.into()).collect())
    }

    // === Payment Operations ===

    /// Set the payment method.
    ///
    /// Args:
    ///     id: Cart UUID
    ///     payment_method: Payment method string (e.g., "credit_card")
    ///     payment_token: Payment token (optional)
    ///
    /// Returns:
    ///     Cart: Updated cart
    #[pyo3(signature = (id, payment_method, payment_token=None))]
    fn set_payment(&self, id: String, payment_method: String, payment_token: Option<String>) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .set_payment(uuid, stateset_core::SetCartPayment {
                payment_method,
                payment_token,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to set payment: {}", e)))?;

        Ok(cart.into())
    }

    // === Discount Operations ===

    /// Apply a coupon code to the cart.
    ///
    /// Args:
    ///     id: Cart UUID
    ///     coupon_code: Coupon/discount code
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn apply_discount(&self, id: String, coupon_code: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .apply_discount(uuid, &coupon_code)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to apply discount: {}", e)))?;

        Ok(cart.into())
    }

    /// Remove the discount from the cart.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn remove_discount(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .remove_discount(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to remove discount: {}", e)))?;

        Ok(cart.into())
    }

    // === Checkout Flow ===

    /// Mark the cart as ready for payment.
    ///
    /// Validates that all required info is present.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn mark_ready_for_payment(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .mark_ready_for_payment(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to mark ready: {}", e)))?;

        Ok(cart.into())
    }

    /// Begin the checkout process.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn begin_checkout(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .begin_checkout(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to begin checkout: {}", e)))?;

        Ok(cart.into())
    }

    /// Complete the checkout and create an order.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     CheckoutResult: Order creation result
    fn complete(&self, id: String) -> PyResult<CheckoutResult> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let result = commerce
            .carts()
            .complete(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to complete checkout: {}", e)))?;

        Ok(result.into())
    }

    /// Cancel the cart.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn cancel(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .cancel(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to cancel cart: {}", e)))?;

        Ok(cart.into())
    }

    /// Mark the cart as abandoned.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn abandon(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .abandon(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to abandon cart: {}", e)))?;

        Ok(cart.into())
    }

    /// Expire the cart.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn expire(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .expire(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to expire cart: {}", e)))?;

        Ok(cart.into())
    }

    // === Inventory Operations ===

    /// Reserve inventory for cart items.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn reserve_inventory(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .reserve_inventory(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to reserve inventory: {}", e)))?;

        Ok(cart.into())
    }

    /// Release inventory reservations.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn release_inventory(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .release_inventory(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to release inventory: {}", e)))?;

        Ok(cart.into())
    }

    /// Recalculate cart totals.
    ///
    /// Args:
    ///     id: Cart UUID
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn recalculate(&self, id: String) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .recalculate(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to recalculate: {}", e)))?;

        Ok(cart.into())
    }

    /// Set the tax amount for the cart.
    ///
    /// Args:
    ///     id: Cart UUID
    ///     tax_amount: Tax amount
    ///
    /// Returns:
    ///     Cart: Updated cart
    fn set_tax(&self, id: String, tax_amount: f64) -> PyResult<Cart> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cart = commerce
            .carts()
            .set_tax(uuid, Decimal::from_str_exact(&tax_amount.to_string()).unwrap_or_default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to set tax: {}", e)))?;

        Ok(cart.into())
    }

    // === Query Operations ===

    /// Get abandoned carts.
    ///
    /// Returns:
    ///     List[Cart]: Abandoned carts
    fn get_abandoned(&self) -> PyResult<Vec<Cart>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let carts = commerce
            .carts()
            .get_abandoned()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get abandoned carts: {}", e)))?;

        Ok(carts.into_iter().map(|c| c.into()).collect())
    }

    /// Get expired carts.
    ///
    /// Returns:
    ///     List[Cart]: Expired carts
    fn get_expired(&self) -> PyResult<Vec<Cart>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let carts = commerce
            .carts()
            .get_expired()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get expired carts: {}", e)))?;

        Ok(carts.into_iter().map(|c| c.into()).collect())
    }

    /// Count carts.
    ///
    /// Returns:
    ///     int: Number of carts
    fn count(&self) -> PyResult<u32> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let count = commerce
            .carts()
            .count(Default::default())
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to count carts: {}", e)))?;

        Ok(count as u32)
    }
}

// ============================================================================
// Analytics Types
// ============================================================================

fn dec_to_f64(d: &Decimal) -> f64 {
    to_f64_or_nan(*d)
}

fn parse_time_period(period: &str) -> stateset_core::TimePeriod {
    match period.to_lowercase().as_str() {
        "today" => stateset_core::TimePeriod::Today,
        "yesterday" => stateset_core::TimePeriod::Yesterday,
        "last7days" | "last_7_days" => stateset_core::TimePeriod::Last7Days,
        "last30days" | "last_30_days" => stateset_core::TimePeriod::Last30Days,
        "this_month" | "thismonth" => stateset_core::TimePeriod::ThisMonth,
        "last_month" | "lastmonth" => stateset_core::TimePeriod::LastMonth,
        "this_quarter" | "thisquarter" => stateset_core::TimePeriod::ThisQuarter,
        "last_quarter" | "lastquarter" => stateset_core::TimePeriod::LastQuarter,
        "this_year" | "thisyear" => stateset_core::TimePeriod::ThisYear,
        "last_year" | "lastyear" => stateset_core::TimePeriod::LastYear,
        "all_time" | "alltime" | "all" => stateset_core::TimePeriod::AllTime,
        _ => stateset_core::TimePeriod::Last30Days,
    }
}

fn parse_time_granularity(granularity: &str) -> stateset_core::TimeGranularity {
    match granularity.to_lowercase().as_str() {
        "hour" | "hourly" => stateset_core::TimeGranularity::Hour,
        "day" | "daily" => stateset_core::TimeGranularity::Day,
        "week" | "weekly" => stateset_core::TimeGranularity::Week,
        "month" | "monthly" => stateset_core::TimeGranularity::Month,
        "quarter" | "quarterly" => stateset_core::TimeGranularity::Quarter,
        "year" | "yearly" => stateset_core::TimeGranularity::Year,
        _ => stateset_core::TimeGranularity::Day,
    }
}

fn build_analytics_query(
    period: Option<String>,
    granularity: Option<String>,
    limit: Option<u32>,
) -> stateset_core::AnalyticsQuery {
    let mut q = stateset_core::AnalyticsQuery::new();
    if let Some(p) = period {
        q = q.period(parse_time_period(&p));
    }
    if let Some(g) = granularity {
        q = q.granularity(parse_time_granularity(&g));
    }
    if let Some(l) = limit {
        q = q.limit(l);
    }
    q
}

/// Sales summary metrics.
#[pyclass]
#[derive(Clone)]
pub struct SalesSummary {
    #[pyo3(get)]
    total_revenue: f64,
    #[pyo3(get)]
    order_count: u32,
    #[pyo3(get)]
    average_order_value: f64,
    #[pyo3(get)]
    items_sold: u32,
    #[pyo3(get)]
    unique_customers: u32,
}

impl From<stateset_core::SalesSummary> for SalesSummary {
    fn from(s: stateset_core::SalesSummary) -> Self {
        Self {
            total_revenue: dec_to_f64(&s.total_revenue),
            order_count: s.order_count as u32,
            average_order_value: dec_to_f64(&s.average_order_value),
            items_sold: s.items_sold as u32,
            unique_customers: s.unique_customers as u32,
        }
    }
}

/// Revenue metrics grouped by time period.
#[pyclass]
#[derive(Clone)]
pub struct RevenueByPeriod {
    #[pyo3(get)]
    period: String,
    #[pyo3(get)]
    revenue: f64,
    #[pyo3(get)]
    order_count: u32,
    #[pyo3(get)]
    period_start: String,
}

impl From<stateset_core::RevenueByPeriod> for RevenueByPeriod {
    fn from(r: stateset_core::RevenueByPeriod) -> Self {
        Self {
            period: r.period,
            revenue: dec_to_f64(&r.revenue),
            order_count: r.order_count as u32,
            period_start: r.period_start.to_rfc3339(),
        }
    }
}

/// Top selling product metrics.
#[pyclass]
#[derive(Clone)]
pub struct TopProduct {
    #[pyo3(get)]
    product_id: Option<String>,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    units_sold: u32,
    #[pyo3(get)]
    revenue: f64,
    #[pyo3(get)]
    order_count: u32,
}

impl From<stateset_core::TopProduct> for TopProduct {
    fn from(p: stateset_core::TopProduct) -> Self {
        Self {
            product_id: p.product_id.map(|id| id.to_string()),
            sku: p.sku,
            name: p.name,
            units_sold: p.units_sold as u32,
            revenue: dec_to_f64(&p.revenue),
            order_count: p.order_count as u32,
        }
    }
}

/// Product performance with period comparison.
#[pyclass]
#[derive(Clone)]
pub struct ProductPerformance {
    #[pyo3(get)]
    product_id: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    units_sold: u32,
    #[pyo3(get)]
    revenue: f64,
    #[pyo3(get)]
    previous_units_sold: u32,
    #[pyo3(get)]
    previous_revenue: f64,
    #[pyo3(get)]
    units_growth_percent: f64,
    #[pyo3(get)]
    revenue_growth_percent: f64,
}

impl From<stateset_core::ProductPerformance> for ProductPerformance {
    fn from(p: stateset_core::ProductPerformance) -> Self {
        Self {
            product_id: p.product_id.to_string(),
            sku: p.sku,
            name: p.name,
            units_sold: p.units_sold as u32,
            revenue: dec_to_f64(&p.revenue),
            previous_units_sold: p.previous_units_sold as u32,
            previous_revenue: dec_to_f64(&p.previous_revenue),
            units_growth_percent: dec_to_f64(&p.units_growth_percent),
            revenue_growth_percent: dec_to_f64(&p.revenue_growth_percent),
        }
    }
}

/// Customer segment metrics.
#[pyclass]
#[derive(Clone)]
pub struct CustomerMetrics {
    #[pyo3(get)]
    total_customers: u32,
    #[pyo3(get)]
    new_customers: u32,
    #[pyo3(get)]
    returning_customers: u32,
    #[pyo3(get)]
    average_lifetime_value: f64,
    #[pyo3(get)]
    average_orders_per_customer: f64,
}

impl From<stateset_core::CustomerMetrics> for CustomerMetrics {
    fn from(m: stateset_core::CustomerMetrics) -> Self {
        Self {
            total_customers: m.total_customers as u32,
            new_customers: m.new_customers as u32,
            returning_customers: m.returning_customers as u32,
            average_lifetime_value: dec_to_f64(&m.average_lifetime_value),
            average_orders_per_customer: dec_to_f64(&m.average_orders_per_customer),
        }
    }
}

/// Top customer by spend.
#[pyclass]
#[derive(Clone)]
pub struct TopCustomer {
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    email: String,
    #[pyo3(get)]
    order_count: u32,
    #[pyo3(get)]
    total_spent: f64,
    #[pyo3(get)]
    average_order_value: f64,
}

impl From<stateset_core::TopCustomer> for TopCustomer {
    fn from(c: stateset_core::TopCustomer) -> Self {
        Self {
            customer_id: c.customer_id.to_string(),
            name: c.name,
            email: c.email,
            order_count: c.order_count as u32,
            total_spent: dec_to_f64(&c.total_spent),
            average_order_value: dec_to_f64(&c.average_order_value),
        }
    }
}

/// Inventory health summary.
#[pyclass]
#[derive(Clone)]
pub struct InventoryHealth {
    #[pyo3(get)]
    total_skus: u32,
    #[pyo3(get)]
    in_stock_skus: u32,
    #[pyo3(get)]
    low_stock_skus: u32,
    #[pyo3(get)]
    out_of_stock_skus: u32,
    #[pyo3(get)]
    total_value: f64,
}

impl From<stateset_core::InventoryHealth> for InventoryHealth {
    fn from(h: stateset_core::InventoryHealth) -> Self {
        Self {
            total_skus: h.total_skus as u32,
            in_stock_skus: h.in_stock_skus as u32,
            low_stock_skus: h.low_stock_skus as u32,
            out_of_stock_skus: h.out_of_stock_skus as u32,
            total_value: dec_to_f64(&h.total_value),
        }
    }
}

/// Low stock item.
#[pyclass]
#[derive(Clone)]
pub struct LowStockItem {
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    on_hand: f64,
    #[pyo3(get)]
    allocated: f64,
    #[pyo3(get)]
    available: f64,
    #[pyo3(get)]
    reorder_point: Option<f64>,
    #[pyo3(get)]
    average_daily_sales: Option<f64>,
    #[pyo3(get)]
    days_of_stock: Option<f64>,
}

impl From<stateset_core::LowStockItem> for LowStockItem {
    fn from(i: stateset_core::LowStockItem) -> Self {
        Self {
            sku: i.sku,
            name: i.name,
            on_hand: dec_to_f64(&i.on_hand),
            allocated: dec_to_f64(&i.allocated),
            available: dec_to_f64(&i.available),
            reorder_point: i.reorder_point.as_ref().map(dec_to_f64),
            average_daily_sales: i.average_daily_sales.as_ref().map(dec_to_f64),
            days_of_stock: i.days_of_stock.as_ref().map(dec_to_f64),
        }
    }
}

/// Inventory movement summary.
#[pyclass]
#[derive(Clone)]
pub struct InventoryMovement {
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    units_sold: u32,
    #[pyo3(get)]
    units_received: u32,
    #[pyo3(get)]
    units_returned: u32,
    #[pyo3(get)]
    units_adjusted: i32,
    #[pyo3(get)]
    net_change: i32,
}

impl From<stateset_core::InventoryMovement> for InventoryMovement {
    fn from(m: stateset_core::InventoryMovement) -> Self {
        Self {
            sku: m.sku,
            name: m.name,
            units_sold: m.units_sold as u32,
            units_received: m.units_received as u32,
            units_returned: m.units_returned as u32,
            units_adjusted: m.units_adjusted as i32,
            net_change: m.net_change as i32,
        }
    }
}

/// Order status breakdown.
#[pyclass]
#[derive(Clone)]
pub struct OrderStatusBreakdown {
    #[pyo3(get)]
    pending: u32,
    #[pyo3(get)]
    confirmed: u32,
    #[pyo3(get)]
    processing: u32,
    #[pyo3(get)]
    shipped: u32,
    #[pyo3(get)]
    delivered: u32,
    #[pyo3(get)]
    cancelled: u32,
    #[pyo3(get)]
    refunded: u32,
}

impl From<stateset_core::OrderStatusBreakdown> for OrderStatusBreakdown {
    fn from(b: stateset_core::OrderStatusBreakdown) -> Self {
        Self {
            pending: b.pending as u32,
            confirmed: b.confirmed as u32,
            processing: b.processing as u32,
            shipped: b.shipped as u32,
            delivered: b.delivered as u32,
            cancelled: b.cancelled as u32,
            refunded: b.refunded as u32,
        }
    }
}

/// Order fulfillment metrics.
#[pyclass]
#[derive(Clone)]
pub struct FulfillmentMetrics {
    #[pyo3(get)]
    avg_time_to_ship_hours: Option<f64>,
    #[pyo3(get)]
    avg_time_to_deliver_hours: Option<f64>,
    #[pyo3(get)]
    on_time_shipping_percent: Option<f64>,
    #[pyo3(get)]
    on_time_delivery_percent: Option<f64>,
    #[pyo3(get)]
    shipped_today: u32,
    #[pyo3(get)]
    awaiting_shipment: u32,
}

impl From<stateset_core::FulfillmentMetrics> for FulfillmentMetrics {
    fn from(m: stateset_core::FulfillmentMetrics) -> Self {
        Self {
            avg_time_to_ship_hours: m.avg_time_to_ship_hours.as_ref().map(dec_to_f64),
            avg_time_to_deliver_hours: m.avg_time_to_deliver_hours.as_ref().map(dec_to_f64),
            on_time_shipping_percent: m.on_time_shipping_percent.as_ref().map(dec_to_f64),
            on_time_delivery_percent: m.on_time_delivery_percent.as_ref().map(dec_to_f64),
            shipped_today: m.shipped_today as u32,
            awaiting_shipment: m.awaiting_shipment as u32,
        }
    }
}

/// Return metrics.
#[pyclass]
#[derive(Clone)]
pub struct ReturnMetrics {
    #[pyo3(get)]
    total_returns: u32,
    #[pyo3(get)]
    return_rate_percent: f64,
    #[pyo3(get)]
    total_refunded: f64,
}

impl From<stateset_core::ReturnMetrics> for ReturnMetrics {
    fn from(m: stateset_core::ReturnMetrics) -> Self {
        Self {
            total_returns: m.total_returns as u32,
            return_rate_percent: dec_to_f64(&m.return_rate_percent),
            total_refunded: dec_to_f64(&m.total_refunded),
        }
    }
}

fn trend_to_string(t: &stateset_core::Trend) -> String {
    match t {
        stateset_core::Trend::Rising => "rising".to_string(),
        stateset_core::Trend::Stable => "stable".to_string(),
        stateset_core::Trend::Falling => "falling".to_string(),
    }
}

/// Demand forecast for a SKU.
#[pyclass]
#[derive(Clone)]
pub struct DemandForecast {
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    average_daily_demand: f64,
    #[pyo3(get)]
    forecasted_demand: f64,
    #[pyo3(get)]
    confidence: f64,
    #[pyo3(get)]
    current_stock: f64,
    #[pyo3(get)]
    days_until_stockout: Option<i32>,
    #[pyo3(get)]
    recommended_reorder_qty: Option<f64>,
    #[pyo3(get)]
    trend: String,
}

impl From<stateset_core::DemandForecast> for DemandForecast {
    fn from(f: stateset_core::DemandForecast) -> Self {
        Self {
            sku: f.sku,
            name: f.name,
            average_daily_demand: dec_to_f64(&f.average_daily_demand),
            forecasted_demand: dec_to_f64(&f.forecasted_demand),
            confidence: dec_to_f64(&f.confidence),
            current_stock: dec_to_f64(&f.current_stock),
            days_until_stockout: f.days_until_stockout,
            recommended_reorder_qty: f.recommended_reorder_qty.as_ref().map(dec_to_f64),
            trend: trend_to_string(&f.trend),
        }
    }
}

/// Revenue forecast.
#[pyclass]
#[derive(Clone)]
pub struct RevenueForecast {
    #[pyo3(get)]
    period: String,
    #[pyo3(get)]
    forecasted_revenue: f64,
    #[pyo3(get)]
    lower_bound: f64,
    #[pyo3(get)]
    upper_bound: f64,
    #[pyo3(get)]
    confidence_level: f64,
    #[pyo3(get)]
    based_on_periods: u32,
}

impl From<stateset_core::RevenueForecast> for RevenueForecast {
    fn from(f: stateset_core::RevenueForecast) -> Self {
        Self {
            period: f.period,
            forecasted_revenue: dec_to_f64(&f.forecasted_revenue),
            lower_bound: dec_to_f64(&f.lower_bound),
            upper_bound: dec_to_f64(&f.upper_bound),
            confidence_level: dec_to_f64(&f.confidence_level),
            based_on_periods: f.based_on_periods,
        }
    }
}

// ============================================================================
// Analytics API
// ============================================================================

/// Business intelligence and forecasting operations.
#[pyclass]
pub struct Analytics {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl Analytics {
    /// Get sales summary.
    #[pyo3(signature = (period=None, limit=None))]
    fn sales_summary(&self, period: Option<String>, limit: Option<u32>) -> PyResult<SalesSummary> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, limit);
        let summary = commerce
            .analytics()
            .sales_summary(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get sales summary: {}", e)))?;

        Ok(summary.into())
    }

    /// Get revenue by period.
    #[pyo3(signature = (period=None, granularity=None))]
    fn revenue_by_period(
        &self,
        period: Option<String>,
        granularity: Option<String>,
    ) -> PyResult<Vec<RevenueByPeriod>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, granularity, None);
        let rows = commerce
            .analytics()
            .revenue_by_period(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get revenue: {}", e)))?;

        Ok(rows.into_iter().map(|r| r.into()).collect())
    }

    /// Get top selling products.
    #[pyo3(signature = (period=None, limit=None))]
    fn top_products(&self, period: Option<String>, limit: Option<u32>) -> PyResult<Vec<TopProduct>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, limit);
        let rows = commerce
            .analytics()
            .top_products(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get top products: {}", e)))?;

        Ok(rows.into_iter().map(|p| p.into()).collect())
    }

    /// Get product performance with period comparison.
    #[pyo3(signature = (period=None, limit=None))]
    fn product_performance(&self, period: Option<String>, limit: Option<u32>) -> PyResult<Vec<ProductPerformance>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, limit);
        let rows = commerce
            .analytics()
            .product_performance(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get product performance: {}", e)))?;

        Ok(rows.into_iter().map(|p| p.into()).collect())
    }

    /// Get customer metrics.
    #[pyo3(signature = (period=None))]
    fn customer_metrics(&self, period: Option<String>) -> PyResult<CustomerMetrics> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, None);
        let metrics = commerce
            .analytics()
            .customer_metrics(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get customer metrics: {}", e)))?;

        Ok(metrics.into())
    }

    /// Get top customers by spend.
    #[pyo3(signature = (period=None, limit=None))]
    fn top_customers(&self, period: Option<String>, limit: Option<u32>) -> PyResult<Vec<TopCustomer>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, limit);
        let rows = commerce
            .analytics()
            .top_customers(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get top customers: {}", e)))?;

        Ok(rows.into_iter().map(|c| c.into()).collect())
    }

    /// Get inventory health summary.
    fn inventory_health(&self) -> PyResult<InventoryHealth> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let health = commerce
            .analytics()
            .inventory_health()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get inventory health: {}", e)))?;

        Ok(health.into())
    }

    /// Get low stock items.
    #[pyo3(signature = (threshold=None))]
    fn low_stock_items(&self, threshold: Option<f64>) -> PyResult<Vec<LowStockItem>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let threshold_dec = match threshold {
            Some(v) => Some(
                Decimal::from_f64_retain(v)
                    .ok_or_else(|| PyValueError::new_err("Invalid threshold"))?,
            ),
            None => None,
        };

        let rows = commerce
            .analytics()
            .low_stock_items(threshold_dec)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get low stock items: {}", e)))?;

        Ok(rows.into_iter().map(|i| i.into()).collect())
    }

    /// Get inventory movement summary.
    #[pyo3(signature = (period=None))]
    fn inventory_movement(&self, period: Option<String>) -> PyResult<Vec<InventoryMovement>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, None);
        let rows = commerce
            .analytics()
            .inventory_movement(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get inventory movement: {}", e)))?;

        Ok(rows.into_iter().map(|m| m.into()).collect())
    }

    /// Get order status breakdown.
    #[pyo3(signature = (period=None))]
    fn order_status_breakdown(&self, period: Option<String>) -> PyResult<OrderStatusBreakdown> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, None);
        let breakdown = commerce
            .analytics()
            .order_status_breakdown(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get order status breakdown: {}", e)))?;

        Ok(breakdown.into())
    }

    /// Get fulfillment metrics.
    #[pyo3(signature = (period=None))]
    fn fulfillment_metrics(&self, period: Option<String>) -> PyResult<FulfillmentMetrics> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, None);
        let metrics = commerce
            .analytics()
            .fulfillment_metrics(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get fulfillment metrics: {}", e)))?;

        Ok(metrics.into())
    }

    /// Get return metrics.
    #[pyo3(signature = (period=None))]
    fn return_metrics(&self, period: Option<String>) -> PyResult<ReturnMetrics> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let q = build_analytics_query(period, None, None);
        let metrics = commerce
            .analytics()
            .return_metrics(q)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get return metrics: {}", e)))?;

        Ok(metrics.into())
    }

    /// Get demand forecast.
    #[pyo3(signature = (skus=None, days_ahead=None))]
    fn demand_forecast(
        &self,
        skus: Option<Vec<String>>,
        days_ahead: Option<u32>,
    ) -> PyResult<Vec<DemandForecast>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let forecasts = commerce
            .analytics()
            .demand_forecast(skus, days_ahead.unwrap_or(30))
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get demand forecast: {}", e)))?;

        Ok(forecasts.into_iter().map(|f| f.into()).collect())
    }

    /// Get revenue forecast.
    #[pyo3(signature = (periods_ahead=None, granularity=None))]
    fn revenue_forecast(
        &self,
        periods_ahead: Option<u32>,
        granularity: Option<String>,
    ) -> PyResult<Vec<RevenueForecast>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let gran = granularity
            .as_deref()
            .map(parse_time_granularity)
            .unwrap_or(stateset_core::TimeGranularity::Month);

        let forecasts = commerce
            .analytics()
            .revenue_forecast(periods_ahead.unwrap_or(3), gran)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get revenue forecast: {}", e)))?;

        Ok(forecasts.into_iter().map(|f| f.into()).collect())
    }
}

// ============================================================================
// Currency Types + API
// ============================================================================

fn parse_currency(code: &str) -> PyResult<stateset_core::Currency> {
    use std::str::FromStr;
    stateset_core::Currency::from_str(code)
        .map_err(|e| PyValueError::new_err(format!("Invalid currency code '{}': {}", code, e)))
}

fn parse_rounding_mode(mode: &str) -> stateset_core::RoundingMode {
    match mode.to_lowercase().as_str() {
        "half_down" => stateset_core::RoundingMode::HalfDown,
        "up" => stateset_core::RoundingMode::Up,
        "down" => stateset_core::RoundingMode::Down,
        "half_even" => stateset_core::RoundingMode::HalfEven,
        _ => stateset_core::RoundingMode::HalfUp,
    }
}

fn rounding_mode_to_string(mode: &stateset_core::RoundingMode) -> String {
    match mode {
        stateset_core::RoundingMode::HalfUp => "half_up".to_string(),
        stateset_core::RoundingMode::HalfDown => "half_down".to_string(),
        stateset_core::RoundingMode::Up => "up".to_string(),
        stateset_core::RoundingMode::Down => "down".to_string(),
        stateset_core::RoundingMode::HalfEven => "half_even".to_string(),
    }
}

/// Exchange rate between currencies.
#[pyclass]
#[derive(Clone)]
pub struct ExchangeRate {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    base_currency: String,
    #[pyo3(get)]
    quote_currency: String,
    #[pyo3(get)]
    rate: f64,
    #[pyo3(get)]
    source: String,
    #[pyo3(get)]
    rate_at: String,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::ExchangeRate> for ExchangeRate {
    fn from(r: stateset_core::ExchangeRate) -> Self {
        Self {
            id: r.id.to_string(),
            base_currency: r.base_currency.code().to_string(),
            quote_currency: r.quote_currency.code().to_string(),
            rate: dec_to_f64(&r.rate),
            source: r.source,
            rate_at: r.rate_at.to_rfc3339(),
            created_at: r.created_at.to_rfc3339(),
            updated_at: r.updated_at.to_rfc3339(),
        }
    }
}

/// Result of a currency conversion.
#[pyclass]
#[derive(Clone)]
pub struct ConversionResult {
    #[pyo3(get)]
    original_amount: f64,
    #[pyo3(get)]
    original_currency: String,
    #[pyo3(get)]
    converted_amount: f64,
    #[pyo3(get)]
    target_currency: String,
    #[pyo3(get)]
    rate: f64,
    #[pyo3(get)]
    inverse_rate: f64,
    #[pyo3(get)]
    rate_at: String,
}

impl From<stateset_core::ConversionResult> for ConversionResult {
    fn from(r: stateset_core::ConversionResult) -> Self {
        Self {
            original_amount: dec_to_f64(&r.original_amount),
            original_currency: r.original_currency.code().to_string(),
            converted_amount: dec_to_f64(&r.converted_amount),
            target_currency: r.target_currency.code().to_string(),
            rate: dec_to_f64(&r.rate),
            inverse_rate: dec_to_f64(&r.inverse_rate),
            rate_at: r.rate_at.to_rfc3339(),
        }
    }
}

/// Store currency settings.
#[pyclass]
#[derive(Clone)]
pub struct StoreCurrencySettings {
    #[pyo3(get)]
    base_currency: String,
    #[pyo3(get)]
    enabled_currencies: Vec<String>,
    #[pyo3(get)]
    auto_convert: bool,
    #[pyo3(get)]
    rounding_mode: String,
}

impl From<stateset_core::StoreCurrencySettings> for StoreCurrencySettings {
    fn from(s: stateset_core::StoreCurrencySettings) -> Self {
        Self {
            base_currency: s.base_currency.code().to_string(),
            enabled_currencies: s.enabled_currencies.iter().map(|c| c.code().to_string()).collect(),
            auto_convert: s.auto_convert,
            rounding_mode: rounding_mode_to_string(&s.rounding_mode),
        }
    }
}

/// Input for setting an exchange rate.
#[pyclass]
#[derive(Clone)]
pub struct SetExchangeRateInput {
    #[pyo3(get, set)]
    base_currency: String,
    #[pyo3(get, set)]
    quote_currency: String,
    #[pyo3(get, set)]
    rate: f64,
    #[pyo3(get, set)]
    source: Option<String>,
}

#[pymethods]
impl SetExchangeRateInput {
    #[new]
    #[pyo3(signature = (base_currency, quote_currency, rate, source=None))]
    fn new(
        base_currency: String,
        quote_currency: String,
        rate: f64,
        source: Option<String>,
    ) -> Self {
        Self {
            base_currency,
            quote_currency,
            rate,
            source,
        }
    }
}

/// Currency and exchange rate operations.
#[pyclass]
pub struct CurrencyOperations {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl CurrencyOperations {
    /// Get exchange rate between two currencies.
    fn get_rate(&self, from_currency: String, to_currency: String) -> PyResult<Option<ExchangeRate>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let rate = commerce
            .currency()
            .get_rate(parse_currency(&from_currency)?, parse_currency(&to_currency)?)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get rate: {}", e)))?;

        Ok(rate.map(|r| r.into()))
    }

    /// Get all exchange rates for a base currency.
    fn get_rates_for(&self, base_currency: String) -> PyResult<Vec<ExchangeRate>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let rates = commerce
            .currency()
            .get_rates_for(parse_currency(&base_currency)?)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get rates: {}", e)))?;

        Ok(rates.into_iter().map(|r| r.into()).collect())
    }

    /// List exchange rates with optional filtering.
    #[pyo3(signature = (base_currency=None, quote_currency=None))]
    fn list_rates(
        &self,
        base_currency: Option<String>,
        quote_currency: Option<String>,
    ) -> PyResult<Vec<ExchangeRate>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let base = match base_currency {
            Some(c) => Some(parse_currency(&c)?),
            None => None,
        };
        let quote = match quote_currency {
            Some(c) => Some(parse_currency(&c)?),
            None => None,
        };

        let rates = commerce
            .currency()
            .list_rates(stateset_core::ExchangeRateFilter {
                base_currency: base,
                quote_currency: quote,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list rates: {}", e)))?;

        Ok(rates.into_iter().map(|r| r.into()).collect())
    }

    /// Set an exchange rate.
    #[pyo3(signature = (base_currency, quote_currency, rate, source=None))]
    fn set_rate(
        &self,
        base_currency: String,
        quote_currency: String,
        rate: f64,
        source: Option<String>,
    ) -> PyResult<ExchangeRate> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let rate_dec = Decimal::from_f64_retain(rate)
            .ok_or_else(|| PyValueError::new_err("Invalid exchange rate"))?;

        let rate = commerce
            .currency()
            .set_rate(stateset_core::SetExchangeRate {
                base_currency: parse_currency(&base_currency)?,
                quote_currency: parse_currency(&quote_currency)?,
                rate: rate_dec,
                source,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to set rate: {}", e)))?;

        Ok(rate.into())
    }

    /// Set multiple exchange rates.
    fn set_rates(&self, rates: Vec<SetExchangeRateInput>) -> PyResult<Vec<ExchangeRate>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let mut inputs = Vec::with_capacity(rates.len());
        for r in rates {
            let rate_dec = Decimal::from_f64_retain(r.rate)
                .ok_or_else(|| PyValueError::new_err("Invalid exchange rate"))?;

            inputs.push(stateset_core::SetExchangeRate {
                base_currency: parse_currency(&r.base_currency)?,
                quote_currency: parse_currency(&r.quote_currency)?,
                rate: rate_dec,
                source: r.source,
            });
        }

        let results = commerce
            .currency()
            .set_rates(inputs)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to set rates: {}", e)))?;

        Ok(results.into_iter().map(|r| r.into()).collect())
    }

    /// Delete an exchange rate by ID.
    fn delete_rate(&self, id: String) -> PyResult<()> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        commerce
            .currency()
            .delete_rate(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to delete rate: {}", e)))?;

        Ok(())
    }

    /// Convert an amount from one currency to another.
    fn convert(&self, from_currency: String, to_currency: String, amount: f64) -> PyResult<ConversionResult> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let amount_dec = Decimal::from_f64_retain(amount)
            .ok_or_else(|| PyValueError::new_err("Invalid amount"))?;

        let result = commerce
            .currency()
            .convert(stateset_core::ConvertCurrency {
                from: parse_currency(&from_currency)?,
                to: parse_currency(&to_currency)?,
                amount: amount_dec,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to convert currency: {}", e)))?;

        Ok(result.into())
    }

    /// Get store currency settings.
    fn get_settings(&self) -> PyResult<StoreCurrencySettings> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let settings = commerce
            .currency()
            .get_settings()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get settings: {}", e)))?;

        Ok(settings.into())
    }

    /// Update store currency settings.
    #[pyo3(signature = (base_currency, enabled_currencies, auto_convert=None, rounding_mode=None))]
    fn update_settings(
        &self,
        base_currency: String,
        enabled_currencies: Vec<String>,
        auto_convert: Option<bool>,
        rounding_mode: Option<String>,
    ) -> PyResult<StoreCurrencySettings> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let mut enabled = Vec::with_capacity(enabled_currencies.len());
        for c in &enabled_currencies {
            enabled.push(parse_currency(c)?);
        }

        let settings = commerce
            .currency()
            .update_settings(stateset_core::StoreCurrencySettings {
                base_currency: parse_currency(&base_currency)?,
                enabled_currencies: enabled,
                auto_convert: auto_convert.unwrap_or(true),
                rounding_mode: rounding_mode.as_deref().map(parse_rounding_mode).unwrap_or_default(),
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to update settings: {}", e)))?;

        Ok(settings.into())
    }

    /// Set the store's base currency.
    fn set_base_currency(&self, currency_code: String) -> PyResult<StoreCurrencySettings> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let settings = commerce
            .currency()
            .set_base_currency(parse_currency(&currency_code)?)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to set base currency: {}", e)))?;

        Ok(settings.into())
    }

    /// Enable currencies for the store.
    fn enable_currencies(&self, currency_codes: Vec<String>) -> PyResult<StoreCurrencySettings> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let mut currencies = Vec::with_capacity(currency_codes.len());
        for c in &currency_codes {
            currencies.push(parse_currency(c)?);
        }

        let settings = commerce
            .currency()
            .enable_currencies(currencies)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to enable currencies: {}", e)))?;

        Ok(settings.into())
    }

    /// Check if a currency is enabled for the store.
    fn is_enabled(&self, currency_code: String) -> PyResult<bool> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        commerce
            .currency()
            .is_enabled(parse_currency(&currency_code)?)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to check currency: {}", e)))
    }

    /// Get the store's base currency code.
    fn base_currency(&self) -> PyResult<String> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let currency = commerce
            .currency()
            .base_currency()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get base currency: {}", e)))?;

        Ok(currency.code().to_string())
    }

    /// Get enabled currency codes.
    fn enabled_currencies(&self) -> PyResult<Vec<String>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let currencies = commerce
            .currency()
            .enabled_currencies()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get enabled currencies: {}", e)))?;

        Ok(currencies.iter().map(|c| c.code().to_string()).collect())
    }

    /// Format an amount with currency symbol.
    fn format(&self, amount: f64, currency_code: String) -> PyResult<String> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let amount_dec = Decimal::from_f64_retain(amount)
            .ok_or_else(|| PyValueError::new_err("Invalid amount"))?;

        Ok(commerce
            .currency()
            .format(amount_dec, parse_currency(&currency_code)?))
    }
}

// ============================================================================
// Subscription Types
// ============================================================================

/// Subscription plan data.
#[pyclass]
#[derive(Clone)]
pub struct SubscriptionPlan {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    code: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    description: Option<String>,
    #[pyo3(get)]
    billing_interval: String,
    #[pyo3(get)]
    billing_interval_count: i32,
    #[pyo3(get)]
    price: f64,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    setup_fee: f64,
    #[pyo3(get)]
    trial_days: i32,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::SubscriptionPlan> for SubscriptionPlan {
    fn from(p: stateset_core::SubscriptionPlan) -> Self {
        Self {
            id: p.id.to_string(),
            code: p.code,
            name: p.name,
            description: p.description,
            billing_interval: format!("{:?}", p.billing_interval).to_lowercase(),
            billing_interval_count: 1, // Default to 1 since core doesn't have this field
            price: to_f64_or_nan(p.price),
            currency: p.currency,
            setup_fee: p.setup_fee.map(|d| to_f64_or_nan(d)).unwrap_or(0.0),
            trial_days: p.trial_days,
            status: format!("{:?}", p.status).to_lowercase(),
            created_at: p.created_at.to_rfc3339(),
            updated_at: p.updated_at.to_rfc3339(),
        }
    }
}

/// Subscription data.
#[pyclass]
#[derive(Clone)]
pub struct Subscription {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    subscription_number: String,
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    plan_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    current_period_start: String,
    #[pyo3(get)]
    current_period_end: String,
    #[pyo3(get)]
    trial_ends_at: Option<String>,
    #[pyo3(get)]
    cancelled_at: Option<String>,
    #[pyo3(get)]
    ends_at: Option<String>,
    #[pyo3(get)]
    price: f64,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::Subscription> for Subscription {
    fn from(s: stateset_core::Subscription) -> Self {
        Self {
            id: s.id.to_string(),
            subscription_number: s.subscription_number,
            customer_id: s.customer_id.to_string(),
            plan_id: s.plan_id.to_string(),
            status: format!("{:?}", s.status).to_lowercase(),
            current_period_start: s.current_period_start.to_rfc3339(),
            current_period_end: s.current_period_end.to_rfc3339(),
            trial_ends_at: s.trial_ends_at.map(|d| d.to_rfc3339()),
            cancelled_at: s.cancelled_at.map(|d| d.to_rfc3339()),
            ends_at: s.ends_at.map(|d| d.to_rfc3339()),
            price: to_f64_or_nan(s.price),
            currency: s.currency,
            created_at: s.created_at.to_rfc3339(),
            updated_at: s.updated_at.to_rfc3339(),
        }
    }
}

/// Billing cycle data.
#[pyclass]
#[derive(Clone)]
pub struct BillingCycle {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    cycle_number: i32,
    #[pyo3(get)]
    subscription_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    period_start: String,
    #[pyo3(get)]
    period_end: String,
    #[pyo3(get)]
    total: f64,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    payment_id: Option<String>,
    #[pyo3(get)]
    invoice_id: Option<String>,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::BillingCycle> for BillingCycle {
    fn from(c: stateset_core::BillingCycle) -> Self {
        Self {
            id: c.id.to_string(),
            cycle_number: c.cycle_number,
            subscription_id: c.subscription_id.to_string(),
            status: format!("{:?}", c.status).to_lowercase(),
            period_start: c.period_start.to_rfc3339(),
            period_end: c.period_end.to_rfc3339(),
            total: to_f64_or_nan(c.total),
            currency: c.currency,
            payment_id: c.payment_id,
            invoice_id: c.invoice_id.map(|id| id.to_string()),
            created_at: c.created_at.to_rfc3339(),
            updated_at: c.updated_at.to_rfc3339(),
        }
    }
}

/// Subscription event data.
#[pyclass]
#[derive(Clone)]
pub struct SubscriptionEvent {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    subscription_id: String,
    #[pyo3(get)]
    event_type: String,
    #[pyo3(get)]
    description: String,
    #[pyo3(get)]
    created_at: String,
}

impl From<stateset_core::SubscriptionEvent> for SubscriptionEvent {
    fn from(e: stateset_core::SubscriptionEvent) -> Self {
        Self {
            id: e.id.to_string(),
            subscription_id: e.subscription_id.to_string(),
            event_type: format!("{:?}", e.event_type).to_lowercase(),
            description: e.description,
            created_at: e.created_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Subscriptions API
// ============================================================================

/// Subscriptions API for subscription management.
#[pyclass]
pub struct Subscriptions {
    commerce: Arc<Mutex<RustCommerce>>,
}

fn parse_billing_interval(s: &str) -> PyResult<stateset_core::BillingInterval> {
    match s.to_lowercase().as_str() {
        "weekly" => Ok(stateset_core::BillingInterval::Weekly),
        "biweekly" => Ok(stateset_core::BillingInterval::Biweekly),
        "monthly" => Ok(stateset_core::BillingInterval::Monthly),
        "quarterly" => Ok(stateset_core::BillingInterval::Quarterly),
        "annual" | "yearly" => Ok(stateset_core::BillingInterval::Annual),
        _ => Err(PyValueError::new_err(format!("Invalid billing interval: {}", s))),
    }
}

#[pymethods]
impl Subscriptions {
    // ========================================================================
    // Subscription Plans
    // ========================================================================

    /// Create a subscription plan.
    #[pyo3(signature = (code, name, price, billing_interval=None, billing_interval_count=None, description=None, currency=None, setup_fee=None, trial_days=None))]
    fn create_plan(
        &self,
        code: String,
        name: String,
        price: f64,
        billing_interval: Option<String>,
        billing_interval_count: Option<i32>,
        description: Option<String>,
        currency: Option<String>,
        setup_fee: Option<f64>,
        trial_days: Option<i32>,
    ) -> PyResult<SubscriptionPlan> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let interval = billing_interval.as_deref()
            .map(parse_billing_interval)
            .transpose()?
            .unwrap_or(stateset_core::BillingInterval::Monthly);

        let plan = commerce
            .subscriptions()
            .create_plan(stateset_core::CreateSubscriptionPlan {
                code: Some(code),
                name,
                description,
                billing_interval: interval,
                custom_interval_days: billing_interval_count,
                price: Decimal::from_f64_retain(price).ok_or_else(|| PyValueError::new_err("Invalid price"))?,
                currency: Some(currency.unwrap_or_else(|| "USD".to_string())),
                setup_fee: setup_fee.map(|f| Decimal::from_f64_retain(f).unwrap_or_default()),
                trial_days: trial_days,
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create plan: {}", e)))?;

        Ok(plan.into())
    }

    /// Get a subscription plan by ID.
    fn get_plan(&self, id: String) -> PyResult<Option<SubscriptionPlan>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let plan = commerce
            .subscriptions()
            .get_plan(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get plan: {}", e)))?;

        Ok(plan.map(|p| p.into()))
    }

    /// List all subscription plans.
    #[pyo3(signature = (status=None, billing_interval=None, limit=None, offset=None))]
    fn list_plans(
        &self,
        status: Option<String>,
        billing_interval: Option<String>,
        limit: Option<u32>,
        offset: Option<u32>,
    ) -> PyResult<Vec<SubscriptionPlan>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let interval = billing_interval.as_deref()
            .map(parse_billing_interval)
            .transpose()?;

        let plan_status = status.as_deref().map(|s| match s.to_lowercase().as_str() {
            "draft" => stateset_core::PlanStatus::Draft,
            "active" => stateset_core::PlanStatus::Active,
            "archived" => stateset_core::PlanStatus::Archived,
            _ => stateset_core::PlanStatus::Draft,
        });

        let plans = commerce
            .subscriptions()
            .list_plans(stateset_core::SubscriptionPlanFilter {
                status: plan_status,
                billing_interval: interval,
                search: None,
                limit,
                offset,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list plans: {}", e)))?;

        Ok(plans.into_iter().map(|p| p.into()).collect())
    }

    /// Activate a subscription plan.
    fn activate_plan(&self, id: String) -> PyResult<SubscriptionPlan> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let plan = commerce
            .subscriptions()
            .activate_plan(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to activate plan: {}", e)))?;

        Ok(plan.into())
    }

    /// Archive a subscription plan.
    fn archive_plan(&self, id: String) -> PyResult<SubscriptionPlan> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let plan = commerce
            .subscriptions()
            .archive_plan(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to archive plan: {}", e)))?;

        Ok(plan.into())
    }

    // ========================================================================
    // Subscriptions
    // ========================================================================

    /// Subscribe a customer to a plan.
    #[pyo3(signature = (customer_id, plan_id, skip_trial=None, price=None))]
    fn subscribe(
        &self,
        customer_id: String,
        plan_id: String,
        skip_trial: Option<bool>,
        price: Option<f64>,
    ) -> PyResult<Subscription> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let cust_uuid = customer_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;
        let plan_uuid = plan_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid plan UUID"))?;

        let subscription = commerce
            .subscriptions()
            .subscribe(stateset_core::CreateSubscription {
                customer_id: cust_uuid,
                plan_id: plan_uuid,
                skip_trial,
                price: price.and_then(|p| Decimal::from_f64_retain(p)),
                ..Default::default()
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create subscription: {}", e)))?;

        Ok(subscription.into())
    }

    /// Get a subscription by ID.
    fn get(&self, id: String) -> PyResult<Option<Subscription>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let subscription = commerce
            .subscriptions()
            .get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get subscription: {}", e)))?;

        Ok(subscription.map(|s| s.into()))
    }

    /// Get a subscription by number.
    fn get_by_number(&self, number: String) -> PyResult<Option<Subscription>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let subscription = commerce
            .subscriptions()
            .get_by_number(&number)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get subscription: {}", e)))?;

        Ok(subscription.map(|s| s.into()))
    }

    /// List subscriptions.
    #[pyo3(signature = (customer_id=None, plan_id=None, status=None, limit=None, offset=None))]
    fn list(
        &self,
        customer_id: Option<String>,
        plan_id: Option<String>,
        status: Option<String>,
        limit: Option<u32>,
        offset: Option<u32>,
    ) -> PyResult<Vec<Subscription>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let cust_uuid = customer_id.map(|id| id.parse()).transpose()
            .map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;
        let p_uuid = plan_id.map(|id| id.parse()).transpose()
            .map_err(|_| PyValueError::new_err("Invalid plan UUID"))?;

        let sub_status = status.as_deref().map(|s| match s.to_lowercase().as_str() {
            "pending" => stateset_core::SubscriptionStatus::Pending,
            "trial" | "trialing" => stateset_core::SubscriptionStatus::Trial,
            "active" => stateset_core::SubscriptionStatus::Active,
            "paused" => stateset_core::SubscriptionStatus::Paused,
            "past_due" | "pastdue" => stateset_core::SubscriptionStatus::PastDue,
            "cancelled" | "canceled" => stateset_core::SubscriptionStatus::Cancelled,
            "expired" => stateset_core::SubscriptionStatus::Expired,
            _ => stateset_core::SubscriptionStatus::Active,
        });

        let subscriptions = commerce
            .subscriptions()
            .list(stateset_core::SubscriptionFilter {
                customer_id: cust_uuid,
                plan_id: p_uuid,
                status: sub_status,
                from_date: None,
                to_date: None,
                search: None,
                limit,
                offset,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list subscriptions: {}", e)))?;

        Ok(subscriptions.into_iter().map(|s| s.into()).collect())
    }

    /// Pause a subscription.
    #[pyo3(signature = (id, reason=None))]
    fn pause(&self, id: String, reason: Option<String>) -> PyResult<Subscription> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let subscription = commerce
            .subscriptions()
            .pause(uuid, stateset_core::PauseSubscription {
                reason,
                resume_at: None,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to pause subscription: {}", e)))?;

        Ok(subscription.into())
    }

    /// Resume a paused subscription.
    fn resume(&self, id: String) -> PyResult<Subscription> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let subscription = commerce
            .subscriptions()
            .resume(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to resume subscription: {}", e)))?;

        Ok(subscription.into())
    }

    /// Cancel a subscription.
    #[pyo3(signature = (id, immediate=None, reason=None))]
    fn cancel(&self, id: String, immediate: Option<bool>, reason: Option<String>) -> PyResult<Subscription> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let subscription = commerce
            .subscriptions()
            .cancel(uuid, stateset_core::CancelSubscription {
                immediate,
                reason: reason.clone(),
                feedback: None,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to cancel subscription: {}", e)))?;

        Ok(subscription.into())
    }

    /// Skip the next billing cycle.
    #[pyo3(signature = (id, reason=None))]
    fn skip_next_cycle(&self, id: String, reason: Option<String>) -> PyResult<Subscription> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let subscription = commerce
            .subscriptions()
            .skip_next_cycle(uuid, stateset_core::SkipBillingCycle { reason })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to skip billing cycle: {}", e)))?;

        Ok(subscription.into())
    }

    // ========================================================================
    // Billing Cycles
    // ========================================================================

    /// List billing cycles.
    #[pyo3(signature = (subscription_id=None, status=None, limit=None, offset=None))]
    fn list_billing_cycles(
        &self,
        subscription_id: Option<String>,
        status: Option<String>,
        limit: Option<u32>,
        offset: Option<u32>,
    ) -> PyResult<Vec<BillingCycle>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let sub_uuid = subscription_id.map(|id| id.parse()).transpose()
            .map_err(|_| PyValueError::new_err("Invalid subscription UUID"))?;

        let cycle_status = status.as_deref().map(|s| match s.to_lowercase().as_str() {
            "scheduled" | "pending" => stateset_core::BillingCycleStatus::Scheduled,
            "processing" => stateset_core::BillingCycleStatus::Processing,
            "paid" => stateset_core::BillingCycleStatus::Paid,
            "failed" => stateset_core::BillingCycleStatus::Failed,
            "skipped" => stateset_core::BillingCycleStatus::Skipped,
            "refunded" => stateset_core::BillingCycleStatus::Refunded,
            "voided" => stateset_core::BillingCycleStatus::Voided,
            _ => stateset_core::BillingCycleStatus::Scheduled,
        });

        let cycles = commerce
            .subscriptions()
            .list_billing_cycles(stateset_core::BillingCycleFilter {
                subscription_id: sub_uuid,
                status: cycle_status,
                from_date: None,
                to_date: None,
                limit,
                offset,
            })
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list billing cycles: {}", e)))?;

        Ok(cycles.into_iter().map(|c| c.into()).collect())
    }

    /// Get a billing cycle by ID.
    fn get_billing_cycle(&self, id: String) -> PyResult<Option<BillingCycle>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let cycle = commerce
            .subscriptions()
            .get_billing_cycle(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get billing cycle: {}", e)))?;

        Ok(cycle.map(|c| c.into()))
    }

    // ========================================================================
    // Events
    // ========================================================================

    /// Get events for a subscription.
    #[pyo3(signature = (subscription_id, limit=None))]
    fn get_events(&self, subscription_id: String, limit: Option<u32>) -> PyResult<Vec<SubscriptionEvent>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = subscription_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let events = commerce
            .subscriptions()
            .get_events(uuid, limit)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get events: {}", e)))?;

        Ok(events.into_iter().map(|e| e.into()).collect())
    }
}

// ============================================================================
// Promotions Types
// ============================================================================

/// Promotion output
#[pyclass]
#[derive(Clone)]
pub struct Promotion {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    code: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    description: Option<String>,
    #[pyo3(get)]
    promotion_type: String,
    #[pyo3(get)]
    trigger: String,
    #[pyo3(get)]
    target: String,
    #[pyo3(get)]
    stacking: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    percentage_off: Option<f64>,
    #[pyo3(get)]
    fixed_amount_off: Option<f64>,
    #[pyo3(get)]
    max_discount_amount: Option<f64>,
    #[pyo3(get)]
    buy_quantity: Option<i32>,
    #[pyo3(get)]
    get_quantity: Option<i32>,
    #[pyo3(get)]
    starts_at: String,
    #[pyo3(get)]
    ends_at: Option<String>,
    #[pyo3(get)]
    total_usage_limit: Option<i32>,
    #[pyo3(get)]
    per_customer_limit: Option<i32>,
    #[pyo3(get)]
    usage_count: i32,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    priority: i32,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::Promotion> for Promotion {
    fn from(p: stateset_core::Promotion) -> Self {
        Self {
            id: p.id.to_string(),
            code: p.code,
            name: p.name,
            description: p.description,
            promotion_type: format!("{:?}", p.promotion_type).to_lowercase(),
            trigger: format!("{:?}", p.trigger).to_lowercase(),
            target: format!("{:?}", p.target).to_lowercase(),
            stacking: format!("{:?}", p.stacking).to_lowercase(),
            status: format!("{:?}", p.status).to_lowercase(),
            percentage_off: p.percentage_off.map(|d| to_f64_or_nan(d)),
            fixed_amount_off: p.fixed_amount_off.map(|d| to_f64_or_nan(d)),
            max_discount_amount: p.max_discount_amount.map(|d| to_f64_or_nan(d)),
            buy_quantity: p.buy_quantity,
            get_quantity: p.get_quantity,
            starts_at: p.starts_at.to_rfc3339(),
            ends_at: p.ends_at.map(|d| d.to_rfc3339()),
            total_usage_limit: p.total_usage_limit,
            per_customer_limit: p.per_customer_limit,
            usage_count: p.usage_count,
            currency: p.currency,
            priority: p.priority,
            created_at: p.created_at.to_rfc3339(),
            updated_at: p.updated_at.to_rfc3339(),
        }
    }
}

/// Coupon code output
#[pyclass]
#[derive(Clone)]
pub struct Coupon {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    promotion_id: String,
    #[pyo3(get)]
    code: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    usage_limit: Option<i32>,
    #[pyo3(get)]
    per_customer_limit: Option<i32>,
    #[pyo3(get)]
    usage_count: i32,
    #[pyo3(get)]
    starts_at: Option<String>,
    #[pyo3(get)]
    ends_at: Option<String>,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::CouponCode> for Coupon {
    fn from(c: stateset_core::CouponCode) -> Self {
        Self {
            id: c.id.to_string(),
            promotion_id: c.promotion_id.to_string(),
            code: c.code,
            status: format!("{:?}", c.status).to_lowercase(),
            usage_limit: c.usage_limit,
            per_customer_limit: c.per_customer_limit,
            usage_count: c.usage_count,
            starts_at: c.starts_at.map(|d| d.to_rfc3339()),
            ends_at: c.ends_at.map(|d| d.to_rfc3339()),
            created_at: c.created_at.to_rfc3339(),
            updated_at: c.updated_at.to_rfc3339(),
        }
    }
}

/// Result of applying promotions
#[pyclass]
#[derive(Clone)]
pub struct ApplyPromotionsResult {
    #[pyo3(get)]
    original_subtotal: f64,
    #[pyo3(get)]
    total_discount: f64,
    #[pyo3(get)]
    discounted_subtotal: f64,
    #[pyo3(get)]
    original_shipping: f64,
    #[pyo3(get)]
    shipping_discount: f64,
    #[pyo3(get)]
    final_shipping: f64,
    #[pyo3(get)]
    grand_total: f64,
    #[pyo3(get)]
    applied_promotions: Vec<AppliedPromotion>,
}

impl From<stateset_core::ApplyPromotionsResult> for ApplyPromotionsResult {
    fn from(r: stateset_core::ApplyPromotionsResult) -> Self {
        Self {
            original_subtotal: to_f64_or_nan(r.original_subtotal),
            total_discount: to_f64_or_nan(r.total_discount),
            discounted_subtotal: to_f64_or_nan(r.discounted_subtotal),
            original_shipping: to_f64_or_nan(r.original_shipping),
            shipping_discount: to_f64_or_nan(r.shipping_discount),
            final_shipping: to_f64_or_nan(r.final_shipping),
            grand_total: to_f64_or_nan(r.grand_total),
            applied_promotions: r.applied_promotions.into_iter().map(|a| a.into()).collect(),
        }
    }
}

/// An applied promotion
#[pyclass]
#[derive(Clone)]
pub struct AppliedPromotion {
    #[pyo3(get)]
    promotion_id: String,
    #[pyo3(get)]
    promotion_name: String,
    #[pyo3(get)]
    coupon_code: Option<String>,
    #[pyo3(get)]
    discount_amount: f64,
    #[pyo3(get)]
    discount_type: String,
}

impl From<stateset_core::AppliedPromotion> for AppliedPromotion {
    fn from(a: stateset_core::AppliedPromotion) -> Self {
        Self {
            promotion_id: a.promotion_id.to_string(),
            promotion_name: a.promotion_name,
            coupon_code: a.coupon_code,
            discount_amount: to_f64_or_nan(a.discount_amount),
            discount_type: format!("{:?}", a.discount_type).to_lowercase(),
        }
    }
}

/// Promotion usage record
#[pyclass]
#[derive(Clone)]
pub struct PromotionUsage {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    promotion_id: String,
    #[pyo3(get)]
    coupon_id: Option<String>,
    #[pyo3(get)]
    customer_id: Option<String>,
    #[pyo3(get)]
    order_id: Option<String>,
    #[pyo3(get)]
    cart_id: Option<String>,
    #[pyo3(get)]
    discount_amount: f64,
    #[pyo3(get)]
    currency: String,
    #[pyo3(get)]
    used_at: String,
}

impl From<stateset_core::PromotionUsage> for PromotionUsage {
    fn from(u: stateset_core::PromotionUsage) -> Self {
        Self {
            id: u.id.to_string(),
            promotion_id: u.promotion_id.to_string(),
            coupon_id: u.coupon_id.map(|id| id.to_string()),
            customer_id: u.customer_id.map(|id| id.to_string()),
            order_id: u.order_id.map(|id| id.to_string()),
            cart_id: u.cart_id.map(|id| id.to_string()),
            discount_amount: to_f64_or_nan(u.discount_amount),
            currency: u.currency,
            used_at: u.used_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Promotions API
// ============================================================================

fn parse_promotion_type(s: &str) -> stateset_core::PromotionType {
    match s.to_lowercase().as_str() {
        "percentage_off" => stateset_core::PromotionType::PercentageOff,
        "fixed_amount_off" => stateset_core::PromotionType::FixedAmountOff,
        "buy_x_get_y" | "bogo" => stateset_core::PromotionType::BuyXGetY,
        "free_shipping" => stateset_core::PromotionType::FreeShipping,
        "tiered_discount" => stateset_core::PromotionType::TieredDiscount,
        "bundle_discount" => stateset_core::PromotionType::BundleDiscount,
        _ => stateset_core::PromotionType::PercentageOff,
    }
}

fn parse_promotion_trigger(s: &str) -> stateset_core::PromotionTrigger {
    match s.to_lowercase().as_str() {
        "automatic" => stateset_core::PromotionTrigger::Automatic,
        "coupon_code" => stateset_core::PromotionTrigger::CouponCode,
        "both" => stateset_core::PromotionTrigger::Both,
        _ => stateset_core::PromotionTrigger::Automatic,
    }
}

fn parse_promotion_target(s: &str) -> stateset_core::PromotionTarget {
    match s.to_lowercase().as_str() {
        "order" => stateset_core::PromotionTarget::Order,
        "product" => stateset_core::PromotionTarget::Product,
        "category" => stateset_core::PromotionTarget::Category,
        "shipping" => stateset_core::PromotionTarget::Shipping,
        "line_item" => stateset_core::PromotionTarget::LineItem,
        _ => stateset_core::PromotionTarget::Order,
    }
}

fn parse_stacking_behavior(s: &str) -> stateset_core::StackingBehavior {
    match s.to_lowercase().as_str() {
        "stackable" => stateset_core::StackingBehavior::Stackable,
        "exclusive" => stateset_core::StackingBehavior::Exclusive,
        "selective_stack" => stateset_core::StackingBehavior::SelectiveStack,
        _ => stateset_core::StackingBehavior::Stackable,
    }
}

fn parse_promotion_status(s: &str) -> stateset_core::PromotionStatus {
    match s.to_lowercase().as_str() {
        "draft" => stateset_core::PromotionStatus::Draft,
        "scheduled" => stateset_core::PromotionStatus::Scheduled,
        "active" => stateset_core::PromotionStatus::Active,
        "paused" => stateset_core::PromotionStatus::Paused,
        "expired" => stateset_core::PromotionStatus::Expired,
        "exhausted" => stateset_core::PromotionStatus::Exhausted,
        "archived" => stateset_core::PromotionStatus::Archived,
        _ => stateset_core::PromotionStatus::Draft,
    }
}

fn parse_coupon_status(s: &str) -> stateset_core::CouponStatus {
    match s.to_lowercase().as_str() {
        "active" => stateset_core::CouponStatus::Active,
        "disabled" => stateset_core::CouponStatus::Disabled,
        "exhausted" => stateset_core::CouponStatus::Exhausted,
        "expired" => stateset_core::CouponStatus::Expired,
        _ => stateset_core::CouponStatus::Active,
    }
}

/// Promotions API for managing discounts and coupon codes
#[pyclass]
pub struct PromotionsApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl PromotionsApi {
    /// Create a new promotion.
    #[pyo3(signature = (name, promotion_type=None, trigger=None, target=None, stacking=None,
                        percentage_off=None, fixed_amount_off=None, max_discount_amount=None,
                        buy_quantity=None, get_quantity=None, starts_at=None, ends_at=None,
                        total_usage_limit=None, per_customer_limit=None, currency=None, priority=None))]
    fn create(
        &self,
        name: String,
        promotion_type: Option<String>,
        trigger: Option<String>,
        target: Option<String>,
        stacking: Option<String>,
        percentage_off: Option<f64>,
        fixed_amount_off: Option<f64>,
        max_discount_amount: Option<f64>,
        buy_quantity: Option<i32>,
        get_quantity: Option<i32>,
        starts_at: Option<String>,
        ends_at: Option<String>,
        total_usage_limit: Option<i32>,
        per_customer_limit: Option<i32>,
        currency: Option<String>,
        priority: Option<i32>,
    ) -> PyResult<Promotion> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let create = stateset_core::CreatePromotion {
            code: None,
            name,
            description: None,
            internal_notes: None,
            promotion_type: promotion_type.map(|s| parse_promotion_type(&s)).unwrap_or_default(),
            trigger: trigger.map(|s| parse_promotion_trigger(&s)).unwrap_or_default(),
            target: target.map(|s| parse_promotion_target(&s)).unwrap_or_default(),
            stacking: stacking.map(|s| parse_stacking_behavior(&s)).unwrap_or_default(),
            percentage_off: percentage_off.map(|v| Decimal::from_f64_retain(v).unwrap_or_default()),
            fixed_amount_off: fixed_amount_off.map(|v| Decimal::from_f64_retain(v).unwrap_or_default()),
            max_discount_amount: max_discount_amount.map(|v| Decimal::from_f64_retain(v).unwrap_or_default()),
            buy_quantity,
            get_quantity,
            get_discount_percent: None,
            tiers: None,
            bundle_product_ids: None,
            bundle_discount: None,
            starts_at: starts_at.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok().map(|d| d.with_timezone(&chrono::Utc))),
            ends_at: ends_at.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok().map(|d| d.with_timezone(&chrono::Utc))),
            total_usage_limit,
            per_customer_limit,
            conditions: None,
            applicable_product_ids: None,
            applicable_category_ids: None,
            applicable_skus: None,
            excluded_product_ids: None,
            excluded_category_ids: None,
            eligible_customer_ids: None,
            eligible_customer_groups: None,
            currency,
            priority,
            metadata: None,
        };

        let promo = commerce.promotions().create(create)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create promotion: {}", e)))?;

        Ok(promo.into())
    }

    /// Get a promotion by ID.
    fn get(&self, id: String) -> PyResult<Option<Promotion>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let promo = commerce.promotions().get(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get promotion: {}", e)))?;

        Ok(promo.map(|p| p.into()))
    }

    /// Get a promotion by its internal code.
    fn get_by_code(&self, code: String) -> PyResult<Option<Promotion>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let promo = commerce.promotions().get_by_code(&code)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get promotion: {}", e)))?;

        Ok(promo.map(|p| p.into()))
    }

    /// List promotions with optional filtering.
    #[pyo3(signature = (status=None, promotion_type=None, is_active=None, limit=None, offset=None))]
    fn list(
        &self,
        status: Option<String>,
        promotion_type: Option<String>,
        is_active: Option<bool>,
        limit: Option<i32>,
        offset: Option<i32>,
    ) -> PyResult<Vec<Promotion>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let filter = stateset_core::PromotionFilter {
            status: status.map(|s| parse_promotion_status(&s)),
            promotion_type: promotion_type.map(|s| parse_promotion_type(&s)),
            trigger: None,
            is_active,
            search: None,
            limit: limit.map(|v| v as u32),
            offset: offset.map(|v| v as u32),
        };

        let promos = commerce.promotions().list(filter)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list promotions: {}", e)))?;

        Ok(promos.into_iter().map(|p| p.into()).collect())
    }

    /// Activate a promotion.
    fn activate(&self, id: String) -> PyResult<Promotion> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let promo = commerce.promotions().activate(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to activate promotion: {}", e)))?;

        Ok(promo.into())
    }

    /// Deactivate (pause) a promotion.
    fn deactivate(&self, id: String) -> PyResult<Promotion> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let promo = commerce.promotions().deactivate(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to deactivate promotion: {}", e)))?;

        Ok(promo.into())
    }

    /// Delete a promotion.
    fn delete(&self, id: String) -> PyResult<()> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        commerce.promotions().delete(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to delete promotion: {}", e)))?;

        Ok(())
    }

    /// Get all active promotions.
    fn get_active(&self) -> PyResult<Vec<Promotion>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let promos = commerce.promotions().get_active()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get active promotions: {}", e)))?;

        Ok(promos.into_iter().map(|p| p.into()).collect())
    }

    // ========================================================================
    // Coupon Codes
    // ========================================================================

    /// Create a coupon code for a promotion.
    #[pyo3(signature = (promotion_id, code, usage_limit=None, per_customer_limit=None, starts_at=None, ends_at=None))]
    fn create_coupon(
        &self,
        promotion_id: String,
        code: String,
        usage_limit: Option<i32>,
        per_customer_limit: Option<i32>,
        starts_at: Option<String>,
        ends_at: Option<String>,
    ) -> PyResult<Coupon> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let promo_uuid = promotion_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid promotion UUID"))?;

        let create = stateset_core::CreateCouponCode {
            promotion_id: promo_uuid,
            code,
            usage_limit,
            per_customer_limit,
            starts_at: starts_at.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok().map(|d| d.with_timezone(&chrono::Utc))),
            ends_at: ends_at.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok().map(|d| d.with_timezone(&chrono::Utc))),
            metadata: None,
        };

        let coupon = commerce.promotions().create_coupon(create)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create coupon: {}", e)))?;

        Ok(coupon.into())
    }

    /// Get a coupon by ID.
    fn get_coupon(&self, id: String) -> PyResult<Option<Coupon>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let coupon = commerce.promotions().get_coupon(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get coupon: {}", e)))?;

        Ok(coupon.map(|c| c.into()))
    }

    /// Get a coupon by its code.
    fn get_coupon_by_code(&self, code: String) -> PyResult<Option<Coupon>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let coupon = commerce.promotions().get_coupon_by_code(&code)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get coupon: {}", e)))?;

        Ok(coupon.map(|c| c.into()))
    }

    /// List coupons with optional filtering.
    #[pyo3(signature = (promotion_id=None, status=None, limit=None, offset=None))]
    fn list_coupons(
        &self,
        promotion_id: Option<String>,
        status: Option<String>,
        limit: Option<i32>,
        offset: Option<i32>,
    ) -> PyResult<Vec<Coupon>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let filter = stateset_core::CouponFilter {
            promotion_id: promotion_id.and_then(|s| s.parse().ok()),
            status: status.map(|s| parse_coupon_status(&s)),
            search: None,
            limit: limit.map(|v| v as u32),
            offset: offset.map(|v| v as u32),
        };

        let coupons = commerce.promotions().list_coupons(filter)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list coupons: {}", e)))?;

        Ok(coupons.into_iter().map(|c| c.into()).collect())
    }

    /// Validate a coupon code.
    fn validate_coupon(&self, code: String) -> PyResult<Option<Coupon>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let coupon = commerce.promotions().validate_coupon(&code)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to validate coupon: {}", e)))?;

        Ok(coupon.map(|c| c.into()))
    }

    // ========================================================================
    // Apply Promotions
    // ========================================================================

    /// Apply promotions to cart/order items.
    #[pyo3(signature = (subtotal, coupon_codes=None, shipping_amount=None, currency=None))]
    fn apply(
        &self,
        subtotal: f64,
        coupon_codes: Option<Vec<String>>,
        shipping_amount: Option<f64>,
        currency: Option<String>,
    ) -> PyResult<ApplyPromotionsResult> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let request = stateset_core::ApplyPromotionsRequest {
            cart_id: None,
            customer_id: None,
            coupon_codes: coupon_codes.unwrap_or_default(),
            line_items: vec![],
            subtotal: Decimal::from_f64_retain(subtotal).unwrap_or_default(),
            shipping_amount: Decimal::from_f64_retain(shipping_amount.unwrap_or(0.0)).unwrap_or_default(),
            shipping_country: None,
            shipping_state: None,
            currency: currency.unwrap_or_else(|| "USD".to_string()),
            is_first_order: false,
        };

        let result = commerce.promotions().apply(request)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to apply promotions: {}", e)))?;

        Ok(result.into())
    }

    /// Record promotion usage (after order completion).
    #[pyo3(signature = (promotion_id, discount_amount, currency, coupon_id=None, customer_id=None, order_id=None, cart_id=None))]
    fn record_usage(
        &self,
        promotion_id: String,
        discount_amount: f64,
        currency: String,
        coupon_id: Option<String>,
        customer_id: Option<String>,
        order_id: Option<String>,
        cart_id: Option<String>,
    ) -> PyResult<PromotionUsage> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let promo_uuid = promotion_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid promotion UUID"))?;

        let usage = commerce.promotions().record_usage(
            promo_uuid,
            coupon_id.and_then(|s| s.parse().ok()),
            customer_id.and_then(|s| s.parse().ok()),
            order_id.and_then(|s| s.parse().ok()),
            cart_id.and_then(|s| s.parse().ok()),
            Decimal::from_f64_retain(discount_amount).unwrap_or_default(),
            &currency,
        ).map_err(|e| PyRuntimeError::new_err(format!("Failed to record usage: {}", e)))?;

        Ok(usage.into())
    }
}

// ============================================================================
// Tax API
// ============================================================================

/// Tax jurisdiction data.
#[pyclass]
#[derive(Clone)]
pub struct TaxJurisdiction {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    parent_id: Option<String>,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    code: String,
    #[pyo3(get)]
    level: String,
    #[pyo3(get)]
    country_code: String,
    #[pyo3(get)]
    state_code: Option<String>,
    #[pyo3(get)]
    county: Option<String>,
    #[pyo3(get)]
    city: Option<String>,
    #[pyo3(get)]
    postal_codes: Vec<String>,
    #[pyo3(get)]
    active: bool,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::TaxJurisdiction> for TaxJurisdiction {
    fn from(j: stateset_core::TaxJurisdiction) -> Self {
        Self {
            id: j.id.to_string(),
            parent_id: j.parent_id.map(|u| u.to_string()),
            name: j.name,
            code: j.code,
            level: format!("{:?}", j.level).to_lowercase(),
            country_code: j.country_code,
            state_code: j.state_code,
            county: j.county,
            city: j.city,
            postal_codes: j.postal_codes,
            active: j.active,
            created_at: j.created_at.to_rfc3339(),
            updated_at: j.updated_at.to_rfc3339(),
        }
    }
}

/// Tax rate data.
#[pyclass]
#[derive(Clone)]
pub struct TaxRate {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    jurisdiction_id: String,
    #[pyo3(get)]
    tax_type: String,
    #[pyo3(get)]
    product_category: String,
    #[pyo3(get)]
    rate: f64,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    description: Option<String>,
    #[pyo3(get)]
    is_compound: bool,
    #[pyo3(get)]
    priority: i32,
    #[pyo3(get)]
    effective_from: String,
    #[pyo3(get)]
    effective_to: Option<String>,
    #[pyo3(get)]
    active: bool,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::TaxRate> for TaxRate {
    fn from(r: stateset_core::TaxRate) -> Self {
        Self {
            id: r.id.to_string(),
            jurisdiction_id: r.jurisdiction_id.to_string(),
            tax_type: r.tax_type.as_str().to_string(),
            product_category: r.product_category.as_str().to_string(),
            rate: to_f64_or_nan(r.rate),
            name: r.name,
            description: r.description,
            is_compound: r.is_compound,
            priority: r.priority,
            effective_from: r.effective_from.to_string(),
            effective_to: r.effective_to.map(|d| d.to_string()),
            active: r.active,
            created_at: r.created_at.to_rfc3339(),
            updated_at: r.updated_at.to_rfc3339(),
        }
    }
}

/// Tax exemption data.
#[pyclass]
#[derive(Clone)]
pub struct TaxExemption {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    exemption_type: String,
    #[pyo3(get)]
    certificate_number: Option<String>,
    #[pyo3(get)]
    issuing_authority: Option<String>,
    #[pyo3(get)]
    jurisdiction_ids: Vec<String>,
    #[pyo3(get)]
    exempt_categories: Vec<String>,
    #[pyo3(get)]
    effective_from: String,
    #[pyo3(get)]
    expires_at: Option<String>,
    #[pyo3(get)]
    verified: bool,
    #[pyo3(get)]
    notes: Option<String>,
    #[pyo3(get)]
    active: bool,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::TaxExemption> for TaxExemption {
    fn from(e: stateset_core::TaxExemption) -> Self {
        Self {
            id: e.id.to_string(),
            customer_id: e.customer_id.to_string(),
            exemption_type: format!("{:?}", e.exemption_type).to_lowercase(),
            certificate_number: e.certificate_number,
            issuing_authority: e.issuing_authority,
            jurisdiction_ids: e.jurisdiction_ids.iter().map(|u| u.to_string()).collect(),
            exempt_categories: e.exempt_categories.iter().map(|c| c.as_str().to_string()).collect(),
            effective_from: e.effective_from.to_string(),
            expires_at: e.expires_at.map(|d| d.to_string()),
            verified: e.verified,
            notes: e.notes,
            active: e.active,
            created_at: e.created_at.to_rfc3339(),
            updated_at: e.updated_at.to_rfc3339(),
        }
    }
}

/// Tax settings data.
#[pyclass]
#[derive(Clone)]
pub struct TaxSettings {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    enabled: bool,
    #[pyo3(get)]
    calculation_method: String,
    #[pyo3(get)]
    compound_method: String,
    #[pyo3(get)]
    tax_shipping: bool,
    #[pyo3(get)]
    tax_handling: bool,
    #[pyo3(get)]
    tax_gift_wrap: bool,
    #[pyo3(get)]
    default_product_category: String,
    #[pyo3(get)]
    rounding_mode: String,
    #[pyo3(get)]
    decimal_places: i32,
    #[pyo3(get)]
    validate_addresses: bool,
    #[pyo3(get)]
    tax_provider: Option<String>,
    #[pyo3(get)]
    created_at: String,
    #[pyo3(get)]
    updated_at: String,
}

impl From<stateset_core::TaxSettings> for TaxSettings {
    fn from(s: stateset_core::TaxSettings) -> Self {
        Self {
            id: s.id.to_string(),
            enabled: s.enabled,
            calculation_method: format!("{:?}", s.calculation_method).to_lowercase(),
            compound_method: format!("{:?}", s.compound_method).to_lowercase(),
            tax_shipping: s.tax_shipping,
            tax_handling: s.tax_handling,
            tax_gift_wrap: s.tax_gift_wrap,
            default_product_category: s.default_product_category.as_str().to_string(),
            rounding_mode: s.rounding_mode,
            decimal_places: s.decimal_places,
            validate_addresses: s.validate_addresses,
            tax_provider: s.tax_provider,
            created_at: s.created_at.to_rfc3339(),
            updated_at: s.updated_at.to_rfc3339(),
        }
    }
}

/// Tax calculation result.
#[pyclass]
#[derive(Clone)]
pub struct TaxCalculationResult {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    total_tax: f64,
    #[pyo3(get)]
    subtotal: f64,
    #[pyo3(get)]
    total: f64,
    #[pyo3(get)]
    shipping_tax: f64,
    #[pyo3(get)]
    exemptions_applied: bool,
    #[pyo3(get)]
    calculated_at: String,
    #[pyo3(get)]
    is_estimate: bool,
}

impl From<stateset_core::TaxCalculationResult> for TaxCalculationResult {
    fn from(r: stateset_core::TaxCalculationResult) -> Self {
        Self {
            id: r.id.to_string(),
            total_tax: to_f64_or_nan(r.total_tax),
            subtotal: to_f64_or_nan(r.subtotal),
            total: to_f64_or_nan(r.total),
            shipping_tax: to_f64_or_nan(r.shipping_tax),
            exemptions_applied: r.exemptions_applied,
            calculated_at: r.calculated_at.to_rfc3339(),
            is_estimate: r.is_estimate,
        }
    }
}

/// US state tax info.
#[pyclass]
#[derive(Clone)]
pub struct UsStateTaxInfo {
    #[pyo3(get)]
    state_code: String,
    #[pyo3(get)]
    state_name: String,
    #[pyo3(get)]
    state_rate: f64,
    #[pyo3(get)]
    has_local_taxes: bool,
    #[pyo3(get)]
    origin_based: bool,
    #[pyo3(get)]
    tax_shipping: bool,
    #[pyo3(get)]
    tax_clothing: bool,
    #[pyo3(get)]
    tax_food: bool,
    #[pyo3(get)]
    tax_digital: bool,
}

impl From<stateset_core::UsStateTaxInfo> for UsStateTaxInfo {
    fn from(i: stateset_core::UsStateTaxInfo) -> Self {
        Self {
            state_code: i.state_code,
            state_name: i.state_name,
            state_rate: to_f64_or_nan(i.state_rate),
            has_local_taxes: i.has_local_taxes,
            origin_based: i.origin_based,
            tax_shipping: i.tax_shipping,
            tax_clothing: i.tax_clothing,
            tax_food: i.tax_food,
            tax_digital: i.tax_digital,
        }
    }
}

/// EU VAT info.
#[pyclass]
#[derive(Clone)]
pub struct EuVatInfo {
    #[pyo3(get)]
    country_code: String,
    #[pyo3(get)]
    country_name: String,
    #[pyo3(get)]
    standard_rate: f64,
    #[pyo3(get)]
    reduced_rate: Option<f64>,
    #[pyo3(get)]
    super_reduced_rate: Option<f64>,
    #[pyo3(get)]
    parking_rate: Option<f64>,
}

impl From<stateset_core::EuVatInfo> for EuVatInfo {
    fn from(i: stateset_core::EuVatInfo) -> Self {
        Self {
            country_code: i.country_code,
            country_name: i.country_name,
            standard_rate: to_f64_or_nan(i.standard_rate),
            reduced_rate: i.reduced_rate.map(|d| to_f64_or_nan(d)),
            super_reduced_rate: i.super_reduced_rate.map(|d| to_f64_or_nan(d)),
            parking_rate: i.parking_rate.map(|d| to_f64_or_nan(d)),
        }
    }
}

/// Canadian tax info.
#[pyclass]
#[derive(Clone)]
pub struct CanadianTaxInfo {
    #[pyo3(get)]
    province_code: String,
    #[pyo3(get)]
    province_name: String,
    #[pyo3(get)]
    gst_rate: f64,
    #[pyo3(get)]
    pst_rate: Option<f64>,
    #[pyo3(get)]
    hst_rate: Option<f64>,
    #[pyo3(get)]
    qst_rate: Option<f64>,
    #[pyo3(get)]
    total_rate: f64,
}

impl From<stateset_core::CanadianTaxInfo> for CanadianTaxInfo {
    fn from(i: stateset_core::CanadianTaxInfo) -> Self {
        Self {
            province_code: i.province_code,
            province_name: i.province_name,
            gst_rate: to_f64_or_nan(i.gst_rate),
            pst_rate: i.pst_rate.map(|d| to_f64_or_nan(d)),
            hst_rate: i.hst_rate.map(|d| to_f64_or_nan(d)),
            qst_rate: i.qst_rate.map(|d| to_f64_or_nan(d)),
            total_rate: to_f64_or_nan(i.total_rate),
        }
    }
}

// --- Helper Functions ---

fn parse_tax_type(s: &str) -> stateset_core::TaxType {
    match s.to_lowercase().as_str() {
        "sales_tax" => stateset_core::TaxType::SalesTax,
        "vat" => stateset_core::TaxType::Vat,
        "gst" => stateset_core::TaxType::Gst,
        "hst" => stateset_core::TaxType::Hst,
        "pst" => stateset_core::TaxType::Pst,
        "qst" => stateset_core::TaxType::Qst,
        "consumption_tax" => stateset_core::TaxType::ConsumptionTax,
        "custom" => stateset_core::TaxType::Custom,
        _ => stateset_core::TaxType::SalesTax,
    }
}

fn parse_product_tax_category(s: &str) -> stateset_core::ProductTaxCategory {
    match s.to_lowercase().as_str() {
        "standard" => stateset_core::ProductTaxCategory::Standard,
        "reduced" => stateset_core::ProductTaxCategory::Reduced,
        "super_reduced" => stateset_core::ProductTaxCategory::SuperReduced,
        "zero_rated" => stateset_core::ProductTaxCategory::ZeroRated,
        "exempt" => stateset_core::ProductTaxCategory::Exempt,
        "digital" => stateset_core::ProductTaxCategory::Digital,
        "clothing" => stateset_core::ProductTaxCategory::Clothing,
        "food" => stateset_core::ProductTaxCategory::Food,
        "prepared_food" => stateset_core::ProductTaxCategory::PreparedFood,
        "medical" => stateset_core::ProductTaxCategory::Medical,
        "educational" => stateset_core::ProductTaxCategory::Educational,
        "luxury" => stateset_core::ProductTaxCategory::Luxury,
        _ => stateset_core::ProductTaxCategory::Standard,
    }
}

fn parse_jurisdiction_level(s: &str) -> stateset_core::JurisdictionLevel {
    match s.to_lowercase().as_str() {
        "country" => stateset_core::JurisdictionLevel::Country,
        "state" => stateset_core::JurisdictionLevel::State,
        "county" => stateset_core::JurisdictionLevel::County,
        "city" => stateset_core::JurisdictionLevel::City,
        "district" => stateset_core::JurisdictionLevel::District,
        "special" => stateset_core::JurisdictionLevel::Special,
        _ => stateset_core::JurisdictionLevel::Country,
    }
}

fn parse_exemption_type(s: &str) -> stateset_core::ExemptionType {
    match s.to_lowercase().as_str() {
        "resale" => stateset_core::ExemptionType::Resale,
        "non_profit" | "nonprofit" => stateset_core::ExemptionType::NonProfit,
        "government" => stateset_core::ExemptionType::Government,
        "educational" => stateset_core::ExemptionType::Educational,
        "religious" => stateset_core::ExemptionType::Religious,
        "medical" => stateset_core::ExemptionType::Medical,
        "manufacturing" => stateset_core::ExemptionType::Manufacturing,
        "agricultural" => stateset_core::ExemptionType::Agricultural,
        "export" => stateset_core::ExemptionType::Export,
        "diplomatic" => stateset_core::ExemptionType::Diplomatic,
        _ => stateset_core::ExemptionType::Other,
    }
}

/// Tax operations API.
#[pyclass]
pub struct TaxApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl TaxApi {
    // ========================================================================
    // Jurisdiction Operations
    // ========================================================================

    /// Create a tax jurisdiction.
    #[pyo3(signature = (name, code, country_code, parent_id=None, level=None, state_code=None, county=None, city=None, postal_codes=None))]
    fn create_jurisdiction(
        &self,
        name: String,
        code: String,
        country_code: String,
        parent_id: Option<String>,
        level: Option<String>,
        state_code: Option<String>,
        county: Option<String>,
        city: Option<String>,
        postal_codes: Option<Vec<String>>,
    ) -> PyResult<TaxJurisdiction> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let create = stateset_core::CreateTaxJurisdiction {
            parent_id: parent_id.and_then(|s| s.parse().ok()),
            name,
            code,
            level: level.map(|s| parse_jurisdiction_level(&s)).unwrap_or_default(),
            country_code,
            state_code,
            county,
            city,
            postal_codes: postal_codes.unwrap_or_default(),
        };

        let jurisdiction = commerce.tax().create_jurisdiction(create)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create jurisdiction: {}", e)))?;

        Ok(jurisdiction.into())
    }

    /// Get a jurisdiction by ID.
    fn get_jurisdiction(&self, id: String) -> PyResult<Option<TaxJurisdiction>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let jurisdiction = commerce.tax().get_jurisdiction(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get jurisdiction: {}", e)))?;

        Ok(jurisdiction.map(|j| j.into()))
    }

    /// Get a jurisdiction by code.
    fn get_jurisdiction_by_code(&self, code: String) -> PyResult<Option<TaxJurisdiction>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let jurisdiction = commerce.tax().get_jurisdiction_by_code(&code)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get jurisdiction: {}", e)))?;

        Ok(jurisdiction.map(|j| j.into()))
    }

    /// List jurisdictions.
    #[pyo3(signature = (country_code=None, state_code=None, level=None, active_only=None))]
    fn list_jurisdictions(
        &self,
        country_code: Option<String>,
        state_code: Option<String>,
        level: Option<String>,
        active_only: Option<bool>,
    ) -> PyResult<Vec<TaxJurisdiction>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let filter = stateset_core::TaxJurisdictionFilter {
            country_code,
            state_code,
            level: level.map(|s| parse_jurisdiction_level(&s)),
            active_only: active_only.unwrap_or(false),
        };

        let jurisdictions = commerce.tax().list_jurisdictions(filter)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list jurisdictions: {}", e)))?;

        Ok(jurisdictions.into_iter().map(|j| j.into()).collect())
    }

    // ========================================================================
    // Tax Rate Operations
    // ========================================================================

    /// Create a tax rate.
    #[pyo3(signature = (jurisdiction_id, rate, name, effective_from, tax_type=None, product_category=None, description=None, is_compound=None, priority=None, effective_to=None))]
    fn create_rate(
        &self,
        jurisdiction_id: String,
        rate: f64,
        name: String,
        effective_from: String,
        tax_type: Option<String>,
        product_category: Option<String>,
        description: Option<String>,
        is_compound: Option<bool>,
        priority: Option<i32>,
        effective_to: Option<String>,
    ) -> PyResult<TaxRate> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let jid = jurisdiction_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid jurisdiction UUID"))?;

        let eff_from = chrono::NaiveDate::parse_from_str(&effective_from, "%Y-%m-%d")
            .map_err(|e| PyRuntimeError::new_err(format!("Invalid date format: {}", e)))?;

        let create = stateset_core::CreateTaxRate {
            jurisdiction_id: jid,
            tax_type: tax_type.map(|s| parse_tax_type(&s)).unwrap_or_default(),
            product_category: product_category.map(|s| parse_product_tax_category(&s)).unwrap_or_default(),
            rate: Decimal::from_f64_retain(rate).unwrap_or_default(),
            name,
            description,
            is_compound: is_compound.unwrap_or(false),
            priority: priority.unwrap_or(0),
            threshold_min: None,
            threshold_max: None,
            fixed_amount: None,
            effective_from: eff_from,
            effective_to: effective_to.and_then(|s| chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").ok()),
        };

        let rate_result = commerce.tax().create_rate(create)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create rate: {}", e)))?;

        Ok(rate_result.into())
    }

    /// Get a rate by ID.
    fn get_rate(&self, id: String) -> PyResult<Option<TaxRate>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let rate = commerce.tax().get_rate(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get rate: {}", e)))?;

        Ok(rate.map(|r| r.into()))
    }

    /// List tax rates.
    #[pyo3(signature = (jurisdiction_id=None, tax_type=None, product_category=None, active_only=None))]
    fn list_rates(
        &self,
        jurisdiction_id: Option<String>,
        tax_type: Option<String>,
        product_category: Option<String>,
        active_only: Option<bool>,
    ) -> PyResult<Vec<TaxRate>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let filter = stateset_core::TaxRateFilter {
            jurisdiction_id: jurisdiction_id.and_then(|s| s.parse().ok()),
            tax_type: tax_type.map(|s| parse_tax_type(&s)),
            product_category: product_category.map(|s| parse_product_tax_category(&s)),
            active_only: active_only.unwrap_or(false),
            effective_date: None,
        };

        let rates = commerce.tax().list_rates(filter)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to list rates: {}", e)))?;

        Ok(rates.into_iter().map(|r| r.into()).collect())
    }

    // ========================================================================
    // Exemption Operations
    // ========================================================================

    /// Create a tax exemption.
    #[pyo3(signature = (customer_id, exemption_type, effective_from, certificate_number=None, issuing_authority=None, jurisdiction_ids=None, exempt_categories=None, expires_at=None, notes=None))]
    fn create_exemption(
        &self,
        customer_id: String,
        exemption_type: String,
        effective_from: String,
        certificate_number: Option<String>,
        issuing_authority: Option<String>,
        jurisdiction_ids: Option<Vec<String>>,
        exempt_categories: Option<Vec<String>>,
        expires_at: Option<String>,
        notes: Option<String>,
    ) -> PyResult<TaxExemption> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let cid = customer_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;

        let eff_from = chrono::NaiveDate::parse_from_str(&effective_from, "%Y-%m-%d")
            .map_err(|e| PyRuntimeError::new_err(format!("Invalid date format: {}", e)))?;

        let create = stateset_core::CreateTaxExemption {
            customer_id: cid,
            exemption_type: parse_exemption_type(&exemption_type),
            certificate_number,
            issuing_authority,
            jurisdiction_ids: jurisdiction_ids
                .unwrap_or_default()
                .into_iter()
                .filter_map(|s| s.parse().ok())
                .collect(),
            exempt_categories: exempt_categories
                .unwrap_or_default()
                .into_iter()
                .map(|s| parse_product_tax_category(&s))
                .collect(),
            effective_from: eff_from,
            expires_at: expires_at.and_then(|s| chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").ok()),
            notes,
        };

        let exemption = commerce.tax().create_exemption(create)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to create exemption: {}", e)))?;

        Ok(exemption.into())
    }

    /// Get an exemption by ID.
    fn get_exemption(&self, id: String) -> PyResult<Option<TaxExemption>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let exemption = commerce.tax().get_exemption(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get exemption: {}", e)))?;

        Ok(exemption.map(|e| e.into()))
    }

    /// Get exemptions for a customer.
    fn get_customer_exemptions(&self, customer_id: String) -> PyResult<Vec<TaxExemption>> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = customer_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let exemptions = commerce.tax().get_customer_exemptions(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get exemptions: {}", e)))?;

        Ok(exemptions.into_iter().map(|e| e.into()).collect())
    }

    /// Check if a customer is tax exempt.
    fn customer_is_exempt(&self, customer_id: String) -> PyResult<bool> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = customer_id.parse()
            .map_err(|_| PyValueError::new_err("Invalid UUID"))?;

        let is_exempt = commerce.tax().customer_is_exempt(uuid)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to check exemption: {}", e)))?;

        Ok(is_exempt)
    }

    // ========================================================================
    // Settings Operations
    // ========================================================================

    /// Get tax settings.
    fn get_settings(&self) -> PyResult<TaxSettings> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let settings = commerce.tax().get_settings()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to get settings: {}", e)))?;

        Ok(settings.into())
    }

    /// Enable or disable tax calculation.
    fn set_enabled(&self, enabled: bool) -> PyResult<TaxSettings> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let settings = commerce.tax().set_enabled(enabled)
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to update settings: {}", e)))?;

        Ok(settings.into())
    }

    /// Check if tax calculation is enabled.
    fn is_enabled(&self) -> PyResult<bool> {
        let commerce = self.commerce.lock()
            .map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;

        let enabled = commerce.tax().is_enabled()
            .map_err(|e| PyRuntimeError::new_err(format!("Failed to check settings: {}", e)))?;

        Ok(enabled)
    }

    // ========================================================================
    // Helper Methods
    // ========================================================================

    /// Get US state tax information.
    #[staticmethod]
    fn get_us_state_info(state_code: String) -> Option<UsStateTaxInfo> {
        stateset_core::get_us_state_tax_info(&state_code).map(|i| i.into())
    }

    /// Get EU VAT information.
    #[staticmethod]
    fn get_eu_vat_info(country_code: String) -> Option<EuVatInfo> {
        stateset_core::get_eu_vat_info(&country_code).map(|i| i.into())
    }

    /// Get Canadian tax information.
    #[staticmethod]
    fn get_canadian_tax_info(province_code: String) -> Option<CanadianTaxInfo> {
        stateset_core::get_canadian_tax_info(&province_code).map(|i| i.into())
    }

    /// Check if a country is in the EU.
    #[staticmethod]
    fn is_eu_country(country_code: String) -> bool {
        stateset_core::is_eu_member(&country_code)
    }
}

// ============================================================================
// Quality Control Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct Inspection {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    inspection_number: String,
    #[pyo3(get)]
    inspection_type: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    reference_type: String,
    #[pyo3(get)]
    reference_id: String,
    #[pyo3(get)]
    inspector_id: Option<String>,
    #[pyo3(get)]
    notes: Option<String>,
    #[pyo3(get)]
    created_at: String,
}

#[pymethods]
impl Inspection {
    fn __repr__(&self) -> String {
        format!("Inspection(number='{}', status='{}')", self.inspection_number, self.status)
    }
}

impl From<stateset_core::Inspection> for Inspection {
    fn from(i: stateset_core::Inspection) -> Self {
        Self {
            id: i.id.to_string(),
            inspection_number: i.inspection_number,
            inspection_type: format!("{:?}", i.inspection_type),
            status: format!("{:?}", i.status),
            reference_type: i.reference_type,
            reference_id: i.reference_id.to_string(),
            inspector_id: i.inspector_id,
            notes: i.notes,
            created_at: i.created_at.to_rfc3339(),
        }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct NonConformance {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    ncr_number: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    description: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    source: String,
    #[pyo3(get)]
    severity: String,
    #[pyo3(get)]
    quantity_affected: f64,
}

impl From<stateset_core::NonConformance> for NonConformance {
    fn from(n: stateset_core::NonConformance) -> Self {
        Self {
            id: n.id.to_string(),
            ncr_number: n.ncr_number,
            sku: n.sku,
            description: n.description,
            status: format!("{:?}", n.status),
            source: format!("{:?}", n.source),
            severity: format!("{:?}", n.severity),
            quantity_affected: to_f64_or_nan(n.quantity_affected),
        }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct QualityHold {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    reason: String,
    #[pyo3(get)]
    quantity_held: f64,
    #[pyo3(get)]
    hold_type: String,
    #[pyo3(get)]
    placed_by: String,
}

impl From<stateset_core::QualityHold> for QualityHold {
    fn from(h: stateset_core::QualityHold) -> Self {
        Self {
            id: h.id.to_string(),
            sku: h.sku,
            reason: h.reason,
            quantity_held: to_f64_or_nan(h.quantity_held),
            hold_type: format!("{:?}", h.hold_type),
            placed_by: h.placed_by,
        }
    }
}

// ============================================================================
// Quality Control API
// ============================================================================

#[pyclass]
pub struct QualityApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl QualityApi {
    #[pyo3(signature = (reference_type, reference_id, inspection_type, inspector_id=None))]
    fn create_inspection(&self, reference_type: String, reference_id: String, inspection_type: String, inspector_id: Option<String>) -> PyResult<Inspection> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let itype = match inspection_type.to_lowercase().as_str() {
            "incoming" => stateset_core::InspectionType::Incoming,
            "receiving" => stateset_core::InspectionType::Receiving,
            "in_process" => stateset_core::InspectionType::InProcess,
            "final" => stateset_core::InspectionType::Final,
            "random" => stateset_core::InspectionType::Random,
            _ => stateset_core::InspectionType::Incoming,
        };
        let ref_uuid = reference_id.parse().map_err(|_| PyValueError::new_err("Invalid reference UUID"))?;
        let inspection = commerce.quality().create_inspection(stateset_core::CreateInspection {
            inspection_type: itype,
            reference_type,
            reference_id: ref_uuid,
            inspector_id,
            ..Default::default()
        }).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(inspection.into())
    }

    fn get_inspection(&self, id: String) -> PyResult<Option<Inspection>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let inspection = commerce.quality().get_inspection(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(inspection.map(|i| i.into()))
    }

    fn list_inspections(&self) -> PyResult<Vec<Inspection>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let inspections = commerce.quality().list_inspections(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(inspections.into_iter().map(|i| i.into()).collect())
    }

    fn complete_inspection(&self, id: String) -> PyResult<Inspection> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let inspection = commerce.quality().complete_inspection(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(inspection.into())
    }

    fn create_ncr(&self, sku: String, description: String, quantity_affected: f64, source: String, severity: String) -> PyResult<NonConformance> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let src = match source.to_lowercase().as_str() {
            "inspection" => stateset_core::NonConformanceSource::Inspection,
            "production" | "production_defect" => stateset_core::NonConformanceSource::ProductionDefect,
            "customer" | "customer_complaint" => stateset_core::NonConformanceSource::CustomerComplaint,
            "supplier" | "supplier_issue" => stateset_core::NonConformanceSource::SupplierIssue,
            "internal_audit" => stateset_core::NonConformanceSource::InternalAudit,
            "shipping_damage" => stateset_core::NonConformanceSource::ShippingDamage,
            _ => stateset_core::NonConformanceSource::Inspection,
        };
        let sev = match severity.to_lowercase().as_str() {
            "critical" => stateset_core::Severity::Critical,
            "major" => stateset_core::Severity::Major,
            "minor" => stateset_core::Severity::Minor,
            "observation" => stateset_core::Severity::Observation,
            _ => stateset_core::Severity::Minor,
        };
        let ncr = commerce.quality().create_ncr(stateset_core::CreateNonConformance {
            sku, description, quantity_affected: Decimal::from_f64_retain(quantity_affected).unwrap_or_default(),
            source: src, severity: sev, ..Default::default()
        }).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(ncr.into())
    }

    fn list_ncrs(&self) -> PyResult<Vec<NonConformance>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let ncrs = commerce.quality().list_ncrs(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(ncrs.into_iter().map(|n| n.into()).collect())
    }

    fn create_hold(&self, sku: String, reason: String, quantity: f64) -> PyResult<QualityHold> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let hold = commerce.quality().create_hold(stateset_core::CreateQualityHold {
            sku, reason, quantity: Decimal::from_f64_retain(quantity).unwrap_or_default(), ..Default::default()
        }).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(hold.into())
    }

    #[pyo3(signature = (id, released_by, release_notes=None))]
    fn release_hold(&self, id: String, released_by: String, release_notes: Option<String>) -> PyResult<QualityHold> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let hold = commerce.quality().release_hold(uuid, stateset_core::ReleaseQualityHold { released_by, release_notes }).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(hold.into())
    }
}

// ============================================================================
// Lot/Batch Tracking Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct Lot {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    lot_number: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    quantity_remaining: f64,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    expiration_date: Option<String>,
    #[pyo3(get)]
    created_at: String,
}

impl From<stateset_core::Lot> for Lot {
    fn from(l: stateset_core::Lot) -> Self {
        Self {
            id: l.id.to_string(),
            lot_number: l.lot_number,
            sku: l.sku,
            quantity_remaining: to_f64_or_nan(l.quantity_remaining),
            status: format!("{:?}", l.status),
            expiration_date: l.expiration_date.map(|d| d.to_rfc3339()),
            created_at: l.created_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Lots API
// ============================================================================

#[pyclass]
pub struct LotsApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl LotsApi {
    #[pyo3(signature = (sku, lot_number, quantity, expiration_date=None))]
    fn create_lot(&self, sku: String, lot_number: String, quantity: f64, expiration_date: Option<String>) -> PyResult<Lot> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let exp = expiration_date.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok()).map(|dt| dt.with_timezone(&chrono::Utc));
        let lot = commerce.lots().create(stateset_core::CreateLot {
            sku, lot_number: Some(lot_number), quantity: Decimal::from_f64_retain(quantity).unwrap_or_default(), expiration_date: exp, ..Default::default()
        }).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(lot.into())
    }

    fn get_lot(&self, id: String) -> PyResult<Option<Lot>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let lot = commerce.lots().get(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(lot.map(|l| l.into()))
    }

    fn list_lots(&self) -> PyResult<Vec<Lot>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let lots = commerce.lots().list(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(lots.into_iter().map(|l| l.into()).collect())
    }

    fn get_lots_by_sku(&self, sku: String) -> PyResult<Vec<Lot>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let lots = commerce.lots().get_available_lots_for_sku(&sku).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(lots.into_iter().map(|l| l.into()).collect())
    }

    fn quarantine_lot(&self, id: String, reason: String) -> PyResult<Lot> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let lot = commerce.lots().quarantine(uuid, &reason).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(lot.into())
    }

    fn get_expiring_lots(&self, days_ahead: i32) -> PyResult<Vec<Lot>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let lots = commerce.lots().get_expiring_lots(days_ahead).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(lots.into_iter().map(|l| l.into()).collect())
    }
}

// ============================================================================
// Serial Number Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct SerialNumber {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    serial: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    created_at: String,
}

impl From<stateset_core::SerialNumber> for SerialNumber {
    fn from(s: stateset_core::SerialNumber) -> Self {
        Self {
            id: s.id.to_string(),
            serial: s.serial,
            sku: s.sku,
            status: format!("{:?}", s.status),
            created_at: s.created_at.to_rfc3339(),
        }
    }
}

// ============================================================================
// Serials API
// ============================================================================

#[pyclass]
pub struct SerialsApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl SerialsApi {
    #[pyo3(signature = (sku, serial=None))]
    fn create(&self, sku: String, serial: Option<String>) -> PyResult<SerialNumber> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let s = commerce.serials().create(stateset_core::CreateSerialNumber { sku, serial, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(s.into())
    }

    fn get_by_serial(&self, serial: String) -> PyResult<Option<SerialNumber>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let s = commerce.serials().get_by_serial(&serial).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(s.map(|s| s.into()))
    }

    fn list(&self) -> PyResult<Vec<SerialNumber>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let serials = commerce.serials().list(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(serials.into_iter().map(|s| s.into()).collect())
    }

    fn change_status(&self, id: String, status: String) -> PyResult<SerialNumber> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let serial_status = match status.to_lowercase().as_str() {
            "available" => stateset_core::SerialStatus::Available,
            "sold" => stateset_core::SerialStatus::Sold,
            "returned" => stateset_core::SerialStatus::Returned,
            "scrapped" => stateset_core::SerialStatus::Scrapped,
            "reserved" => stateset_core::SerialStatus::Reserved,
            "shipped" => stateset_core::SerialStatus::Shipped,
            "quarantine" | "quarantined" => stateset_core::SerialStatus::Quarantined,
            _ => stateset_core::SerialStatus::Available,
        };
        let serial = commerce.serials().change_status(stateset_core::ChangeSerialStatus { serial_id: uuid, new_status: serial_status, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(serial.into())
    }
}

// ============================================================================
// Warehouse Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct Warehouse {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    code: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    address: Option<String>,
    #[pyo3(get)]
    is_active: bool,
}

impl From<stateset_core::Warehouse> for Warehouse {
    fn from(w: stateset_core::Warehouse) -> Self {
        // Convert WarehouseAddress to a simple string representation
        let addr_str = if w.address.street1.is_empty() && w.address.city.is_empty() {
            None
        } else {
            Some(format!("{}, {}, {}", w.address.street1, w.address.city, w.address.country))
        };
        Self { id: w.id.to_string(), code: w.code, name: w.name, address: addr_str, is_active: w.is_active }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct WarehouseLocation {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    warehouse_id: String,
    #[pyo3(get)]
    code: String,
    #[pyo3(get)]
    location_type: String,
}

impl From<stateset_core::Location> for WarehouseLocation {
    fn from(l: stateset_core::Location) -> Self {
        Self { id: l.id.to_string(), warehouse_id: l.warehouse_id.to_string(), code: l.code, location_type: format!("{:?}", l.location_type) }
    }
}

// ============================================================================
// Warehouse API
// ============================================================================

#[pyclass]
pub struct WarehouseApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl WarehouseApi {
    #[pyo3(signature = (code, name, address=None))]
    fn create_warehouse(&self, code: String, name: String, address: Option<String>) -> PyResult<Warehouse> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let _ = address; // Address is simplified - WarehouseAddress uses Default
        let warehouse = commerce.warehouse().create_warehouse(stateset_core::CreateWarehouse { code, name, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(warehouse.into())
    }

    fn get_warehouse(&self, id: String) -> PyResult<Option<Warehouse>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let wh_id: i32 = id.parse().map_err(|_| PyValueError::new_err("Invalid warehouse ID"))?;
        let warehouse = commerce.warehouse().get_warehouse(wh_id).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(warehouse.map(|w| w.into()))
    }

    fn list_warehouses(&self) -> PyResult<Vec<Warehouse>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let warehouses = commerce.warehouse().list_warehouses(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(warehouses.into_iter().map(|w| w.into()).collect())
    }

    #[pyo3(signature = (warehouse_id, code, location_type=None))]
    fn create_location(&self, warehouse_id: String, code: String, location_type: Option<String>) -> PyResult<WarehouseLocation> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let wh_id: i32 = warehouse_id.parse().map_err(|_| PyValueError::new_err("Invalid warehouse ID"))?;
        let loc_type = location_type.map(|t| match t.to_lowercase().as_str() {
            "pick" | "picking" => stateset_core::LocationType::Pick,
            "bulk" => stateset_core::LocationType::Bulk,
            "receiving" => stateset_core::LocationType::Receiving,
            "shipping" => stateset_core::LocationType::Shipping,
            "staging" => stateset_core::LocationType::Staging,
            "quarantine" => stateset_core::LocationType::Quarantine,
            _ => stateset_core::LocationType::Bulk,
        }).unwrap_or(stateset_core::LocationType::Bulk);
        let location = commerce.warehouse().create_location(stateset_core::CreateLocation { warehouse_id: wh_id, code: Some(code), location_type: loc_type, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(location.into())
    }

    fn get_locations_for_warehouse(&self, warehouse_id: String) -> PyResult<Vec<WarehouseLocation>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let wh_id: i32 = warehouse_id.parse().map_err(|_| PyValueError::new_err("Invalid warehouse ID"))?;
        let locations = commerce.warehouse().get_locations_for_warehouse(wh_id).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(locations.into_iter().map(|l| l.into()).collect())
    }
}

// ============================================================================
// Receiving Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct Receipt {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    receipt_number: String,
    #[pyo3(get)]
    receipt_type: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    reference_id: Option<String>,
    #[pyo3(get)]
    supplier_id: Option<String>,
    #[pyo3(get)]
    warehouse_id: String,
    #[pyo3(get)]
    created_at: String,
}

impl From<stateset_core::Receipt> for Receipt {
    fn from(r: stateset_core::Receipt) -> Self {
        Self {
            id: r.id.to_string(), receipt_number: r.receipt_number, receipt_type: format!("{:?}", r.receipt_type),
            status: format!("{:?}", r.status), reference_id: r.reference_id.map(|id| id.to_string()),
            supplier_id: r.supplier_id.map(|id| id.to_string()), warehouse_id: r.warehouse_id.to_string(),
            created_at: r.created_at.to_rfc3339(),
        }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct ReceiptLine {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    receipt_id: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    expected_quantity: f64,
    #[pyo3(get)]
    received_quantity: f64,
    #[pyo3(get)]
    unit_cost: Option<f64>,
    #[pyo3(get)]
    status: String,
}

impl From<stateset_core::ReceiptItem> for ReceiptLine {
    fn from(l: stateset_core::ReceiptItem) -> Self {
        Self {
            id: l.id.to_string(), receipt_id: l.receipt_id.to_string(), sku: l.sku,
            expected_quantity: to_f64_or_nan(l.expected_quantity),
            received_quantity: to_f64_or_nan(l.received_quantity),
            unit_cost: l.unit_cost.map(|c| to_f64_or_nan(c)),
            status: format!("{:?}", l.status),
        }
    }
}

// ============================================================================
// Receiving API
// ============================================================================

#[pyclass]
pub struct ReceivingApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl ReceivingApi {
    #[pyo3(signature = (warehouse_id, supplier_id=None))]
    fn create_receipt(&self, warehouse_id: String, supplier_id: Option<String>) -> PyResult<Receipt> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let wh_id: i32 = warehouse_id.parse().map_err(|_| PyValueError::new_err("Invalid warehouse ID"))?;
        let sup_uuid = supplier_id.and_then(|id| id.parse().ok());
        let receipt = commerce.receiving().create_receipt(stateset_core::CreateReceipt { warehouse_id: wh_id, supplier_id: sup_uuid, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(receipt.into())
    }

    fn get_receipt(&self, id: String) -> PyResult<Option<Receipt>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let receipt = commerce.receiving().get_receipt(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(receipt.map(|r| r.into()))
    }

    fn list_receipts(&self) -> PyResult<Vec<Receipt>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let receipts = commerce.receiving().list_receipts(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(receipts.into_iter().map(|r| r.into()).collect())
    }

    fn get_receipt_items(&self, receipt_id: String) -> PyResult<Vec<ReceiptLine>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = receipt_id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let items = commerce.receiving().get_receipt_items(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(items.into_iter().map(|i| i.into()).collect())
    }

    fn complete_receiving(&self, id: String) -> PyResult<Receipt> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let receipt = commerce.receiving().complete_receiving(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(receipt.into())
    }
}

// ============================================================================
// Fulfillment Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct Wave {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    wave_number: String,
    #[pyo3(get)]
    warehouse_id: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    order_count: i32,
    #[pyo3(get)]
    pick_count: i32,
}

impl From<stateset_core::Wave> for Wave {
    fn from(w: stateset_core::Wave) -> Self {
        Self { id: w.id.to_string(), wave_number: w.wave_number, warehouse_id: w.warehouse_id.to_string(), status: format!("{:?}", w.status), order_count: w.order_count, pick_count: w.pick_count }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct PickTask {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    order_id: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    quantity_requested: f64,
    #[pyo3(get)]
    quantity_picked: f64,
    #[pyo3(get)]
    status: String,
}

impl From<stateset_core::PickTask> for PickTask {
    fn from(t: stateset_core::PickTask) -> Self {
        Self {
            id: t.id.to_string(), order_id: t.order_id.to_string(), sku: t.sku,
            quantity_requested: to_f64_or_nan(t.quantity_requested),
            quantity_picked: to_f64_or_nan(t.quantity_picked),
            status: format!("{:?}", t.status),
        }
    }
}

// ============================================================================
// Fulfillment API
// ============================================================================

#[pyclass]
pub struct FulfillmentApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl FulfillmentApi {
    fn create_wave(&self, warehouse_id: String, order_ids: Vec<String>) -> PyResult<Wave> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let wh_id: i32 = warehouse_id.parse().map_err(|_| PyValueError::new_err("Invalid warehouse ID"))?;
        let order_uuids: Vec<uuid::Uuid> = order_ids.iter()
            .filter_map(|id| id.parse().ok())
            .collect();
        let wave = commerce.fulfillment().create_wave(stateset_core::CreateWave { warehouse_id: wh_id, order_ids: order_uuids, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(wave.into())
    }

    fn get_wave(&self, id: String) -> PyResult<Option<Wave>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let wave = commerce.fulfillment().get_wave(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(wave.map(|w| w.into()))
    }

    fn list_waves(&self) -> PyResult<Vec<Wave>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let waves = commerce.fulfillment().list_waves(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(waves.into_iter().map(|w| w.into()).collect())
    }

    fn release_wave(&self, id: String) -> PyResult<Wave> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let wave = commerce.fulfillment().release_wave(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(wave.into())
    }

    fn list_picks(&self) -> PyResult<Vec<PickTask>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let picks = commerce.fulfillment().list_picks(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(picks.into_iter().map(|p| p.into()).collect())
    }

    fn complete_pick(&self, id: String, quantity_picked: f64) -> PyResult<PickTask> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let task = commerce.fulfillment().complete_pick(stateset_core::CompletePick { pick_id: uuid, quantity_picked: Decimal::from_f64_retain(quantity_picked).unwrap_or_default(), quantity_short: None, short_reason: None, lot_id: None, serial_number: None, completed_by: None })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(task.into())
    }
}

// ============================================================================
// Accounts Payable Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct Bill {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    bill_number: String,
    #[pyo3(get)]
    supplier_id: String,
    #[pyo3(get)]
    total_amount: f64,
    #[pyo3(get)]
    amount_paid: f64,
    #[pyo3(get)]
    amount_due: f64,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    due_date: String,
}

impl From<stateset_core::Bill> for Bill {
    fn from(b: stateset_core::Bill) -> Self {
        Self {
            id: b.id.to_string(), bill_number: b.bill_number, supplier_id: b.supplier_id.to_string(),
            total_amount: to_f64_or_nan(b.total_amount), amount_paid: to_f64_or_nan(b.amount_paid),
            amount_due: to_f64_or_nan(b.amount_due),
            status: format!("{:?}", b.status), due_date: b.due_date.to_rfc3339(),
        }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct ApAgingSummary {
    #[pyo3(get)]
    current: f64,
    #[pyo3(get)]
    days_1_30: f64,
    #[pyo3(get)]
    days_31_60: f64,
    #[pyo3(get)]
    days_61_90: f64,
    #[pyo3(get)]
    days_over_90: f64,
    #[pyo3(get)]
    total: f64,
}

impl From<stateset_core::ApAgingSummary> for ApAgingSummary {
    fn from(s: stateset_core::ApAgingSummary) -> Self {
        Self {
            current: to_f64_or_nan(s.current),
            days_1_30: to_f64_or_nan(s.days_1_30),
            days_31_60: to_f64_or_nan(s.days_31_60),
            days_61_90: to_f64_or_nan(s.days_61_90),
            days_over_90: to_f64_or_nan(s.days_over_90),
            total: to_f64_or_nan(s.total),
        }
    }
}

// ============================================================================
// Accounts Payable API
// ============================================================================

#[pyclass]
pub struct AccountsPayableApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl AccountsPayableApi {
    fn create_bill(&self, supplier_id: String, due_date: String) -> PyResult<Bill> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = supplier_id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let due = chrono::DateTime::parse_from_rfc3339(&due_date).map_err(|_| PyValueError::new_err("Invalid due_date format"))?.with_timezone(&chrono::Utc);
        let bill = commerce.accounts_payable().create_bill(stateset_core::CreateBill { supplier_id: uuid, due_date: due, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(bill.into())
    }

    fn get_bill(&self, id: String) -> PyResult<Option<Bill>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let bill = commerce.accounts_payable().get_bill(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(bill.map(|b| b.into()))
    }

    fn list_bills(&self) -> PyResult<Vec<Bill>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let bills = commerce.accounts_payable().list_bills(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(bills.into_iter().map(|b| b.into()).collect())
    }

    fn approve_bill(&self, id: String) -> PyResult<Bill> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let bill = commerce.accounts_payable().approve_bill(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(bill.into())
    }

    #[pyo3(signature = (id, amount, payment_method=None))]
    fn pay_bill(&self, id: String, amount: f64, payment_method: Option<String>) -> PyResult<Bill> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let pm = payment_method.map(|s| match s.to_lowercase().as_str() {
            "check" => stateset_core::PaymentMethodAP::Check,
            "ach" => stateset_core::PaymentMethodAP::Ach,
            "wire" => stateset_core::PaymentMethodAP::Wire,
            "credit_card" => stateset_core::PaymentMethodAP::CreditCard,
            "cash" => stateset_core::PaymentMethodAP::Cash,
            _ => stateset_core::PaymentMethodAP::Other,
        }).unwrap_or(stateset_core::PaymentMethodAP::Check);
        let bill = commerce.accounts_payable().pay_bill(uuid, stateset_core::PayBill { amount: Decimal::from_f64_retain(amount).unwrap_or_default(), payment_method: pm, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(bill.into())
    }

    fn get_aging_summary(&self) -> PyResult<ApAgingSummary> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let summary = commerce.accounts_payable().get_aging_summary().map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(summary.into())
    }

    fn get_overdue_bills(&self) -> PyResult<Vec<Bill>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let bills = commerce.accounts_payable().get_overdue_bills().map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(bills.into_iter().map(|b| b.into()).collect())
    }
}

// ============================================================================
// Accounts Receivable Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct ArAgingSummary {
    #[pyo3(get)]
    current: f64,
    #[pyo3(get)]
    days_1_30: f64,
    #[pyo3(get)]
    days_31_60: f64,
    #[pyo3(get)]
    days_61_90: f64,
    #[pyo3(get)]
    days_over_90: f64,
    #[pyo3(get)]
    total: f64,
}

impl From<stateset_core::ArAgingSummary> for ArAgingSummary {
    fn from(s: stateset_core::ArAgingSummary) -> Self {
        Self {
            current: to_f64_or_nan(s.current),
            days_1_30: to_f64_or_nan(s.days_1_30),
            days_31_60: to_f64_or_nan(s.days_31_60),
            days_61_90: to_f64_or_nan(s.days_61_90),
            days_over_90: to_f64_or_nan(s.days_over_90),
            total: to_f64_or_nan(s.total),
        }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct CreditMemo {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    credit_memo_number: String,
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    amount: f64,
    #[pyo3(get)]
    reason: String,
    #[pyo3(get)]
    status: String,
}

impl From<stateset_core::CreditMemo> for CreditMemo {
    fn from(m: stateset_core::CreditMemo) -> Self {
        Self {
            id: m.id.to_string(), credit_memo_number: m.credit_memo_number, customer_id: m.customer_id.to_string(),
            amount: to_f64_or_nan(m.amount), reason: format!("{:?}", m.reason), status: format!("{:?}", m.status),
        }
    }
}

// ============================================================================
// Accounts Receivable API
// ============================================================================

#[pyclass]
pub struct AccountsReceivableApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl AccountsReceivableApi {
    fn get_aging_summary(&self) -> PyResult<ArAgingSummary> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let summary = commerce.accounts_receivable().get_aging_summary().map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(summary.into())
    }

    fn create_credit_memo(&self, customer_id: String, amount: f64, reason: String) -> PyResult<CreditMemo> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = customer_id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let r = match reason.to_lowercase().as_str() {
            "returned_goods" | "return" => stateset_core::CreditMemoReason::ReturnedGoods,
            "pricing_error" | "price" => stateset_core::CreditMemoReason::PricingError,
            "overpayment" => stateset_core::CreditMemoReason::Overpayment,
            "damaged" => stateset_core::CreditMemoReason::Damaged,
            "service_credit" | "service" => stateset_core::CreditMemoReason::ServiceCredit,
            "goodwill" | "adjustment" => stateset_core::CreditMemoReason::GoodwillAdjustment,
            _ => stateset_core::CreditMemoReason::Other,
        };
        let memo = commerce.accounts_receivable().create_credit_memo(stateset_core::CreateCreditMemo { customer_id: uuid, amount: Decimal::from_f64_retain(amount).unwrap_or_default(), reason: r, original_invoice_id: None, notes: None })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(memo.into())
    }

    #[pyo3(signature = (days=None))]
    fn get_dso(&self, days: Option<i32>) -> PyResult<f64> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let dso = commerce.accounts_receivable().get_dso(days.unwrap_or(30)).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(to_f64_or_nan(dso))
    }
}

// ============================================================================
// Cost Accounting Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct ItemCost {
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    standard_cost: f64,
    #[pyo3(get)]
    average_cost: f64,
    #[pyo3(get)]
    last_cost: f64,
    #[pyo3(get)]
    material_cost: f64,
    #[pyo3(get)]
    labor_cost: f64,
    #[pyo3(get)]
    overhead_cost: f64,
}

impl From<stateset_core::ItemCost> for ItemCost {
    fn from(c: stateset_core::ItemCost) -> Self {
        Self {
            sku: c.sku,
            standard_cost: to_f64_or_nan(c.standard_cost),
            average_cost: to_f64_or_nan(c.average_cost),
            last_cost: to_f64_or_nan(c.last_cost),
            material_cost: to_f64_or_nan(c.material_cost),
            labor_cost: to_f64_or_nan(c.labor_cost),
            overhead_cost: to_f64_or_nan(c.overhead_cost),
        }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct InventoryValuation {
    #[pyo3(get)]
    total_value: f64,
    #[pyo3(get)]
    total_quantity: f64,
    #[pyo3(get)]
    average_unit_cost: f64,
}

impl From<stateset_core::InventoryValuation> for InventoryValuation {
    fn from(v: stateset_core::InventoryValuation) -> Self {
        Self {
            total_value: to_f64_or_nan(v.total_value),
            total_quantity: to_f64_or_nan(v.total_quantity),
            average_unit_cost: to_f64_or_nan(v.average_unit_cost),
        }
    }
}

// ============================================================================
// Cost Accounting API
// ============================================================================

#[pyclass]
pub struct CostAccountingApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl CostAccountingApi {
    fn get_item_cost(&self, sku: String) -> PyResult<Option<ItemCost>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let cost = commerce.cost_accounting().get_item_cost(&sku).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(cost.map(|c| c.into()))
    }

    #[pyo3(signature = (sku, standard_cost=None, material_cost=None, labor_cost=None, overhead_cost=None))]
    fn set_item_cost(&self, sku: String, standard_cost: Option<f64>, material_cost: Option<f64>, labor_cost: Option<f64>, overhead_cost: Option<f64>) -> PyResult<ItemCost> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let cost = commerce.cost_accounting().set_item_cost(stateset_core::SetItemCost {
            sku, standard_cost: standard_cost.and_then(Decimal::from_f64_retain), material_cost: material_cost.and_then(Decimal::from_f64_retain),
            labor_cost: labor_cost.and_then(Decimal::from_f64_retain), overhead_cost: overhead_cost.and_then(Decimal::from_f64_retain), ..Default::default()
        }).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(cost.into())
    }

    fn update_average_cost(&self, sku: String, quantity: f64, unit_cost: f64) -> PyResult<ItemCost> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let cost = commerce.cost_accounting().update_average_cost(&sku, Decimal::from_f64_retain(quantity).unwrap_or_default(), Decimal::from_f64_retain(unit_cost).unwrap_or_default())
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(cost.into())
    }

    #[pyo3(signature = (cost_method=None))]
    fn get_inventory_valuation(&self, cost_method: Option<String>) -> PyResult<InventoryValuation> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let method = cost_method.and_then(|m| match m.to_lowercase().as_str() {
            "standard" => Some(stateset_core::CostMethod::Standard),
            "average" => Some(stateset_core::CostMethod::Average),
            "fifo" => Some(stateset_core::CostMethod::Fifo),
            "lifo" => Some(stateset_core::CostMethod::Lifo),
            _ => None,
        }).unwrap_or(stateset_core::CostMethod::Average);
        let valuation = commerce.cost_accounting().get_inventory_valuation(method).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(valuation.into())
    }

    fn get_total_inventory_value(&self) -> PyResult<f64> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let value = commerce.cost_accounting().get_total_inventory_value().map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(to_f64_or_nan(value))
    }
}

// ============================================================================
// Credit Management Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct CreditAccount {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    credit_limit: f64,
    #[pyo3(get)]
    current_balance: f64,
    #[pyo3(get)]
    available_credit: f64,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    payment_terms: Option<String>,
}

impl From<stateset_core::CreditAccount> for CreditAccount {
    fn from(a: stateset_core::CreditAccount) -> Self {
        Self {
            id: a.id.to_string(), customer_id: a.customer_id.to_string(),
            credit_limit: to_f64_or_nan(a.credit_limit),
            current_balance: to_f64_or_nan(a.current_balance),
            available_credit: to_f64_or_nan(a.available_credit),
            status: format!("{:?}", a.status), payment_terms: a.payment_terms,
        }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct CreditCheckResult {
    #[pyo3(get)]
    approved: bool,
    #[pyo3(get)]
    reason: Option<String>,
    #[pyo3(get)]
    available_credit: f64,
    #[pyo3(get)]
    requires_approval: bool,
}

impl From<stateset_core::CreditCheckResult> for CreditCheckResult {
    fn from(r: stateset_core::CreditCheckResult) -> Self {
        Self {
            approved: r.approved, reason: r.reason.map(|r| format!("{:?}", r)),
            available_credit: to_f64_or_nan(r.available_credit), requires_approval: r.requires_approval,
        }
    }
}

// ============================================================================
// Credit Management API
// ============================================================================

#[pyclass]
pub struct CreditApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl CreditApi {
    #[pyo3(signature = (customer_id, credit_limit, payment_terms=None))]
    fn create_credit_account(&self, customer_id: String, credit_limit: f64, payment_terms: Option<String>) -> PyResult<CreditAccount> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = customer_id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let account = commerce.credit().create_credit_account(stateset_core::CreateCreditAccount { customer_id: uuid, credit_limit: Decimal::from_f64_retain(credit_limit).unwrap_or_default(), payment_terms, ..Default::default() })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(account.into())
    }

    fn get_credit_account_by_customer(&self, customer_id: String) -> PyResult<Option<CreditAccount>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = customer_id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let account = commerce.credit().get_credit_account_by_customer(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(account.map(|a| a.into()))
    }

    fn check_credit(&self, customer_id: String, order_amount: f64) -> PyResult<CreditCheckResult> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = customer_id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let result = commerce.credit().check_credit(uuid, Decimal::from_f64_retain(order_amount).unwrap_or_default())
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(result.into())
    }

    fn adjust_credit_limit(&self, customer_id: String, new_limit: f64, reason: String) -> PyResult<CreditAccount> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = customer_id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let account = commerce.credit().adjust_credit_limit(uuid, Decimal::from_f64_retain(new_limit).unwrap_or_default(), &reason)
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(account.into())
    }

    fn get_over_limit_customers(&self) -> PyResult<Vec<CreditAccount>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let accounts = commerce.credit().get_over_limit_customers().map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(accounts.into_iter().map(|a| a.into()).collect())
    }
}

// ============================================================================
// Backorder Management Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct Backorder {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    backorder_number: String,
    #[pyo3(get)]
    order_id: String,
    #[pyo3(get)]
    customer_id: String,
    #[pyo3(get)]
    sku: String,
    #[pyo3(get)]
    quantity_ordered: f64,
    #[pyo3(get)]
    quantity_fulfilled: f64,
    #[pyo3(get)]
    quantity_remaining: f64,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    priority: String,
}

impl From<stateset_core::Backorder> for Backorder {
    fn from(b: stateset_core::Backorder) -> Self {
        Self {
            id: b.id.to_string(), backorder_number: b.backorder_number, order_id: b.order_id.to_string(), customer_id: b.customer_id.to_string(), sku: b.sku,
            quantity_ordered: to_f64_or_nan(b.quantity_ordered), quantity_fulfilled: to_f64_or_nan(b.quantity_fulfilled),
            quantity_remaining: to_f64_or_nan(b.quantity_remaining), status: format!("{:?}", b.status), priority: format!("{:?}", b.priority),
        }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct BackorderSummary {
    #[pyo3(get)]
    total_backorders: i32,
    #[pyo3(get)]
    total_quantity: f64,
    #[pyo3(get)]
    critical_count: i32,
    #[pyo3(get)]
    overdue_count: i32,
}

impl From<stateset_core::BackorderSummary> for BackorderSummary {
    fn from(s: stateset_core::BackorderSummary) -> Self {
        Self { total_backorders: s.total_backorders, total_quantity: to_f64_or_nan(s.total_quantity), critical_count: s.critical_count, overdue_count: s.overdue_count }
    }
}

// ============================================================================
// Backorder Management API
// ============================================================================

#[pyclass]
pub struct BackorderApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl BackorderApi {
    #[pyo3(signature = (order_id, customer_id, sku, quantity, priority=None))]
    fn create_backorder(&self, order_id: String, customer_id: String, sku: String, quantity: f64, priority: Option<String>) -> PyResult<Backorder> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let ord_uuid = order_id.parse().map_err(|_| PyValueError::new_err("Invalid order UUID"))?;
        let cust_uuid = customer_id.parse().map_err(|_| PyValueError::new_err("Invalid customer UUID"))?;
        let prio = priority.and_then(|p| match p.to_lowercase().as_str() {
            "low" => Some(stateset_core::BackorderPriority::Low),
            "normal" => Some(stateset_core::BackorderPriority::Normal),
            "high" => Some(stateset_core::BackorderPriority::High),
            "critical" => Some(stateset_core::BackorderPriority::Critical),
            _ => None,
        });
        let backorder = commerce.backorder().create_backorder(stateset_core::CreateBackorder { order_id: ord_uuid, customer_id: cust_uuid, sku, quantity: Decimal::from_f64_retain(quantity).unwrap_or_default(), priority: prio, order_line_id: None, expected_date: None, promised_date: None, source_location_id: None, notes: None })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(backorder.into())
    }

    fn get_backorder(&self, id: String) -> PyResult<Option<Backorder>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let backorder = commerce.backorder().get_backorder(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(backorder.map(|b| b.into()))
    }

    fn list_backorders(&self) -> PyResult<Vec<Backorder>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let backorders = commerce.backorder().list_backorders(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(backorders.into_iter().map(|b| b.into()).collect())
    }

    fn fulfill_backorder(&self, id: String, quantity: f64) -> PyResult<Backorder> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let backorder = commerce.backorder().fulfill_backorder(stateset_core::FulfillBackorder { backorder_id: uuid, quantity: Decimal::from_f64_retain(quantity).unwrap_or_default(), source_type: stateset_core::FulfillmentSourceType::Inventory, source_id: None, notes: None, fulfilled_by: None })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(backorder.into())
    }

    fn cancel_backorder(&self, id: String) -> PyResult<Backorder> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let backorder = commerce.backorder().cancel_backorder(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(backorder.into())
    }

    fn get_summary(&self) -> PyResult<BackorderSummary> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let summary = commerce.backorder().get_summary().map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(summary.into())
    }

    fn get_overdue_backorders(&self) -> PyResult<Vec<Backorder>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let backorders = commerce.backorder().get_overdue_backorders().map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(backorders.into_iter().map(|b| b.into()).collect())
    }
}

// ============================================================================
// General Ledger Types
// ============================================================================

#[pyclass]
#[derive(Clone)]
pub struct GlAccount {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    account_number: String,
    #[pyo3(get)]
    name: String,
    #[pyo3(get)]
    account_type: String,
    #[pyo3(get)]
    current_balance: f64,
    #[pyo3(get)]
    status: String,
}

impl From<stateset_core::GlAccount> for GlAccount {
    fn from(a: stateset_core::GlAccount) -> Self {
        Self { id: a.id.to_string(), account_number: a.account_number, name: a.name, account_type: format!("{:?}", a.account_type), current_balance: to_f64_or_nan(a.current_balance), status: format!("{:?}", a.status) }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct JournalEntry {
    #[pyo3(get)]
    id: String,
    #[pyo3(get)]
    entry_number: String,
    #[pyo3(get)]
    description: String,
    #[pyo3(get)]
    status: String,
    #[pyo3(get)]
    entry_date: String,
}

impl From<stateset_core::JournalEntry> for JournalEntry {
    fn from(e: stateset_core::JournalEntry) -> Self {
        Self { id: e.id.to_string(), entry_number: e.entry_number, description: e.description, status: format!("{:?}", e.status), entry_date: e.entry_date.to_string() }
    }
}

#[pyclass]
#[derive(Clone)]
pub struct TrialBalance {
    #[pyo3(get)]
    total_debits: f64,
    #[pyo3(get)]
    total_credits: f64,
    #[pyo3(get)]
    is_balanced: bool,
}

impl From<stateset_core::TrialBalance> for TrialBalance {
    fn from(t: stateset_core::TrialBalance) -> Self {
        Self { total_debits: to_f64_or_nan(t.total_debits), total_credits: to_f64_or_nan(t.total_credits), is_balanced: t.is_balanced }
    }
}

// ============================================================================
// General Ledger API
// ============================================================================

#[pyclass]
pub struct GeneralLedgerApi {
    commerce: Arc<Mutex<RustCommerce>>,
}

#[pymethods]
impl GeneralLedgerApi {
    #[pyo3(signature = (account_number, name, account_type, description=None))]
    fn create_account(&self, account_number: String, name: String, account_type: String, description: Option<String>) -> PyResult<GlAccount> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let acct_type = match account_type.to_lowercase().as_str() {
            "asset" => stateset_core::AccountType::Asset,
            "liability" => stateset_core::AccountType::Liability,
            "equity" => stateset_core::AccountType::Equity,
            "revenue" => stateset_core::AccountType::Revenue,
            "expense" => stateset_core::AccountType::Expense,
            _ => stateset_core::AccountType::Asset,
        };
        let account = commerce.general_ledger().create_account(stateset_core::CreateGlAccount { account_number, name, account_type: acct_type, description, account_sub_type: None, parent_account_id: None, is_header: None, is_posting: None, currency: None })
            .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(account.into())
    }

    fn get_account(&self, id: String) -> PyResult<Option<GlAccount>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let account = commerce.general_ledger().get_account(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(account.map(|a| a.into()))
    }

    fn get_account_by_number(&self, account_number: String) -> PyResult<Option<GlAccount>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let account = commerce.general_ledger().get_account_by_number(&account_number).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(account.map(|a| a.into()))
    }

    fn list_accounts(&self) -> PyResult<Vec<GlAccount>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let accounts = commerce.general_ledger().list_accounts(Default::default()).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(accounts.into_iter().map(|a| a.into()).collect())
    }

    fn get_journal_entry(&self, id: String) -> PyResult<Option<JournalEntry>> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let entry = commerce.general_ledger().get_journal_entry(uuid).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(entry.map(|e| e.into()))
    }

    fn post_journal_entry(&self, id: String, posted_by: String) -> PyResult<JournalEntry> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let uuid = id.parse().map_err(|_| PyValueError::new_err("Invalid UUID"))?;
        let entry = commerce.general_ledger().post_journal_entry(uuid, &posted_by).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(entry.into())
    }

    #[pyo3(signature = (as_of_date=None))]
    fn get_trial_balance(&self, as_of_date: Option<String>) -> PyResult<TrialBalance> {
        let commerce = self.commerce.lock().map_err(|e| PyRuntimeError::new_err(format!("Lock error: {}", e)))?;
        let date = as_of_date.and_then(|s| chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").ok()).unwrap_or_else(|| chrono::Utc::now().date_naive());
        let balance = commerce.general_ledger().get_trial_balance(date).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
        Ok(balance.into())
    }
}

// ============================================================================
// Module Definition
// ============================================================================

/// StateSet Embedded Commerce - Local-first commerce library
#[pymodule]
fn stateset_embedded(m: &Bound<'_, PyModule>) -> PyResult<()> {
    // Core
    m.add_class::<Commerce>()?;

    // Customers
    m.add_class::<Customers>()?;
    m.add_class::<Customer>()?;

    // Orders
    m.add_class::<Orders>()?;
    m.add_class::<Order>()?;
    m.add_class::<OrderItem>()?;
    m.add_class::<CreateOrderItemInput>()?;

    // Products
    m.add_class::<Products>()?;
    m.add_class::<Product>()?;
    m.add_class::<ProductVariant>()?;
    m.add_class::<CreateProductVariantInput>()?;

    // Inventory
    m.add_class::<Inventory>()?;
    m.add_class::<InventoryItem>()?;
    m.add_class::<StockLevel>()?;
    m.add_class::<Reservation>()?;

    // Returns
    m.add_class::<Returns>()?;
    m.add_class::<Return>()?;
    m.add_class::<CreateReturnItemInput>()?;

    // Payments
    m.add_class::<Payments>()?;
    m.add_class::<Payment>()?;
    m.add_class::<Refund>()?;

    // Shipments
    m.add_class::<Shipments>()?;
    m.add_class::<Shipment>()?;

    // Warranties
    m.add_class::<Warranties>()?;
    m.add_class::<Warranty>()?;
    m.add_class::<WarrantyClaim>()?;

    // Purchase Orders
    m.add_class::<PurchaseOrders>()?;
    m.add_class::<Supplier>()?;
    m.add_class::<PurchaseOrder>()?;

    // Invoices
    m.add_class::<Invoices>()?;
    m.add_class::<Invoice>()?;

    // Bill of Materials
    m.add_class::<BomApi>()?;
    m.add_class::<Bom>()?;
    m.add_class::<BomComponent>()?;

    // Work Orders
    m.add_class::<WorkOrders>()?;
    m.add_class::<WorkOrder>()?;

    // Carts
    m.add_class::<Carts>()?;
    m.add_class::<Cart>()?;
    m.add_class::<CartItem>()?;
    m.add_class::<CartAddress>()?;
    m.add_class::<AddCartItemInput>()?;
    m.add_class::<ShippingRate>()?;
    m.add_class::<CheckoutResult>()?;

    // Analytics
    m.add_class::<Analytics>()?;
    m.add_class::<SalesSummary>()?;
    m.add_class::<RevenueByPeriod>()?;
    m.add_class::<TopProduct>()?;
    m.add_class::<ProductPerformance>()?;
    m.add_class::<CustomerMetrics>()?;
    m.add_class::<TopCustomer>()?;
    m.add_class::<InventoryHealth>()?;
    m.add_class::<LowStockItem>()?;
    m.add_class::<InventoryMovement>()?;
    m.add_class::<OrderStatusBreakdown>()?;
    m.add_class::<FulfillmentMetrics>()?;
    m.add_class::<ReturnMetrics>()?;
    m.add_class::<DemandForecast>()?;
    m.add_class::<RevenueForecast>()?;

    // Currency
    m.add_class::<CurrencyOperations>()?;
    m.add_class::<ExchangeRate>()?;
    m.add_class::<ConversionResult>()?;
    m.add_class::<StoreCurrencySettings>()?;
    m.add_class::<SetExchangeRateInput>()?;

    // Subscriptions
    m.add_class::<Subscriptions>()?;
    m.add_class::<SubscriptionPlan>()?;
    m.add_class::<Subscription>()?;
    m.add_class::<BillingCycle>()?;
    m.add_class::<SubscriptionEvent>()?;

    // Promotions
    m.add_class::<PromotionsApi>()?;
    m.add_class::<Promotion>()?;
    m.add_class::<Coupon>()?;
    m.add_class::<ApplyPromotionsResult>()?;
    m.add_class::<AppliedPromotion>()?;
    m.add_class::<PromotionUsage>()?;

    // Tax
    m.add_class::<TaxApi>()?;
    m.add_class::<TaxJurisdiction>()?;
    m.add_class::<TaxRate>()?;
    m.add_class::<TaxExemption>()?;
    m.add_class::<TaxSettings>()?;
    m.add_class::<TaxCalculationResult>()?;
    m.add_class::<UsStateTaxInfo>()?;
    m.add_class::<EuVatInfo>()?;
    m.add_class::<CanadianTaxInfo>()?;

    // Quality Control
    m.add_class::<QualityApi>()?;
    m.add_class::<Inspection>()?;
    m.add_class::<NonConformance>()?;
    m.add_class::<QualityHold>()?;

    // Lots/Batch Tracking
    m.add_class::<LotsApi>()?;
    m.add_class::<Lot>()?;

    // Serial Numbers
    m.add_class::<SerialsApi>()?;
    m.add_class::<SerialNumber>()?;

    // Warehouse
    m.add_class::<WarehouseApi>()?;
    m.add_class::<Warehouse>()?;
    m.add_class::<WarehouseLocation>()?;

    // Receiving
    m.add_class::<ReceivingApi>()?;
    m.add_class::<Receipt>()?;
    m.add_class::<ReceiptLine>()?;

    // Fulfillment
    m.add_class::<FulfillmentApi>()?;
    m.add_class::<Wave>()?;
    m.add_class::<PickTask>()?;

    // Accounts Payable
    m.add_class::<AccountsPayableApi>()?;
    m.add_class::<Bill>()?;
    m.add_class::<ApAgingSummary>()?;

    // Accounts Receivable
    m.add_class::<AccountsReceivableApi>()?;
    m.add_class::<ArAgingSummary>()?;
    m.add_class::<CreditMemo>()?;

    // Cost Accounting
    m.add_class::<CostAccountingApi>()?;
    m.add_class::<ItemCost>()?;
    m.add_class::<InventoryValuation>()?;

    // Credit Management
    m.add_class::<CreditApi>()?;
    m.add_class::<CreditAccount>()?;
    m.add_class::<CreditCheckResult>()?;

    // Backorder Management
    m.add_class::<BackorderApi>()?;
    m.add_class::<Backorder>()?;
    m.add_class::<BackorderSummary>()?;

    // General Ledger
    m.add_class::<GeneralLedgerApi>()?;
    m.add_class::<GlAccount>()?;
    m.add_class::<JournalEntry>()?;
    m.add_class::<TrialBalance>()?;

    Ok(())
}
