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

export interface UnifiedOrder {
  id: string;
  tenantId: string;
  platformOrderId: string;
  platform: string;
  customerId: string;
  items: OrderItem[];
  totalAmount: number;
  currency: string;
  status: 'pending' | 'confirmed' | 'processing' | 'shipped' | 'delivered' | 'cancelled';
  shippingAddress: Record<string, any>;
  fulfillmentStatus: 'unfulfilled' | 'partial' | 'fulfilled';
  createdAt: Date;
  updatedAt: Date;
}

export interface OrderItem {
  productId: string;
  skuId: string;
  quantity: number;
  unitPrice: number;
}

export interface FulfillmentRoute {
  orderId: string;
  warehouseId: string;
  priority: number;
  estimatedShipDate: Date;
  estimatedDeliveryDate: Date;
  cost: number;
}

export interface FulfillmentStatus {
  orderId: string;
  status: 'pending' | 'picked' | 'packed' | 'shipped' | 'delivered';
  trackingNumber?: string;
  carrier?: string;
  events: FulfillmentEvent[];
}

export interface FulfillmentEvent {
  timestamp: Date;
  status: string;
  location?: string;
  description: string;
}

export class UnifiedFulfillmentService {
  private static ORDER_TABLE = 'cf_unified_order';
  private static ROUTE_TABLE = 'cf_fulfillment_route';
  private static STATUS_TABLE = 'cf_fulfillment_status';

  static async initTables(): Promise<void> {
    await this.initOrderTable();
    await this.initRouteTable();
    await this.initStatusTable();
  }

  private static async initOrderTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.ORDER_TABLE);
    if (!hasTable) {
      logger.info(`[UnifiedFulfillment] Creating ${this.ORDER_TABLE} table...`);
      await db.schema.createTable(this.ORDER_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 36).notNullable().index();
        table.string('platform_order_id', 128).notNullable();
        table.string('platform', 32).notNullable();
        table.string('customer_id', 36).notNullable().index();
        table.json('items');
        table.decimal('total_amount', 10, 2).notNullable();
        table.string('currency', 3).defaultTo('USD');
        table.enum('status', ['pending', 'confirmed', 'processing', 'shipped', 'delivered', 'cancelled']).defaultTo('pending');
        table.json('shipping_address');
        table.enum('fulfillment_status', ['unfulfilled', 'partial', 'fulfilled']).defaultTo('unfulfilled');
        table.datetime('created_at').notNullable().defaultTo(db.fn.now());
        table.datetime('updated_at').notNullable().defaultTo(db.fn.now());
        table.unique(['tenant_id', 'platform_order_id'], 'uk_tenant_platform_order');
        table.index(['tenant_id', 'status'], 'idx_tenant_status');
      });
      logger.info(`[UnifiedFulfillment] Table ${this.ORDER_TABLE} created`);
    }
  }

  private static async initRouteTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.ROUTE_TABLE);
    if (!hasTable) {
      logger.info(`[UnifiedFulfillment] Creating ${this.ROUTE_TABLE} table...`);
      await db.schema.createTable(this.ROUTE_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 36).notNullable().index();
        table.string('order_id', 36).notNullable().index();
        table.string('warehouse_id', 36).notNullable();
        table.integer('priority').defaultTo(0);
        table.datetime('estimated_ship_date');
        table.datetime('estimated_delivery_date');
        table.decimal('cost', 10, 2);
        table.datetime('created_at').notNullable().defaultTo(db.fn.now());
      });
      logger.info(`[UnifiedFulfillment] Table ${this.ROUTE_TABLE} created`);
    }
  }

  private static async initStatusTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.STATUS_TABLE);
    if (!hasTable) {
      logger.info(`[UnifiedFulfillment] Creating ${this.STATUS_TABLE} table...`);
      await db.schema.createTable(this.STATUS_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 36).notNullable().index();
        table.string('order_id', 36).notNullable().index();
        table.enum('status', ['pending', 'picked', 'packed', 'shipped', 'delivered']).defaultTo('pending');
        table.string('tracking_number', 64);
        table.string('carrier', 64);
        table.json('events');
        table.datetime('created_at').notNullable().defaultTo(db.fn.now());
        table.datetime('updated_at').notNullable().defaultTo(db.fn.now());
      });
      logger.info(`[UnifiedFulfillment] Table ${this.STATUS_TABLE} created`);
    }
  }

  static async unifyOrders(
    tenantId: string,
    platformOrders: Array<{
      platform: string;
      orderId: string;
      data: Record<string, any>;
    }>
  ): Promise<UnifiedOrder[]> {
    const unified: UnifiedOrder[] = [];

    for (const platformOrder of platformOrders) {
      const id = this.generateId();
      const order: UnifiedOrder = {
        id,
        tenantId,
        platformOrderId: platformOrder.orderId,
        platform: platformOrder.platform,
        customerId: platformOrder.data.customerId || `cust_${Date.now()}`,
        items: platformOrder.data.items || [],
        totalAmount: platformOrder.data.totalAmount || 0,
        currency: platformOrder.data.currency || 'USD',
        status: 'pending',
        shippingAddress: platformOrder.data.shippingAddress || {},
        fulfillmentStatus: 'unfulfilled',
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      await db(this.ORDER_TABLE).insert({
        id: order.id,
        tenant_id: order.tenantId,
        platform_order_id: order.platformOrderId,
        platform: order.platform,
        customer_id: order.customerId,
        items: JSON.stringify(order.items),
        total_amount: order.totalAmount,
        currency: order.currency,
        status: order.status,
        shipping_address: JSON.stringify(order.shippingAddress),
        fulfillment_status: order.fulfillmentStatus,
        created_at: order.createdAt,
        updated_at: order.updatedAt,
      });

      unified.push(order);
    }

    logger.info(`[UnifiedFulfillment] Unified ${unified.length} orders from ${platformOrders.length} platforms`);
    return unified;
  }

  static async routeOrder(
    tenantId: string,
    orderId: string,
    inventoryData: Array<{ warehouseId: string; available: number }>
  ): Promise<FulfillmentRoute> {
    const order = await db(this.ORDER_TABLE)
      .where({ tenant_id: tenantId, id: orderId })
      .first();

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

    const items: OrderItem[] = JSON.parse(order.items);
    const totalQuantity = items.reduce((sum, item) => sum + item.quantity, 0);

    let bestWarehouse = inventoryData[0]?.warehouseId || 'default_warehouse';
    let maxAvailable = 0;

    for (const inv of inventoryData) {
      if (inv.available >= totalQuantity && inv.available > maxAvailable) {
        maxAvailable = inv.available;
        bestWarehouse = inv.warehouseId;
      }
    }

    const route: FulfillmentRoute = {
      orderId,
      warehouseId: bestWarehouse,
      priority: 0,
      estimatedShipDate: new Date(Date.now() + 24 * 60 * 60 * 1000),
      estimatedDeliveryDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
      cost: 5.99,
    };

    await db(this.ROUTE_TABLE).insert({
      id: this.generateId(),
      tenant_id: tenantId,
      order_id: orderId,
      warehouse_id: route.warehouseId,
      priority: route.priority,
      estimated_ship_date: route.estimatedShipDate,
      estimated_delivery_date: route.estimatedDeliveryDate,
      cost: route.cost,
      created_at: new Date(),
    });

    DomainEventBus.publish('order.routed', {
      orderId,
      warehouseId: bestWarehouse,
      tenantId,
      timestamp: new Date(),
    });

    logger.info(`[UnifiedFulfillment] Routed order ${orderId} to warehouse ${bestWarehouse}`);
    return route;
  }

  static async manageFulfillment(
    tenantId: string,
    orderId: string,
    action: 'pick' | 'pack' | 'ship' | 'deliver',
    params: Record<string, any>
  ): Promise<FulfillmentStatus> {
    let status = await db(this.STATUS_TABLE)
      .where({ tenant_id: tenantId, order_id: orderId })
      .first();

    const statusMap: Record<string, FulfillmentStatus['status']> = {
      pick: 'picked',
      pack: 'packed',
      ship: 'shipped',
      deliver: 'delivered',
    };

    const newStatus = statusMap[action];
    const event: FulfillmentEvent = {
      timestamp: new Date(),
      status: newStatus,
      location: params.location,
      description: `Order ${action}ed`,
    };

    if (status) {
      const events: FulfillmentEvent[] = status.events ? JSON.parse(status.events) : [];
      events.push(event);

      await db(this.STATUS_TABLE)
        .where({ id: status.id })
        .update({
          status: newStatus,
          tracking_number: params.trackingNumber || status.tracking_number,
          carrier: params.carrier || status.carrier,
          events: JSON.stringify(events),
          updated_at: new Date(),
        });
    } else {
      status = {
        id: this.generateId(),
        tenant_id: tenantId,
        order_id: orderId,
        status: newStatus,
        tracking_number: params.trackingNumber,
        carrier: params.carrier,
        events: JSON.stringify([event]),
        created_at: new Date(),
        updated_at: new Date(),
      } as any;

      await db(this.STATUS_TABLE).insert(status);
    }

    await db(this.ORDER_TABLE)
      .where({ tenant_id: tenantId, id: orderId })
      .update({
        status: newStatus === 'delivered' ? 'delivered' : 'processing',
        fulfillment_status: newStatus === 'delivered' ? 'fulfilled' : 'partial',
        updated_at: new Date(),
      });

    logger.info(`[UnifiedFulfillment] Updated fulfillment status for order ${orderId} to ${newStatus}`);
    return this.rowToStatus(status);
  }

  static async syncStatus(
    tenantId: string,
    orderId: string,
    newStatus: string
  ): Promise<{ success: boolean; message: string }> {
    const order = await db(this.ORDER_TABLE)
      .where({ tenant_id: tenantId, id: orderId })
      .first();

    if (!order) {
      return { success: false, message: 'Order not found' };
    }

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

    DomainEventBus.publish('order.status.synced', {
      orderId,
      newStatus,
      platform: order.platform,
      tenantId,
      timestamp: new Date(),
    });

    logger.info(`[UnifiedFulfillment] Synced order ${orderId} status to ${newStatus}`);
    return { success: true, message: `Status synced to ${newStatus}` };
  }

  private static rowToStatus(row: any): FulfillmentStatus {
    return {
      orderId: row.order_id,
      status: row.status,
      trackingNumber: row.tracking_number,
      carrier: row.carrier,
      events: row.events ? JSON.parse(row.events) : [],
    };
  }

  private static generateId(): string {
    return `ufo_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }
}
