/**
 * [BATCH-SVC-001] 批量操作服务
 * 本服务提供批量操作的统一入口
 * AI注意: 所有批量操作必须通过此服务
 */

import db from '../../config/database';
import { logger } from '../../utils/logger';
import {
  BatchOperationType,
  BatchOperationStatus,
  BatchOperationRecord,
  BatchOperationItem,
  BatchEditPriceParams,
  BatchEditInventoryParams,
  BatchEditTitleParams,
  BatchListingParams,
  BatchDelistParams,
  BatchOperationQuery,
  BatchOperationSummary,
  OPERATION_TYPE_LABELS,
  AI_CAN_AUTO_EXECUTE,
} from '../../shared/types/batch-operation';
import { DomainEventBus } from '../../core/runtime/DomainEventBus';
import { v4 as uuidv4 } from 'uuid';

export class BatchOperationService {
  private static readonly TABLE_NAME = 'cf_batch_operation';
  private static readonly TABLE_NAME_ITEM = 'cf_batch_operation_item';

  static async initTables(): Promise<void> {
    logger.info('[BatchOperationService] Tables already initialized via DatabaseSchema');
  }

  static async createBatchOperation(
    params: {
      tenantId: string;
      shopId: string;
      operatorId: string;
      operatorName: string;
      operationType: BatchOperationType;
      targetType: 'PRODUCT' | 'AD_CAMPAIGN' | 'AD_CREATIVE';
      targetIds: string[];
      operationParams?: BatchEditPriceParams | BatchEditInventoryParams | BatchListingParams | BatchDelistParams;
    }
  ): Promise<BatchOperationRecord> {
    const id = uuidv4();
    const now = new Date();

    await db(this.TABLE_NAME).insert({
      id,
      tenant_id: params.tenantId,
      shop_id: params.shopId,
      operator_id: params.operatorId,
      operator_name: params.operatorName,
      operation_type: params.operationType,
      status: BatchOperationStatus.PENDING,
      target_type: params.targetType,
      target_ids: JSON.stringify(params.targetIds),
      params: params.operationParams ? JSON.stringify(params.operationParams) : null,
      total_count: params.targetIds.length,
      success_count: 0,
      failed_count: 0,
      ai_auto_executed: false,
      created_by: params.operatorId,
      updated_by: params.operatorId,
      created_at: now,
      updated_at: now,
    });

    return {
      id,
      tenantId: params.tenantId,
      shopId: params.shopId,
      operatorId: params.operatorId,
      operatorName: params.operatorName,
      operationType: params.operationType,
      status: BatchOperationStatus.PENDING,
      targetType: params.targetType,
      targetIds: params.targetIds,
      totalCount: params.targetIds.length,
      successCount: 0,
      failedCount: 0,
      beforeValues: [],
      afterValues: [],
      params: params.operationParams,
      aiAutoExecuted: false,
      createdAt: now,
      updatedAt: now,
    };
  }

  static async updateBatchOperation(
    id: string,
    updates: Partial<{
      status: BatchOperationStatus;
      successCount: number;
      failedCount: number;
      beforeValues: Record<string, any>[];
      afterValues: Record<string, any>[];
      aiConfidence: number;
      aiReason: string;
      riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
      subscriptionLevel: string;
      aiAutoExecuted: boolean;
      confirmedAt: Date;
      executedAt: Date;
      completedAt: Date;
    }>
  ): Promise<void> {
    const updateData: any = { updated_at: new Date() };

    if (updates.status) updateData.status = updates.status;
    if (updates.successCount !== undefined) updateData.success_count = updates.successCount;
    if (updates.failedCount !== undefined) updateData.failed_count = updates.failedCount;
    if (updates.beforeValues) updateData.before_values = JSON.stringify(updates.beforeValues);
    if (updates.afterValues) updateData.after_values = JSON.stringify(updates.afterValues);
    if (updates.aiConfidence !== undefined) updateData.ai_confidence = updates.aiConfidence;
    if (updates.aiReason) updateData.ai_reason = updates.aiReason;
    if (updates.riskLevel) updateData.risk_level = updates.riskLevel;
    if (updates.subscriptionLevel) updateData.subscription_level = updates.subscriptionLevel;
    if (updates.aiAutoExecuted !== undefined) updateData.ai_auto_executed = updates.aiAutoExecuted;
    if (updates.confirmedAt) updateData.confirmed_at = updates.confirmedAt;
    if (updates.executedAt) updateData.executed_at = updates.executedAt;
    if (updates.completedAt) updateData.completed_at = updates.completedAt;

    await db(this.TABLE_NAME).where({ id }).update(updateData);
  }

  static async getBatchOperation(id: string): Promise<BatchOperationRecord | null> {
    const record = await db(this.TABLE_NAME).where({ id }).first();
    if (!record) return null;
    return this.mapToRecord(record);
  }

  static async getBatchOperationHistory(
    query: BatchOperationQuery
  ): Promise<{ records: BatchOperationRecord[]; total: number }> {
    let baseQuery = db(this.TABLE_NAME).where('tenant_id', query.tenantId);

    if (query.shopId) {
      baseQuery = baseQuery.where('shop_id', query.shopId);
    }
    if (query.operationType) {
      baseQuery = baseQuery.where('operation_type', query.operationType);
    }
    if (query.status) {
      baseQuery = baseQuery.where('status', query.status);
    }
    if (query.operatorId) {
      baseQuery = baseQuery.where('operator_id', query.operatorId);
    }
    if (query.startDate) {
      baseQuery = baseQuery.where('created_at', '>=', query.startDate);
    }
    if (query.endDate) {
      baseQuery = baseQuery.where('created_at', '<=', query.endDate);
    }

    const total = await baseQuery.clone().count('id as count').first();
    const page = query.page || 1;
    const pageSize = query.pageSize || 20;
    const offset = (page - 1) * pageSize;

    const records = await baseQuery
      .orderBy('created_at', 'desc')
      .limit(pageSize)
      .offset(offset);

    return {
      records: records.map((r: any) => this.mapToRecord(r)),
      total: Number(total?.count || 0),
    };
  }

  static async batchEditPrice(params: BatchEditPriceParams): Promise<{
    success: boolean;
    results: Array<{ productId: string; success: boolean; beforePrice?: number; afterPrice?: number; error?: string }>;
  }> {
    const results: Array<{
      productId: string;
      success: boolean;
      beforePrice?: number;
      afterPrice?: number;
      error?: string;
    }> = [];

    for (const productId of params.productIds) {
      try {
        const product = await db('cf_product').where({ product_id: productId }).first();
        if (!product) {
          results.push({ productId, success: false, error: 'Product not found' });
          continue;
        }

        const beforePrice = product.price;
        let afterPrice: number;

        if (params.priceChange.type === 'ABSOLUTE') {
          afterPrice = params.priceChange.value;
        } else {
          afterPrice = beforePrice * (1 + params.priceChange.value / 100);
        }

        await db('cf_product').where({ product_id: productId }).update({
          price: afterPrice,
          updated_by: 'BATCH_OPERATION_AI',
          updated_at: new Date(),
        });

        results.push({
          productId,
          success: true,
          beforePrice,
          afterPrice,
        });
      } catch (error) {
        results.push({
          productId,
          success: false,
          error: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    }

    return {
      success: results.every((r) => r.success),
      results,
    };
  }

  static async batchEditInventory(params: BatchEditInventoryParams): Promise<{
    success: boolean;
    results: Array<{
      productId: string;
      success: boolean;
      beforeInventory?: number;
      afterInventory?: number;
      error?: string;
    }>;
  }> {
    const results: Array<{
      productId: string;
      success: boolean;
      beforeInventory?: number;
      afterInventory?: number;
      error?: string;
    }> = [];

    for (const productId of params.productIds) {
      try {
        const product = await db('cf_product').where({ product_id: productId }).first();
        if (!product) {
          results.push({ productId, success: false, error: 'Product not found' });
          continue;
        }

        const beforeInventory = product.stock || 0;
        let afterInventory: number;

        if (params.inventoryChange.type === 'ABSOLUTE') {
          afterInventory = params.inventoryChange.value;
        } else {
          afterInventory = Math.round(beforeInventory * (1 + params.inventoryChange.value / 100));
        }

        await db('cf_product').where({ product_id: productId }).update({
          stock: afterInventory,
          updated_by: 'BATCH_OPERATION_AI',
          updated_at: new Date(),
        });

        results.push({
          productId,
          success: true,
          beforeInventory,
          afterInventory,
        });
      } catch (error) {
        results.push({
          productId,
          success: false,
          error: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    }

    return {
      success: results.every((r) => r.success),
      results,
    };
  }

  static async batchListing(params: BatchListingParams): Promise<{
    success: boolean;
    results: Array<{
      productId: string;
      platform: string;
      success: boolean;
      platformProductId?: string;
      error?: string;
    }>;
  }> {
    const results: Array<{
      productId: string;
      platform: string;
      success: boolean;
      platformProductId?: string;
      error?: string;
    }> = [];

    for (const productId of params.productIds) {
      for (const platform of params.targetPlatforms) {
        try {
          const listingResult = await this.listProductToPlatform(productId, platform, params.targetShops, params.listingData);
          results.push({
            productId,
            platform,
            success: listingResult.success,
            platformProductId: listingResult.platformProductId,
            error: listingResult.error,
          });
        } catch (error) {
          results.push({
            productId,
            platform,
            success: false,
            error: error instanceof Error ? error.message : 'Unknown error',
          });
        }
      }
    }

    return {
      success: results.every((r) => r.success),
      results,
    };
  }

  private static async listProductToPlatform(
    productId: string,
    platform: string,
    targetShops: string[],
    listingData?: { price?: number; inventory?: number; category?: string }
  ): Promise<{ success: boolean; platformProductId?: string; error?: string }> {
    logger.info(`[BatchOperationService] Listing product ${productId} to ${platform}`);

    DomainEventBus.publish('product.listing.request', {
      productId,
      platform,
      shops: targetShops,
      listingData,
    });

    return {
      success: true,
      platformProductId: `LISTED-${platform}-${productId}-${Date.now()}`,
    };
  }

  static async batchDelist(params: BatchDelistParams): Promise<{
    success: boolean;
    results: Array<{ productId: string; success: boolean; error?: string }>;
  }> {
    const results: Array<{ productId: string; success: boolean; error?: string }> = [];

    for (const productId of params.productIds) {
      try {
        logger.info(`[BatchOperationService] Delisting product ${productId}`);

        DomainEventBus.publish('product.delisting.request', {
          productId,
          reason: params.reason,
        });

        results.push({ productId, success: true });
      } catch (error) {
        results.push({
          productId,
          success: false,
          error: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    }

    return {
      success: results.every((r) => r.success),
      results,
    };
  }

  static async executeBatchOperation(
    batchId: string,
    params?: BatchEditPriceParams | BatchEditInventoryParams | BatchListingParams | BatchDelistParams
  ): Promise<{ success: boolean; processedCount: number; errorCount: number }> {
    const batch = await this.getBatchOperation(batchId);
    if (!batch) {
      throw new Error('Batch operation not found');
    }

    const operationParams = params || batch.params;
    if (!operationParams) {
      throw new Error('Operation parameters not found');
    }

    await this.updateBatchOperation(batchId, {
      status: BatchOperationStatus.EXECUTING,
      executedAt: new Date(),
    });

    let result: any;
    let operationType = batch.operationType;

    switch (operationType) {
      case BatchOperationType.BATCH_EDIT_PRICE:
        result = await this.batchEditPrice(operationParams as BatchEditPriceParams);
        break;
      case BatchOperationType.BATCH_EDIT_INVENTORY:
        result = await this.batchEditInventory(operationParams as BatchEditInventoryParams);
        break;
      case BatchOperationType.BATCH_LISTING:
        result = await this.batchListing(operationParams as BatchListingParams);
        break;
      case BatchOperationType.BATCH_DELIST:
        result = await this.batchDelist(operationParams as BatchDelistParams);
        break;
      default:
        throw new Error(`Unsupported operation type: ${operationType}`);
    }

    const successCount = result.results.filter((r: any) => r.success).length;
    const errorCount = result.results.filter((r: any) => !r.success).length;

    await this.updateBatchOperation(batchId, {
      status: errorCount === 0 ? BatchOperationStatus.COMPLETED : BatchOperationStatus.PARTIAL_SUCCESS,
      successCount,
      failedCount: errorCount,
      afterValues: result.results,
      completedAt: new Date(),
    });

    DomainEventBus.publish('batch.operation.completed', {
      batchId,
      success: result.success,
      successCount,
      errorCount,
    });

    return {
      success: result.success,
      processedCount: successCount,
      errorCount,
    };
  }

  static async getOperationSummary(tenantId: string): Promise<BatchOperationSummary> {
    const records = await db(this.TABLE_NAME)
      .where('tenant_id', tenantId)
      .select('operation_type', 'status', 'ai_auto_executed', 'ai_confidence');

    const operationsByType: Record<string, number> = {};
    const operationsByStatus: Record<string, number> = {};
    let totalAiAutoExecuted = 0;
    let totalConfidence = 0;
    let confidenceCount = 0;

    for (const record of records) {
      operationsByType[record.operation_type] = (operationsByType[record.operation_type] || 0) + 1;
      operationsByStatus[record.status] = (operationsByStatus[record.status] || 0) + 1;
      if (record.ai_auto_executed) totalAiAutoExecuted++;
      if (record.ai_confidence) {
        totalConfidence += Number(record.ai_confidence);
        confidenceCount++;
      }
    }

    return {
      totalOperations: records.length,
      totalItems: records.reduce((sum, r) => sum + Number(r.total_count), 0),
      successRate: records.length > 0
        ? (records.filter((r) => r.status === 'COMPLETED').length / records.length) * 100
        : 0,
      aiAutoExecuteRate: records.length > 0
        ? (totalAiAutoExecuted / records.length) * 100
        : 0,
      averageConfidence: confidenceCount > 0 ? totalConfidence / confidenceCount : 0,
      operationsByType: operationsByType as any,
      operationsByStatus: operationsByStatus as any,
    };
  }

  static canAIExecute(operationType: BatchOperationType): boolean {
    return AI_CAN_AUTO_EXECUTE[operationType] || false;
  }

  private static mapToRecord(row: any): BatchOperationRecord {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      shopId: row.shop_id,
      operatorId: row.operator_id,
      operatorName: row.operator_name,
      operationType: row.operation_type as BatchOperationType,
      status: row.status as BatchOperationStatus,
      targetType: row.target_type,
      targetIds: row.target_ids ? JSON.parse(row.target_ids) : [],
      totalCount: row.total_count,
      successCount: row.success_count,
      failedCount: row.failed_count,
      beforeValues: row.before_values ? JSON.parse(row.before_values) : [],
      afterValues: row.after_values ? JSON.parse(row.after_values) : [],
      params: row.params ? JSON.parse(row.params) : undefined,
      aiConfidence: row.ai_confidence ? Number(row.ai_confidence) : undefined,
      aiReason: row.ai_reason,
      riskLevel: row.risk_level,
      subscriptionLevel: row.subscription_level,
      aiAutoExecuted: row.ai_auto_executed,
      confirmedAt: row.confirmed_at ? new Date(row.confirmed_at) : undefined,
      executedAt: row.executed_at ? new Date(row.executed_at) : undefined,
      completedAt: row.completed_at ? new Date(row.completed_at) : undefined,
      createdAt: new Date(row.created_at),
      updatedAt: new Date(row.updated_at),
    };
  }
}
