import db from '../config/database';
import { logger } from '../utils/logger';

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

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

export interface Order {
  id: string;
  tenantId: string;
  shopId: string;
  platform: string;
  platformOrderId: string;
  customerId: string;
  customerName: string;
  customerEmail?: string;
  totalAmount: number;
  currency: string;
  status: OrderFulfillmentStatus;
  shippingAddress: ShippingAddress;
  businessType: BusinessType;
  warehouseId?: string;
  logisticsChannel?: string;
  trackingNumber?: string;
  estimatedDelivery?: Date;
  exceptionReason?: string;
  reviewNote?: string;
  traceId?: string;
  createdAt: Date;
  updatedAt: Date;
}

export interface OrderItem {
  id: string;
  orderId: string;
  tenantId: string;
  skuId: string;
  productId: string;
  productName: string;
  quantity: number;
  unitPrice: number;
  totalPrice: number;
  warehouseId?: string;
  allocatedQuantity: number;
  createdAt: Date;
  updatedAt: Date;
}

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

export interface OrderWithItems extends Order {
  items: OrderItem[];
}

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

  static async create(order: Partial<Order>): Promise<string> {
    const id = order.id || this.generateId();
    
    await db(this.TABLE_NAME).insert({
      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 || 'USD',
      status: order.status || 'PULLED',
      shipping_address: JSON.stringify(order.shippingAddress || {}),
      business_type: order.businessType || 'TOC',
      warehouse_id: order.warehouseId,
      logistics_channel: order.logisticsChannel,
      tracking_number: order.trackingNumber,
      estimated_delivery: order.estimatedDelivery,
      exception_reason: order.exceptionReason,
      review_note: order.reviewNote,
      trace_id: order.traceId,
      created_at: order.createdAt || new Date(),
      updated_at: order.updatedAt || new Date()
    });

    return id;
  }

  static async findById(tenantId: string, orderId: string): Promise<OrderWithItems | 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 this.mapToOrderWithItems(order, items);
  }

  static async findByPlatformOrderId(
    tenantId: string,
    platform: string,
    platformOrderId: string
  ): Promise<OrderWithItems | null> {
    const order = await db(this.TABLE_NAME)
      .where({
        tenant_id: tenantId,
        platform,
        platform_order_id: platformOrderId
      })
      .first();

    if (!order) return null;

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

    return this.mapToOrderWithItems(order, items);
  }

  static async findByStatus(
    tenantId: string,
    status: OrderFulfillmentStatus,
    options?: { limit?: number; offset?: number }
  ): Promise<Order[]> {
    let query = db(this.TABLE_NAME)
      .where({ tenant_id: tenantId, status })
      .orderBy('created_at', 'desc');

    if (options?.limit) query = query.limit(options.limit);
    if (options?.offset) query = query.offset(options.offset);

    const orders = await query;
    return orders.map(o => this.mapToOrder(o));
  }

  static async findMany(
    tenantId: string,
    params: {
      status?: OrderFulfillmentStatus;
      shopId?: string;
      startDate?: Date;
      endDate?: Date;
      limit?: number;
      offset?: number;
    }
  ): Promise<{ orders: Order[]; total: number }> {
    let query = db(this.TABLE_NAME).where({ tenant_id: tenantId });

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

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

    let dataQuery = query.orderBy('created_at', 'desc');
    if (params.limit) dataQuery = dataQuery.limit(params.limit);
    if (params.offset) dataQuery = dataQuery.offset(params.offset);

    const orders = await dataQuery;
    return {
      orders: orders.map(o => this.mapToOrder(o)),
      total: Number(total?.count || 0)
    };
  }

  static async update(
    tenantId: string,
    orderId: string,
    updates: Partial<Order>
  ): Promise<boolean> {
    const updateData: Record<string, any> = {
      updated_at: new Date()
    };

    if (updates.status !== undefined) updateData.status = updates.status;
    if (updates.warehouseId !== undefined) updateData.warehouse_id = updates.warehouseId;
    if (updates.logisticsChannel !== undefined) updateData.logistics_channel = updates.logisticsChannel;
    if (updates.trackingNumber !== undefined) updateData.tracking_number = updates.trackingNumber;
    if (updates.estimatedDelivery !== undefined) updateData.estimated_delivery = updates.estimatedDelivery;
    if (updates.exceptionReason !== undefined) updateData.exception_reason = updates.exceptionReason;
    if (updates.reviewNote !== undefined) updateData.review_note = updates.reviewNote;

    const result = await db(this.TABLE_NAME)
      .where({ id: orderId, tenant_id: tenantId })
      .update(updateData);

    return result > 0;
  }

  static async delete(tenantId: string, orderId: string): Promise<boolean> {
    await db(this.ITEM_TABLE)
      .where({ order_id: orderId, tenant_id: tenantId })
      .del();

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

    return result > 0;
  }

  static async createItem(item: Partial<OrderItem>): Promise<string> {
    const id = item.id || this.generateItemId();
    
    await db(this.ITEM_TABLE).insert({
      id,
      order_id: item.orderId,
      tenant_id: item.tenantId,
      sku_id: item.skuId,
      product_id: item.productId,
      product_name: item.productName,
      quantity: item.quantity,
      unit_price: item.unitPrice,
      total_price: item.totalPrice,
      warehouse_id: item.warehouseId,
      allocated_quantity: item.allocatedQuantity || 0,
      created_at: item.createdAt || new Date(),
      updated_at: item.updatedAt || new Date()
    });

    return id;
  }

  static async findItemsByOrderId(orderId: string): Promise<OrderItem[]> {
    const items = await db(this.ITEM_TABLE)
      .where({ order_id: orderId })
      .select('*');

    return items.map(i => this.mapToItem(i));
  }

  static async updateItem(
    itemId: string,
    updates: Partial<OrderItem>
  ): Promise<boolean> {
    const updateData: Record<string, any> = {
      updated_at: new Date()
    };

    if (updates.warehouseId !== undefined) updateData.warehouse_id = updates.warehouseId;
    if (updates.allocatedQuantity !== undefined) updateData.allocated_quantity = updates.allocatedQuantity;

    const result = await db(this.ITEM_TABLE)
      .where({ id: itemId })
      .update(updateData);

    return result > 0;
  }

  static async countByStatus(tenantId: string): Promise<Record<OrderFulfillmentStatus, number>> {
    const result = await db(this.TABLE_NAME)
      .where({ tenant_id: tenantId })
      .select('status')
      .count('id as count')
      .groupBy('status');

    const counts: Record<string, number> = {};
    result.forEach((row: any) => {
      counts[row.status] = Number(row.count);
    });

    return counts as Record<OrderFulfillmentStatus, number>;
  }

  private static mapToOrder(row: any): Order {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      shopId: row.shop_id,
      platform: row.platform,
      platformOrderId: row.platform_order_id,
      customerId: row.customer_id,
      customerName: row.customer_name,
      customerEmail: row.customer_email,
      totalAmount: Number(row.total_amount),
      currency: row.currency,
      status: row.status,
      shippingAddress: typeof row.shipping_address === 'string'
        ? JSON.parse(row.shipping_address)
        : row.shipping_address,
      businessType: row.business_type,
      warehouseId: row.warehouse_id,
      logisticsChannel: row.logistics_channel,
      trackingNumber: row.tracking_number,
      estimatedDelivery: row.estimated_delivery,
      exceptionReason: row.exception_reason,
      reviewNote: row.review_note,
      traceId: row.trace_id,
      createdAt: row.created_at,
      updatedAt: row.updated_at
    };
  }

  private static mapToItem(row: any): OrderItem {
    return {
      id: row.id,
      orderId: row.order_id,
      tenantId: row.tenant_id,
      skuId: row.sku_id,
      productId: row.product_id,
      productName: row.product_name,
      quantity: row.quantity,
      unitPrice: Number(row.unit_price),
      totalPrice: Number(row.total_price),
      warehouseId: row.warehouse_id,
      allocatedQuantity: row.allocated_quantity || 0,
      createdAt: row.created_at,
      updatedAt: row.updated_at
    };
  }

  private static mapToOrderWithItems(order: any, items: any[]): OrderWithItems {
    return {
      ...this.mapToOrder(order),
      items: items.map(i => this.mapToItem(i))
    };
  }

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

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