import db from '../../config/database';
import { logger } from '../../utils/logger';
import { InventoryService } from '../inventory/InventoryService';
import { LogisticsService, LogisticsOption } from '../logistics/LogisticsService';
import { WarehouseService } from '../inventory/WarehouseService';
import { BullMQService } from '../core/BullMQService';
import { DomainEventBus } from '../../core/runtime/DomainEventBus';
import { OrderStatusEnum } from '../../shared/types/enums/BusinessEnums';

export type OrderFulfillmentStatus = 
  | 'PULLED'
  | 'PENDING_REVIEW'
  | 'CONFIRMED'
  | 'ALLOCATED'
  | 'READY_TO_SHIP'
  | 'SHIPPED'
  | 'DELIVERED'
  | 'CANCELLED'
  | 'EXCEPTION';

export type BusinessType = 'TOC' | 'TOB';

export interface OrderFulfillmentContext {
  tenantId: string;
  shopId: string;
  taskId: string;
  traceId: string;
  businessType: BusinessType;
}

export interface FulfillmentOrder {
  id: string;
  tenantId: string;
  shopId: string;
  platform: string;
  platformOrderId: string;
  customerId: string;
  customerName: string;
  customerEmail?: string;
  items: FulfillmentOrderItem[];
  totalAmount: number;
  currency: string;
  status: OrderFulfillmentStatus;
  shippingAddress: ShippingAddressInfo;
  businessType: BusinessType;
  warehouseId?: string;
  logisticsChannel?: string;
  trackingNumber?: string;
  estimatedDelivery?: Date;
  exceptionReason?: string;
  reviewNote?: string;
  createdAt: Date;
  updatedAt: Date;
}

export interface FulfillmentOrderItem {
  skuId: string;
  productId: string;
  productName: string;
  quantity: number;
  unitPrice: number;
  totalPrice: number;
  warehouseId?: string;
  allocatedQuantity?: number;
}

export interface ShippingAddressInfo {
  name: string;
  phone?: string;
  address: string;
  city: string;
  state?: string;
  zip: string;
  country: string;
  countryCode: string;
  isResidential?: boolean;
}

export interface WarehouseAllocation {
  warehouseId: string;
  warehouseName: string;
  items: AllocationItem[];
  totalCost: number;
  estimatedDays: number;
}

export interface AllocationItem {
  skuId: string;
  quantity: number;
  available: number;
  cost: number;
}

export interface FulfillmentResult {
  success: boolean;
  orderId: string;
  status: OrderFulfillmentStatus;
  message: string;
  data?: Record<string, any>;
}

export interface ReviewResult {
  approved: boolean;
  orderId: string;
  reason?: string;
  note?: string;
}

const ALLOWED_TRANSITIONS: Record<OrderFulfillmentStatus, OrderFulfillmentStatus[]> = {
  'PULLED': ['PENDING_REVIEW', 'EXCEPTION'],
  'PENDING_REVIEW': ['CONFIRMED', 'CANCELLED', 'EXCEPTION'],
  'CONFIRMED': ['ALLOCATED', 'EXCEPTION'],
  'ALLOCATED': ['READY_TO_SHIP', 'EXCEPTION'],
  'READY_TO_SHIP': ['SHIPPED', 'EXCEPTION'],
  'SHIPPED': ['DELIVERED', 'EXCEPTION'],
  'DELIVERED': [],
  'CANCELLED': [],
  'EXCEPTION': ['PENDING_REVIEW', 'CONFIRMED', 'CANCELLED']
};

const TOB_PROFIT_THRESHOLD = 0.15;
const TOC_PROFIT_THRESHOLD = 0.20;
const TOB_HIGH_VALUE_THRESHOLD = 5000;

export class OrderFulfillmentService {
  private static readonly TABLE_NAME = 'cf_order_fulfillment';
  private static readonly ITEM_TABLE = 'cf_order_fulfillment_items';

  static async initTables(): Promise<void> {
    await this.initFulfillmentTable();
    await this.initFulfillmentItemsTable();
  }

  private static async initFulfillmentTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.TABLE_NAME);
    if (!hasTable) {
      logger.info(`[OrderFulfillment] Creating ${this.TABLE_NAME} table...`);
      await db.schema.createTable(this.TABLE_NAME, (table) => {
        table.string('id', 64).primary();
        table.string('tenant_id', 64).notNullable();
        table.string('shop_id', 64).notNullable();
        table.string('platform', 32).notNullable();
        table.string('platform_order_id', 128).notNullable();
        table.string('customer_id', 64).notNullable();
        table.string('customer_name', 128);
        table.string('customer_email', 128);
        table.decimal('total_amount', 15, 2).defaultTo(0);
        table.string('currency', 10).defaultTo('USD');
        table.enum('status', [
          'PULLED', 'PENDING_REVIEW', 'CONFIRMED', 'ALLOCATED', 
          'READY_TO_SHIP', 'SHIPPED', 'DELIVERED', 'CANCELLED', 'EXCEPTION'
        ]).defaultTo('PULLED');
        table.json('shipping_address');
        table.enum('business_type', ['TOC', 'TOB']).defaultTo('TOC');
        table.string('warehouse_id', 64);
        table.string('logistics_channel', 64);
        table.string('tracking_number', 64);
        table.datetime('estimated_delivery');
        table.string('exception_reason', 512);
        table.text('review_note');
        table.string('trace_id', 64);
        table.timestamps(true, true);

        table.index(['tenant_id', 'status'], 'idx_fulfillment_tenant_status');
        table.index(['tenant_id', 'shop_id'], 'idx_fulfillment_tenant_shop');
        table.unique(['tenant_id', 'platform', 'platform_order_id'], 'uk_fulfillment_platform_order');
      });
      logger.info(`[OrderFulfillment] Table ${this.TABLE_NAME} created`);
    }
  }

  private static async initFulfillmentItemsTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.ITEM_TABLE);
    if (!hasTable) {
      logger.info(`[OrderFulfillment] Creating ${this.ITEM_TABLE} table...`);
      await db.schema.createTable(this.ITEM_TABLE, (table) => {
        table.string('id', 64).primary();
        table.string('order_id', 64).notNullable();
        table.string('tenant_id', 64).notNullable();
        table.string('sku_id', 64).notNullable();
        table.string('product_id', 64).notNullable();
        table.string('product_name', 255);
        table.integer('quantity').notNullable();
        table.decimal('unit_price', 15, 2);
        table.decimal('total_price', 15, 2);
        table.string('warehouse_id', 64);
        table.integer('allocated_quantity').defaultTo(0);
        table.timestamps(true, true);

        table.index(['order_id'], 'idx_fulfillment_item_order');
        table.index(['sku_id', 'warehouse_id'], 'idx_fulfillment_item_sku_wh');
      });
      logger.info(`[OrderFulfillment] Table ${this.ITEM_TABLE} created`);
    }
  }

  static async pullOrder(
    context: OrderFulfillmentContext,
    orderData: Partial<FulfillmentOrder>
  ): Promise<FulfillmentResult> {
    const { tenantId, shopId, taskId, traceId, businessType } = context;
    logger.info(`[OrderFulfillment] Pulling order - traceId: ${traceId}, businessType: ${businessType}`);

    try {
      const existingOrder = await db(this.TABLE_NAME)
        .where({
          tenant_id: tenantId,
          platform: orderData.platform,
          platform_order_id: orderData.platformOrderId
        })
        .first();

      if (existingOrder) {
        logger.info(`[OrderFulfillment] Order already exists: ${orderData.platformOrderId}`);
        return {
          success: true,
          orderId: existingOrder.id,
          status: existingOrder.status,
          message: 'Order already pulled'
        };
      }

      const orderId = this.generateOrderId();
      const order: FulfillmentOrder = {
        id: orderId,
        tenantId,
        shopId,
        platform: orderData.platform || 'UNKNOWN',
        platformOrderId: orderData.platformOrderId || '',
        customerId: orderData.customerId || '',
        customerName: orderData.customerName || '',
        customerEmail: orderData.customerEmail,
        items: orderData.items || [],
        totalAmount: orderData.totalAmount || 0,
        currency: orderData.currency || 'USD',
        status: 'PULLED',
        shippingAddress: orderData.shippingAddress || {} as ShippingAddressInfo,
        businessType,
        createdAt: new Date(),
        updatedAt: new Date()
      };

      await db(this.TABLE_NAME).insert({
        id: order.id,
        tenant_id: order.tenantId,
        shop_id: order.shopId,
        platform: order.platform,
        platform_order_id: order.platformOrderId,
        customer_id: order.customerId,
        customer_name: order.customerName,
        customer_email: order.customerEmail,
        total_amount: order.totalAmount,
        currency: order.currency,
        status: order.status,
        shipping_address: JSON.stringify(order.shippingAddress),
        business_type: order.businessType,
        trace_id: traceId,
        created_at: order.createdAt,
        updated_at: order.updatedAt
      });

      if (order.items.length > 0) {
        const itemRecords = order.items.map(item => ({
          id: this.generateItemId(),
          order_id: order.id,
          tenant_id: tenantId,
          sku_id: item.skuId,
          product_id: item.productId,
          product_name: item.productName,
          quantity: item.quantity,
          unit_price: item.unitPrice,
          total_price: item.totalPrice,
          created_at: new Date(),
          updated_at: new Date()
        }));
        await db(this.ITEM_TABLE).insert(itemRecords);
      }

      await this.transitionStatus(context, order.id, 'PENDING_REVIEW', 'Order pulled successfully');

      await BullMQService.addJob('order.pulled', {
        orderId: order.id,
        tenantId,
        traceId
      });

      DomainEventBus.publish('order.pulled', {
        orderId: order.id,
        tenantId,
        platform: order.platform,
        businessType,
        timestamp: new Date()
      });

      logger.info(`[OrderFulfillment] Order pulled successfully: ${orderId}`);

      return {
        success: true,
        orderId,
        status: 'PENDING_REVIEW',
        message: 'Order pulled and pending review'
      };
    } catch (error: any) {
      logger.error(`[OrderFulfillment] Failed to pull order: ${error.message}`);
      throw error;
    }
  }

  static async reviewOrder(
    context: OrderFulfillmentContext,
    orderId: string,
    reviewData: {
      approved: boolean;
      reason?: string;
      note?: string;
    }
  ): Promise<FulfillmentResult> {
    const { tenantId, traceId, businessType } = context;
    logger.info(`[OrderFulfillment] Reviewing order: ${orderId} - traceId: ${traceId}`);

    try {
      const order = await this.getOrderById(tenantId, orderId);
      if (!order) {
        throw new Error(`Order not found: ${orderId}`);
      }

      if (order.status !== 'PENDING_REVIEW') {
        throw new Error(`Invalid status for review: ${order.status}`);
      }

      const reviewResult = await this.performOrderReview(context, order);

      if (!reviewResult.approved) {
        await this.transitionStatus(context, orderId, 'CANCELLED', reviewResult.reason);
        return {
          success: true,
          orderId,
          status: 'CANCELLED',
          message: reviewResult.reason || 'Order rejected during review'
        };
      }

      await db(this.TABLE_NAME)
        .where({ id: orderId, tenant_id: tenantId })
        .update({
          review_note: reviewData.note || reviewResult.note,
          updated_at: new Date()
        });

      await this.transitionStatus(context, orderId, 'CONFIRMED', 'Order approved');

      return {
        success: true,
        orderId,
        status: 'CONFIRMED',
        message: 'Order approved and confirmed'
      };
    } catch (error: any) {
      logger.error(`[OrderFulfillment] Review failed: ${error.message}`);
      throw error;
    }
  }

  private static async performOrderReview(
    context: OrderFulfillmentContext,
    order: FulfillmentOrder
  ): Promise<ReviewResult> {
    const { businessType } = context;
    const address = order.shippingAddress;

    if (businessType === 'TOB' && address.isResidential) {
      return {
        approved: false,
        orderId: order.id,
        reason: 'B2B orders cannot be shipped to residential addresses'
      };
    }

    const forbiddenZones = ['PO BOX', 'APO', 'FPO', 'BFPO'];
    const addrStr = `${address.address} ${address.city} ${address.state || ''}`.toUpperCase();
    if (forbiddenZones.some(zone => addrStr.includes(zone))) {
      return {
        approved: false,
        orderId: order.id,
        reason: 'Logistics forbidden zone detected (PO Box/APO/FPO)'
      };
    }

    if (businessType === 'TOB' && order.totalAmount > TOB_HIGH_VALUE_THRESHOLD) {
      return {
        approved: false,
        orderId: order.id,
        reason: `High value B2B order (${order.totalAmount}) requires manual approval`,
        note: 'Order flagged for manual review due to high value'
      };
    }

    const riskScore = Math.random();
    if (riskScore > 0.95) {
      return {
        approved: false,
        orderId: order.id,
        reason: `High fraud risk score: ${riskScore.toFixed(2)}`
      };
    }

    return {
      approved: true,
      orderId: order.id,
      note: 'All checks passed'
    };
  }

  static async allocateWarehouse(
    context: OrderFulfillmentContext,
    orderId: string
  ): Promise<FulfillmentResult> {
    const { tenantId, traceId, businessType } = context;
    logger.info(`[OrderFulfillment] Allocating warehouse for order: ${orderId} - traceId: ${traceId}`);

    try {
      const order = await this.getOrderById(tenantId, orderId);
      if (!order) {
        throw new Error(`Order not found: ${orderId}`);
      }

      if (order.status !== 'CONFIRMED') {
        throw new Error(`Invalid status for allocation: ${order.status}`);
      }

      const items = await db(this.ITEM_TABLE)
        .where({ order_id: orderId, tenant_id: tenantId })
        .select('*');

      if (items.length === 0) {
        throw new Error('No items found for allocation');
      }

      const allocations: WarehouseAllocation[] = [];
      let allAllocated = true;

      for (const item of items) {
        const allocation = await this.allocateItemToWarehouse(
          tenantId,
          item.sku_id,
          item.quantity,
          order.shippingAddress.countryCode
        );

        if (allocation) {
          await db(this.ITEM_TABLE)
            .where({ id: item.id })
            .update({
              warehouse_id: allocation.warehouseId,
              allocated_quantity: item.quantity,
              updated_at: new Date()
            });

          allocations.push(allocation);
        } else {
          allAllocated = false;
          logger.warn(`[OrderFulfillment] Failed to allocate item: ${item.sku_id}`);
        }
      }

      if (!allAllocated) {
        await this.transitionStatus(context, orderId, 'EXCEPTION', 'Insufficient stock for allocation');
        return {
          success: false,
          orderId,
          status: 'EXCEPTION',
          message: 'Failed to allocate all items'
        };
      }

      const primaryWarehouse = allocations[0]?.warehouseId || 'DEFAULT';
      await db(this.TABLE_NAME)
        .where({ id: orderId, tenant_id: tenantId })
        .update({
          warehouse_id: primaryWarehouse,
          updated_at: new Date()
        });

      await this.transitionStatus(context, orderId, 'ALLOCATED', 'Warehouse allocated');

      return {
        success: true,
        orderId,
        status: 'ALLOCATED',
        message: 'Warehouse allocated successfully',
        data: { allocations }
      };
    } catch (error: any) {
      logger.error(`[OrderFulfillment] Allocation failed: ${error.message}`);
      throw error;
    }
  }

  private static async allocateItemToWarehouse(
    tenantId: string,
    skuId: string,
    quantity: number,
    destinationCountry: string
  ): Promise<WarehouseAllocation | null> {
    const recommendedWarehouse = await WarehouseService.recommendWarehouse(destinationCountry, tenantId);

    const stock = await InventoryService.getStock(tenantId, skuId, recommendedWarehouse);
    if (stock && stock.quantity >= quantity) {
      const locked = await InventoryService.lockStock(tenantId, skuId, quantity, recommendedWarehouse);
      if (locked) {
        return {
          warehouseId: recommendedWarehouse,
          warehouseName: 'Primary Warehouse',
          items: [{
            skuId,
            quantity,
            available: stock.quantity,
            cost: 0
          }],
          totalCost: 0,
          estimatedDays: 5
        };
      }
    }

    const allStock = await db('cf_inventory')
      .where({ tenant_id: tenantId, sku_id: skuId })
      .whereRaw('quantity_on_hand - quantity_reserved >= ?', [quantity])
      .select('*');

    if (allStock.length > 0) {
      const warehouse = allStock[0];
      const locked = await InventoryService.lockStock(tenantId, skuId, quantity, warehouse.warehouse_id);
      if (locked) {
        return {
          warehouseId: warehouse.warehouse_id,
          warehouseName: 'Alternative Warehouse',
          items: [{
            skuId,
            quantity,
            available: warehouse.quantity_on_hand - warehouse.quantity_reserved,
            cost: 0
          }],
          totalCost: 0,
          estimatedDays: 7
        };
      }
    }

    return null;
  }

  static async prepareShipment(
    context: OrderFulfillmentContext,
    orderId: string,
    logisticsOption?: LogisticsOption
  ): Promise<FulfillmentResult> {
    const { tenantId, traceId, businessType } = context;
    logger.info(`[OrderFulfillment] Preparing shipment for order: ${orderId} - traceId: ${traceId}`);

    try {
      const order = await this.getOrderById(tenantId, orderId);
      if (!order) {
        throw new Error(`Order not found: ${orderId}`);
      }

      if (order.status !== 'ALLOCATED') {
        throw new Error(`Invalid status for shipment preparation: ${order.status}`);
      }

      const destination = order.shippingAddress.countryCode || 'US';
      const strategy = await LogisticsService.getLogisticsStrategy({
        orderId,
        destination,
        tenantId,
        shopId: order.shopId,
        traceId,
        businessType
      });

      const selectedOption = logisticsOption || strategy.recommendedOption;

      await db(this.TABLE_NAME)
        .where({ id: orderId, tenant_id: tenantId })
        .update({
          logistics_channel: selectedOption.carrierId,
          estimated_delivery: new Date(Date.now() + selectedOption.estimatedDays * 24 * 60 * 60 * 1000),
          updated_at: new Date()
        });

      await this.transitionStatus(context, orderId, 'READY_TO_SHIP', 'Shipment prepared');

      return {
        success: true,
        orderId,
        status: 'READY_TO_SHIP',
        message: 'Shipment prepared successfully',
        data: {
          logisticsOption: selectedOption,
          alternatives: strategy.alternatives
        }
      };
    } catch (error: any) {
      logger.error(`[OrderFulfillment] Shipment preparation failed: ${error.message}`);
      throw error;
    }
  }

  static async shipOrder(
    context: OrderFulfillmentContext,
    orderId: string,
    shipmentData: {
      trackingNumber: string;
      carrier?: string;
      estimatedDelivery?: Date;
    }
  ): Promise<FulfillmentResult> {
    const { tenantId, traceId } = context;
    logger.info(`[OrderFulfillment] Shipping order: ${orderId} - traceId: ${traceId}`);

    try {
      const order = await this.getOrderById(tenantId, orderId);
      if (!order) {
        throw new Error(`Order not found: ${orderId}`);
      }

      if (order.status !== 'READY_TO_SHIP') {
        throw new Error(`Invalid status for shipping: ${order.status}`);
      }

      const items = await db(this.ITEM_TABLE)
        .where({ order_id: orderId, tenant_id: tenantId })
        .select('*');

      for (const item of items) {
        if (item.warehouse_id) {
          await InventoryService.deductStock(
            tenantId,
            item.sku_id,
            item.allocated_quantity || item.quantity,
            item.warehouse_id
          );
        }
      }

      await db(this.TABLE_NAME)
        .where({ id: orderId, tenant_id: tenantId })
        .update({
          tracking_number: shipmentData.trackingNumber,
          carrier: shipmentData.carrier,
          estimated_delivery: shipmentData.estimatedDelivery,
          updated_at: new Date()
        });

      await this.transitionStatus(context, orderId, 'SHIPPED', 'Order shipped');

      DomainEventBus.publish('order.shipped', {
        orderId,
        tenantId,
        trackingNumber: shipmentData.trackingNumber,
        carrier: shipmentData.carrier,
        timestamp: new Date()
      });

      return {
        success: true,
        orderId,
        status: 'SHIPPED',
        message: 'Order shipped successfully',
        data: {
          trackingNumber: shipmentData.trackingNumber,
          estimatedDelivery: shipmentData.estimatedDelivery
        }
      };
    } catch (error: any) {
      logger.error(`[OrderFulfillment] Shipping failed: ${error.message}`);
      throw error;
    }
  }

  static async confirmDelivery(
    context: OrderFulfillmentContext,
    orderId: string,
    deliveryData?: {
      deliveredAt?: Date;
      signature?: string;
    }
  ): Promise<FulfillmentResult> {
    const { tenantId, traceId } = context;
    logger.info(`[OrderFulfillment] Confirming delivery for order: ${orderId} - traceId: ${traceId}`);

    try {
      const order = await this.getOrderById(tenantId, orderId);
      if (!order) {
        throw new Error(`Order not found: ${orderId}`);
      }

      if (order.status !== 'SHIPPED') {
        throw new Error(`Invalid status for delivery confirmation: ${order.status}`);
      }

      await db(this.TABLE_NAME)
        .where({ id: orderId, tenant_id: tenantId })
        .update({
          status: 'DELIVERED',
          updated_at: new Date()
        });

      await this.transitionStatus(context, orderId, 'DELIVERED', 'Order delivered');

      DomainEventBus.publish('order.delivered', {
        orderId,
        tenantId,
        deliveredAt: deliveryData?.deliveredAt || new Date(),
        timestamp: new Date()
      });

      return {
        success: true,
        orderId,
        status: 'DELIVERED',
        message: 'Order delivered successfully'
      };
    } catch (error: any) {
      logger.error(`[OrderFulfillment] Delivery confirmation failed: ${error.message}`);
      throw error;
    }
  }

  static async markAsException(
    context: OrderFulfillmentContext,
    orderId: string,
    reason: string
  ): Promise<FulfillmentResult> {
    const { tenantId, traceId } = context;
    logger.info(`[OrderFulfillment] Marking order as exception: ${orderId} - traceId: ${traceId}`);

    try {
      const order = await this.getOrderById(tenantId, orderId);
      if (!order) {
        throw new Error(`Order not found: ${orderId}`);
      }

      await this.transitionStatus(context, orderId, 'EXCEPTION', reason);

      return {
        success: true,
        orderId,
        status: 'EXCEPTION',
        message: `Order marked as exception: ${reason}`
      };
    } catch (error: any) {
      logger.error(`[OrderFulfillment] Exception marking failed: ${error.message}`);
      throw error;
    }
  }

  static async cancelOrder(
    context: OrderFulfillmentContext,
    orderId: string,
    reason: string
  ): Promise<FulfillmentResult> {
    const { tenantId, traceId } = context;
    logger.info(`[OrderFulfillment] Cancelling order: ${orderId} - traceId: ${traceId}`);

    try {
      const order = await this.getOrderById(tenantId, orderId);
      if (!order) {
        throw new Error(`Order not found: ${orderId}`);
      }

      if (order.status === 'SHIPPED' || order.status === 'DELIVERED') {
        throw new Error(`Cannot cancel order in status: ${order.status}`);
      }

      const items = await db(this.ITEM_TABLE)
        .where({ order_id: orderId, tenant_id: tenantId })
        .select('*');

      for (const item of items) {
        if (item.warehouse_id && item.allocated_quantity > 0) {
          await InventoryService.releaseStock(
            tenantId,
            item.sku_id,
            item.allocated_quantity,
            item.warehouse_id
          );
        }
      }

      await this.transitionStatus(context, orderId, 'CANCELLED', reason);

      DomainEventBus.publish('order.cancelled', {
        orderId,
        tenantId,
        reason,
        timestamp: new Date()
      });

      return {
        success: true,
        orderId,
        status: 'CANCELLED',
        message: `Order cancelled: ${reason}`
      };
    } catch (error: any) {
      logger.error(`[OrderFulfillment] Cancellation failed: ${error.message}`);
      throw error;
    }
  }

  static async getOrderById(tenantId: string, orderId: string): Promise<FulfillmentOrder | null> {
    const order = await db(this.TABLE_NAME)
      .where({ id: orderId, tenant_id: tenantId })
      .first();

    if (!order) return null;

    const items = await db(this.ITEM_TABLE)
      .where({ order_id: orderId, tenant_id: tenantId })
      .select('*');

    return {
      id: order.id,
      tenantId: order.tenant_id,
      shopId: order.shop_id,
      platform: order.platform,
      platformOrderId: order.platform_order_id,
      customerId: order.customer_id,
      customerName: order.customer_name,
      customerEmail: order.customer_email,
      items: items.map(item => ({
        skuId: item.sku_id,
        productId: item.product_id,
        productName: item.product_name,
        quantity: item.quantity,
        unitPrice: Number(item.unit_price),
        totalPrice: Number(item.total_price),
        warehouseId: item.warehouse_id,
        allocatedQuantity: item.allocated_quantity
      })),
      totalAmount: Number(order.total_amount),
      currency: order.currency,
      status: order.status,
      shippingAddress: typeof order.shipping_address === 'string' 
        ? JSON.parse(order.shipping_address) 
        : order.shipping_address,
      businessType: order.business_type,
      warehouseId: order.warehouse_id,
      logisticsChannel: order.logistics_channel,
      trackingNumber: order.tracking_number,
      estimatedDelivery: order.estimated_delivery,
      exceptionReason: order.exception_reason,
      reviewNote: order.review_note,
      createdAt: order.created_at,
      updatedAt: order.updated_at
    };
  }

  static async getOrders(
    tenantId: string,
    params: {
      status?: OrderFulfillmentStatus;
      shopId?: string;
      startDate?: Date;
      endDate?: Date;
      page?: number;
      pageSize?: number;
    }
  ): Promise<{ orders: FulfillmentOrder[]; total: number }> {
    const { status, shopId, startDate, endDate, page = 1, pageSize = 20 } = params;
    const offset = (page - 1) * pageSize;

    let query = db(this.TABLE_NAME).where({ tenant_id: tenantId });

    if (status) query = query.where({ status });
    if (shopId) query = query.where({ shop_id: shopId });
    if (startDate) query = query.where('created_at', '>=', startDate);
    if (endDate) query = query.where('created_at', '<=', endDate);

    const total = await query.clone().count('id as count').first();

    const orders = await query
      .offset(offset)
      .limit(pageSize)
      .orderBy('created_at', 'desc');

    return {
      orders: orders.map(order => ({
        id: order.id,
        tenantId: order.tenant_id,
        shopId: order.shop_id,
        platform: order.platform,
        platformOrderId: order.platform_order_id,
        customerId: order.customer_id,
        customerName: order.customer_name,
        customerEmail: order.customer_email,
        items: [],
        totalAmount: Number(order.total_amount),
        currency: order.currency,
        status: order.status,
        shippingAddress: typeof order.shipping_address === 'string' 
          ? JSON.parse(order.shipping_address) 
          : order.shipping_address,
        businessType: order.business_type,
        warehouseId: order.warehouse_id,
        logisticsChannel: order.logistics_channel,
        trackingNumber: order.tracking_number,
        estimatedDelivery: order.estimated_delivery,
        exceptionReason: order.exception_reason,
        reviewNote: order.review_note,
        createdAt: order.created_at,
        updatedAt: order.updated_at
      })),
      total: Number(total?.count || 0)
    };
  }

  static async batchReview(
    context: OrderFulfillmentContext,
    orderIds: string[]
  ): Promise<{ success: number; failed: number; results: FulfillmentResult[] }> {
    const results: FulfillmentResult[] = [];
    let success = 0;
    let failed = 0;

    for (const orderId of orderIds) {
      try {
        const result = await this.reviewOrder(context, orderId, { approved: true });
        results.push(result);
        if (result.success) success++;
        else failed++;
      } catch (error: any) {
        failed++;
        results.push({
          success: false,
          orderId,
          status: 'PENDING_REVIEW',
          message: error.message
        });
      }
    }

    return { success, failed, results };
  }

  static async batchAllocate(
    context: OrderFulfillmentContext,
    orderIds: string[]
  ): Promise<{ success: number; failed: number; results: FulfillmentResult[] }> {
    const results: FulfillmentResult[] = [];
    let success = 0;
    let failed = 0;

    for (const orderId of orderIds) {
      try {
        const result = await this.allocateWarehouse(context, orderId);
        results.push(result);
        if (result.success) success++;
        else failed++;
      } catch (error: any) {
        failed++;
        results.push({
          success: false,
          orderId,
          status: 'CONFIRMED',
          message: error.message
        });
      }
    }

    return { success, failed, results };
  }

  static async batchShip(
    context: OrderFulfillmentContext,
    shipments: Array<{
      orderId: string;
      trackingNumber: string;
      carrier?: string;
    }>
  ): Promise<{ success: number; failed: number; results: FulfillmentResult[] }> {
    const results: FulfillmentResult[] = [];
    let success = 0;
    let failed = 0;

    for (const shipment of shipments) {
      try {
        const result = await this.shipOrder(context, shipment.orderId, {
          trackingNumber: shipment.trackingNumber,
          carrier: shipment.carrier
        });
        results.push(result);
        if (result.success) success++;
        else failed++;
      } catch (error: any) {
        failed++;
        results.push({
          success: false,
          orderId: shipment.orderId,
          status: 'READY_TO_SHIP',
          message: error.message
        });
      }
    }

    return { success, failed, results };
  }

  private static async transitionStatus(
    context: OrderFulfillmentContext,
    orderId: string,
    nextStatus: OrderFulfillmentStatus,
    reason?: string
  ): Promise<void> {
    const { tenantId, traceId } = context;

    const order = await db(this.TABLE_NAME)
      .where({ id: orderId, tenant_id: tenantId })
      .first();

    if (!order) {
      throw new Error(`Order not found: ${orderId}`);
    }

    const currentStatus = order.status as OrderFulfillmentStatus;

    if (!ALLOWED_TRANSITIONS[currentStatus]?.includes(nextStatus)) {
      throw new Error(
        `Invalid status transition: ${currentStatus} -> ${nextStatus}`
      );
    }

    await db(this.TABLE_NAME)
      .where({ id: orderId, tenant_id: tenantId })
      .update({
        status: nextStatus,
        exception_reason: nextStatus === 'EXCEPTION' ? reason : null,
        updated_at: new Date()
      });

    logger.info(
      `[OrderFulfillment] Status transitioned: ${orderId} ${currentStatus} -> ${nextStatus} - traceId: ${traceId}`
    );

    DomainEventBus.publish('order.status.changed', {
      orderId,
      tenantId,
      fromStatus: currentStatus,
      toStatus: nextStatus,
      reason,
      timestamp: new Date()
    });
  }

  private static generateOrderId(): string {
    return `FO-${Date.now()}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
  }

  private static generateItemId(): string {
    return `FI-${Date.now()}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
  }
}
