import { logger } from '../../utils/logger';
import { ConnectorBus } from '../../core/connectors/IPlatformConnector';
import db from '../../config/database';

interface InventorySyncTask {
  taskId: string;
  tenantId: string;
  shopId: string;
  platform: string;
  skuId: string;
  externalId: string;
  quantity: number;
  status: 'PENDING' | 'SYNCING' | 'COMPLETED' | 'FAILED';
  retryCount: number;
  error?: string;
  createdAt: Date;
  updatedAt: Date;
}

interface InventoryAlert {
  alertId: string;
  tenantId: string;
  skuId: string;
  platform: string;
  type: 'LOW_STOCK' | 'OUT_OF_STOCK' | 'OVERSTOCK' | 'SYNC_FAILED';
  currentQuantity: number;
  threshold: number;
  message: string;
  status: 'ACTIVE' | 'ACKNOWLEDGED' | 'RESOLVED';
  createdAt: Date;
}

interface PlatformInventoryStatus {
  platform: string;
  shopId: string;
  externalId: string;
  quantity: number;
  lastSyncAt: Date;
  syncStatus: 'SUCCESS' | 'FAILED' | 'PENDING';
}

interface SyncResult {
  taskId: string;
  platform: string;
  success: boolean;
  syncedQuantity: number;
  error?: string;
}

export class InventorySyncAdapter {
  private static MAX_RETRY_COUNT = 3;
  private static SYNC_BATCH_SIZE = 50;

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

  static async syncToAllPlatforms(params: {
    tenantId: string;
    skuId: string;
    quantity: number;
    platforms?: string[];
  }): Promise<SyncResult[]> {
    const { tenantId, skuId, quantity, platforms } = params;
    
    logger.info(`[InventorySyncAdapter] Syncing inventory to all platforms`, {
      tenantId,
      skuId,
      quantity
    });

    const listings = await db('cf_listing')
      .where({
        tenant_id: tenantId,
        sku_id: skuId,
        status: 'ACTIVE'
      })
      .whereIn('platform', platforms || ['Shopify', 'TIKTOK', 'Shopee', 'Temu']);

    const results: SyncResult[] = [];

    for (const listing of listings) {
      const result = await this.syncToPlatform({
        tenantId,
        shopId: listing.shop_id,
        platform: listing.platform,
        externalId: listing.external_id,
        quantity
      });

      results.push(result);
    }

    return results;
  }

  static async syncToPlatform(params: {
    tenantId: string;
    shopId: string;
    platform: string;
    externalId: string;
    quantity: number;
  }): Promise<SyncResult> {
    const { tenantId, shopId, platform, externalId, quantity } = params;
    const taskId = this.generateId('sync_task');

    logger.info(`[InventorySyncAdapter] Syncing to platform`, {
      taskId,
      platform,
      externalId,
      quantity
    });

    try {
      const connector = ConnectorBus.getConnector(platform);
      const success = await connector.syncInventory(shopId, externalId, quantity);

      if (success) {
        await this.updateSyncStatus({
          taskId,
          tenantId,
          platform,
          externalId,
          quantity,
          status: 'COMPLETED'
        });

        return {
          taskId,
          platform,
          success: true,
          syncedQuantity: quantity
        };
      } else {
        throw new Error('Sync returned false');
      }
    } catch (error: any) {
      logger.error(`[InventorySyncAdapter] Sync failed`, {
        taskId,
        platform,
        error: error.message
      });

      await this.updateSyncStatus({
        taskId,
        tenantId,
        platform,
        externalId,
        quantity,
        status: 'FAILED',
        error: error.message
      });

      return {
        taskId,
        platform,
        success: false,
        syncedQuantity: 0,
        error: error.message
      };
    }
  }

  static async batchSync(params: {
    tenantId: string;
    items: Array<{
      skuId: string;
      quantity: number;
      platforms?: string[];
    }>;
  }): Promise<{
    totalItems: number;
    successCount: number;
    failedCount: number;
    results: SyncResult[];
  }> {
    const { tenantId, items } = params;
    
    logger.info(`[InventorySyncAdapter] Starting batch sync`, {
      tenantId,
      itemCount: items.length
    });

    const allResults: SyncResult[] = [];
    let successCount = 0;
    let failedCount = 0;

    for (const item of items) {
      const results = await this.syncToAllPlatforms({
        tenantId,
        skuId: item.skuId,
        quantity: item.quantity,
        platforms: item.platforms
      });

      for (const result of results) {
        allResults.push(result);
        if (result.success) {
          successCount++;
        } else {
          failedCount++;
        }
      }
    }

    return {
      totalItems: items.length,
      successCount,
      failedCount,
      results: allResults
    };
  }

  static async checkInventoryAlerts(params: {
    tenantId: string;
    thresholds?: {
      lowStock: number;
      outOfStock: number;
      overstock: number;
    };
  }): Promise<InventoryAlert[]> {
    const { tenantId, thresholds } = params;
    const config = {
      lowStock: thresholds?.lowStock || 10,
      outOfStock: thresholds?.outOfStock || 0,
      overstock: thresholds?.overstock || 1000
    };

    logger.info(`[InventorySyncAdapter] Checking inventory alerts`, {
      tenantId,
      thresholds: config
    });

    const alerts: InventoryAlert[] = [];

    const inventory = await db('cf_inventory')
      .where({ tenant_id: tenantId })
      .where('available_qty', '<=', config.lowStock);

    for (const item of inventory) {
      const alertType = item.available_qty === 0 ? 'OUT_OF_STOCK' : 'LOW_STOCK';
      
      alerts.push({
        alertId: this.generateId('alert'),
        tenantId,
        skuId: item.sku_id,
        platform: 'ALL',
        type: alertType,
        currentQuantity: item.available_qty,
        threshold: alertType === 'OUT_OF_STOCK' ? config.outOfStock : config.lowStock,
        message: alertType === 'OUT_OF_STOCK' 
          ? `SKU ${item.sku_id} is out of stock`
          : `SKU ${item.sku_id} is low on stock (${item.available_qty} remaining)`,
        status: 'ACTIVE',
        createdAt: new Date()
      });

      await this.createAlertRecord({
        tenantId,
        skuId: item.sku_id,
        type: alertType,
        quantity: item.available_qty,
        threshold: alertType === 'OUT_OF_STOCK' ? config.outOfStock : config.lowStock
      });
    }

    return alerts;
  }

  static async getPlatformInventoryStatus(params: {
    tenantId: string;
    skuId: string;
  }): Promise<PlatformInventoryStatus[]> {
    const { tenantId, skuId } = params;

    logger.info(`[InventorySyncAdapter] Getting platform inventory status`, {
      tenantId,
      skuId
    });

    const listings = await db('cf_listing')
      .where({
        tenant_id: tenantId,
        sku_id: skuId
      });

    const statuses: PlatformInventoryStatus[] = [];

    for (const listing of listings) {
      const syncLog = await db('cf_inventory_sync_log')
        .where({
          tenant_id: tenantId,
          sku_id: skuId,
          platform: listing.platform
        })
        .orderBy('created_at', 'desc')
        .first();

      statuses.push({
        platform: listing.platform,
        shopId: listing.shop_id,
        externalId: listing.external_id,
        quantity: syncLog?.quantity || 0,
        lastSyncAt: syncLog?.created_at || new Date(0),
        syncStatus: syncLog?.status || 'PENDING'
      });
    }

    return statuses;
  }

  static async reconcileInventory(params: {
    tenantId: string;
    skuId: string;
    expectedQuantity: number;
  }): Promise<{
    matched: boolean;
    platformStatuses: PlatformInventoryStatus[];
    discrepancies: Array<{
      platform: string;
      expected: number;
      actual: number;
      difference: number;
    }>;
  }> {
    const { tenantId, skuId, expectedQuantity } = params;

    logger.info(`[InventorySyncAdapter] Reconciling inventory`, {
      tenantId,
      skuId,
      expectedQuantity
    });

    const platformStatuses = await this.getPlatformInventoryStatus({ tenantId, skuId });
    
    const discrepancies: Array<{
      platform: string;
      expected: number;
      actual: number;
      difference: number;
    }> = [];

    for (const status of platformStatuses) {
      if (status.quantity !== expectedQuantity) {
        discrepancies.push({
          platform: status.platform,
          expected: expectedQuantity,
          actual: status.quantity,
          difference: status.quantity - expectedQuantity
        });
      }
    }

    return {
      matched: discrepancies.length === 0,
      platformStatuses,
      discrepancies
    };
  }

  static async autoReplenish(params: {
    tenantId: string;
    skuId: string;
    targetQuantity: number;
    platforms?: string[];
  }): Promise<SyncResult[]> {
    const { tenantId, skuId, targetQuantity, platforms } = params;

    logger.info(`[InventorySyncAdapter] Auto replenishing inventory`, {
      tenantId,
      skuId,
      targetQuantity
    });

    const inventory = await db('cf_inventory')
      .where({
        tenant_id: tenantId,
        sku_id: skuId
      })
      .first();

    if (!inventory) {
      throw new Error('Inventory record not found');
    }

    return this.syncToAllPlatforms({
      tenantId,
      skuId,
      quantity: targetQuantity,
      platforms
    });
  }

  private static async updateSyncStatus(params: {
    taskId: string;
    tenantId: string;
    platform: string;
    externalId: string;
    quantity: number;
    status: 'PENDING' | 'SYNCING' | 'COMPLETED' | 'FAILED';
    error?: string;
  }): Promise<void> {
    const { taskId, tenantId, platform, externalId, quantity, status, error } = params;

    try {
      await db('cf_inventory_sync_log').insert({
        id: taskId,
        tenant_id: tenantId,
        platform,
        external_id: externalId,
        quantity,
        status,
        error_message: error,
        created_at: new Date(),
        updated_at: new Date()
      });
    } catch (dbError: any) {
      logger.error('[InventorySyncAdapter] Failed to update sync status', {
        taskId,
        error: dbError.message
      });
    }
  }

  private static async createAlertRecord(params: {
    tenantId: string;
    skuId: string;
    type: 'LOW_STOCK' | 'OUT_OF_STOCK' | 'OVERSTOCK' | 'SYNC_FAILED';
    quantity: number;
    threshold: number;
  }): Promise<void> {
    const { tenantId, skuId, type, quantity, threshold } = params;

    try {
      const existingAlert = await db('cf_inventory_alert')
        .where({
          tenant_id: tenantId,
          sku_id: skuId,
          type,
          status: 'ACTIVE'
        })
        .first();

      if (!existingAlert) {
        await db('cf_inventory_alert').insert({
          id: this.generateId('alert'),
          tenant_id: tenantId,
          sku_id: skuId,
          type,
          current_quantity: quantity,
          threshold,
          status: 'ACTIVE',
          created_at: new Date(),
          updated_at: new Date()
        });
      }
    } catch (dbError: any) {
      logger.error('[InventorySyncAdapter] Failed to create alert record', {
        tenantId,
        skuId,
        error: dbError.message
      });
    }
  }

  static async getSyncHistory(params: {
    tenantId: string;
    skuId?: string;
    platform?: string;
    limit?: number;
  }): Promise<InventorySyncTask[]> {
    const { tenantId, skuId, platform, limit = 50 } = params;

    let query = db('cf_inventory_sync_log')
      .where({ tenant_id: tenantId })
      .orderBy('created_at', 'desc')
      .limit(limit);

    if (skuId) {
      query = query.where({ sku_id: skuId });
    }

    if (platform) {
      query = query.where({ platform });
    }

    return query;
  }

  static async retryFailedSyncs(params: {
    tenantId: string;
    maxAge?: number;
  }): Promise<{
    retriedCount: number;
    successCount: number;
    failedCount: number;
  }> {
    const { tenantId, maxAge = 86400000 } = params;
    const cutoffTime = new Date(Date.now() - maxAge);

    logger.info(`[InventorySyncAdapter] Retrying failed syncs`, {
      tenantId,
      cutoffTime
    });

    const failedSyncs = await db('cf_inventory_sync_log')
      .where({
        tenant_id: tenantId,
        status: 'FAILED'
      })
      .where('created_at', '>=', cutoffTime)
      .where('retry_count', '<', this.MAX_RETRY_COUNT);

    let retriedCount = 0;
    let successCount = 0;
    let failedCount = 0;

    for (const sync of failedSyncs) {
      retriedCount++;

      try {
        const result = await this.syncToPlatform({
          tenantId,
          shopId: sync.shop_id,
          platform: sync.platform,
          externalId: sync.external_id,
          quantity: sync.quantity
        });

        if (result.success) {
          successCount++;
        } else {
          failedCount++;
        }
      } catch (error: any) {
        failedCount++;
        logger.error('[InventorySyncAdapter] Retry failed', {
          syncId: sync.id,
          error: error.message
        });
      }
    }

    return { retriedCount, successCount, failedCount };
  }
}

export default InventorySyncAdapter;
