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

export interface AdOperationPayload {
  tenantId: string;
  shopId: string;
  taskId: string;
  traceId: string;
  businessType: 'TOC' | 'TOB';
  platformAccount: string;
  platform: 'AMAZON' | 'EBAY' | 'SHOPEE' | 'ALIEXPRESS' | 'TIKTOK' | 'TEMU';
  operations: AdOperationItem[];
}

export interface AdOperationItem {
  operation: 'CREATE' | 'UPDATE' | 'PAUSE' | 'RESUME' | 'DELETE';
  adId?: string;
  adName?: string;
  campaignId?: string;
  adGroupId?: string;
  productId?: string;
  bidAmount?: number;
  bidStrategy?: 'MANUAL' | 'AUTO' | 'CPI' | 'CPC' | 'CPM';
  dailyBudget?: number;
  startDate?: Date;
  endDate?: Date;
  targeting?: AdTargeting;
  status?: 'ACTIVE' | 'PAUSED' | 'ARCHIVED';
}

export interface AdTargeting {
  keywords?: string[];
  products?: string[];
  categories?: string[];
  audiences?: string[];
  ageRanges?: string[];
  genders?: ('MALE' | 'FEMALE')[];
  locations?: string[];
}

export interface AdOperationResult {
  success: boolean;
  processedCount: number;
  failedCount: number;
  results: SingleAdOperationResult[];
  batchId: string;
  processedAt: Date;
}

export interface SingleAdOperationResult {
  operation: string;
  adId?: string;
  success: boolean;
  platformAdId?: string;
  errorMessage?: string;
  details?: Record<string, any>;
}

export interface AdDataSyncPayload {
  tenantId: string;
  shopId: string;
  taskId: string;
  traceId: string;
  businessType: 'TOC' | 'TOB';
  platformAccount: string;
  platform: 'AMAZON' | 'EBAY' | 'SHOPEE' | 'ALIEXPRESS' | 'TIKTOK' | 'TEMU';
  adIds?: string[];
  dateRange?: {
    startDate: Date;
    endDate: Date;
  };
}

export interface AdDataSyncResult {
  success: boolean;
  syncedCount: number;
  ads: AdDataRecord[];
  errors: string[];
  syncedAt: Date;
}

export interface AdDataRecord {
  adId: string;
  platformAdId: string;
  adName: string;
  campaignId: string;
  campaignName: string;
  adGroupId: string;
  adGroupName: string;
  productId?: string;
  productName?: string;
  status: 'ACTIVE' | 'PAUSED' | 'ARCHIVED' | 'ENDED';
  impressions: number;
  clicks: number;
  spend: number;
  sales: number;
  orders: number;
  conversions: number;
  ctr: number;
  cpc: number;
  roas: number;
  acos: number;
  date: Date;
}

export interface AdPerformanceMetrics {
  totalImpressions: number;
  totalClicks: number;
  totalSpend: number;
  totalSales: number;
  totalOrders: number;
  averageCTR: number;
  averageCPC: number;
  averageROAS: number;
  averageACOS: number;
}

export default class AdOperationPlugin implements BizPlugin {
  name = 'AdOperationPlugin';
  version = '1.0.0';
  description = 'Advertising operation plugin for ad creation, modification, and data synchronization';

  async onLoad(): Promise<void> {
    logger.info(`[${this.name}] Ad Operation Plugin initialized`);
    await this.initializeTables();
  }

  async onUnload(): Promise<void> {
    logger.info(`[${this.name}] Ad Operation Plugin unloaded`);
  }

  private async initializeTables(): Promise<void> {
    const hasAdTable = await db.schema.hasTable('cf_advertisement');
    if (!hasAdTable) {
      logger.info(`[${this.name}] Creating advertisement table...`);
      await db.schema.createTable('cf_advertisement', (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 64).notNullable().index();
        table.string('shop_id', 64).notNullable().index();
        table.string('platform_ad_id', 128);
        table.string('ad_name', 255).notNullable();
        table.string('campaign_id', 128);
        table.string('campaign_name', 255);
        table.string('ad_group_id', 128);
        table.string('ad_group_name', 255);
        table.string('product_id', 64);
        table.string('product_name', 255);
        table.enum('platform', ['AMAZON', 'EBAY', 'SHOPEE', 'ALIEXPRESS', 'TIKTOK', 'TEMU']).notNullable();
        table.enum('status', ['ACTIVE', 'PAUSED', 'ARCHIVED', 'ENDED']).defaultTo('PAUSED');
        table.decimal('bid_amount', 10, 2);
        table.enum('bid_strategy', ['MANUAL', 'AUTO', 'CPI', 'CPC', 'CPM']);
        table.decimal('daily_budget', 10, 2);
        table.json('targeting');
        table.integer('impressions').defaultTo(0);
        table.integer('clicks').defaultTo(0);
        table.decimal('spend', 12, 2).defaultTo(0);
        table.decimal('sales', 12, 2).defaultTo(0);
        table.integer('orders').defaultTo(0);
        table.integer('conversions').defaultTo(0);
        table.timestamp('start_date');
        table.timestamp('end_date');
        table.timestamp('created_at').defaultTo(db.fn.now());
        table.timestamp('updated_at').defaultTo(db.fn.now());
        table.index(['platform'], 'idx_ad_platform');
        table.index(['status'], 'idx_ad_status');
        table.index(['product_id'], 'idx_ad_product');
      });
      logger.info(`[${this.name}] Advertisement table created`);
    }

    const hasMetricsTable = await db.schema.hasTable('cf_ad_metrics');
    if (!hasMetricsTable) {
      await db.schema.createTable('cf_ad_metrics', (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 64).notNullable().index();
        table.string('shop_id', 64).notNullable().index();
        table.string('ad_id', 36).notNullable().index();
        table.date('record_date').notNullable().index();
        table.integer('impressions').defaultTo(0);
        table.integer('clicks').defaultTo(0);
        table.decimal('spend', 12, 2).defaultTo(0);
        table.decimal('sales', 12, 2).defaultTo(0);
        table.integer('orders').defaultTo(0);
        table.integer('conversions').defaultTo(0);
        table.timestamp('created_at').defaultTo(db.fn.now());
        table.unique(['ad_id', 'record_date'], 'uk_ad_metrics');
      });
      logger.info(`[${this.name}] Ad metrics table created`);
    }
  }

  async executeAdOperation(payload: AdOperationPayload): Promise<AdOperationResult> {
    const { tenantId, shopId, traceId, operations } = payload;
    
    logger.info(`[${this.name}] Executing ad operations: tenantId=${tenantId}, count=${operations.length}, traceId=${traceId}`);

    const result: AdOperationResult = {
      success: true,
      processedCount: 0,
      failedCount: 0,
      results: [],
      batchId: this.generateBatchId(),
      processedAt: new Date(),
    };

    for (const operation of operations) {
      try {
        const opResult = await this.processSingleOperation(tenantId, shopId, operation, traceId);
        result.results.push(opResult);
        
        if (opResult.success) {
          result.processedCount++;
        } else {
          result.failedCount++;
        }
      } catch (error: any) {
        result.failedCount++;
        result.results.push({
          operation: operation.operation,
          adId: operation.adId,
          success: false,
          errorMessage: error.message,
        });
      }
    }

    result.success = result.failedCount === 0;
    
    logger.info(`[${this.name}] Ad operations completed: processed=${result.processedCount}, failed=${result.failedCount}, traceId=${traceId}`);
    return result;
  }

  private async processSingleOperation(
    tenantId: string,
    shopId: string,
    operation: AdOperationItem,
    traceId: string
  ): Promise<SingleAdOperationResult> {
    switch (operation.operation) {
      case 'CREATE':
        return await this.createAd(tenantId, shopId, operation, traceId);
      case 'UPDATE':
        return await this.updateAd(tenantId, shopId, operation, traceId);
      case 'PAUSE':
        return await this.pauseAd(tenantId, shopId, operation, traceId);
      case 'RESUME':
        return await this.resumeAd(tenantId, shopId, operation, traceId);
      case 'DELETE':
        return await this.deleteAd(tenantId, shopId, operation, traceId);
      default:
        return {
          operation: operation.operation,
          success: false,
          errorMessage: `Unknown operation: ${operation.operation}`,
        };
    }
  }

  private async createAd(
    tenantId: string,
    shopId: string,
    operation: AdOperationItem,
    traceId: string
  ): Promise<SingleAdOperationResult> {
    const adId = this.generateAdId();
    
    await db('cf_advertisement').insert({
      id: adId,
      tenant_id: tenantId,
      shop_id: shopId,
      ad_name: operation.adName || `Ad_${adId}`,
      campaign_id: operation.campaignId || null,
      ad_group_id: operation.adGroupId || null,
      product_id: operation.productId || null,
      platform: 'AMAZON',
      status: 'PAUSED',
      bid_amount: operation.bidAmount || null,
      bid_strategy: operation.bidStrategy || 'MANUAL',
      daily_budget: operation.dailyBudget || null,
      targeting: operation.targeting ? JSON.stringify(operation.targeting) : null,
      start_date: operation.startDate || null,
      end_date: operation.endDate || null,
    });

    logger.info(`[${this.name}] Ad created: adId=${adId}, traceId=${traceId}`);
    
    return {
      operation: 'CREATE',
      adId,
      success: true,
      platformAdId: `PLATFORM_AD_${adId}`,
    };
  }

  private async updateAd(
    tenantId: string,
    shopId: string,
    operation: AdOperationItem,
    traceId: string
  ): Promise<SingleAdOperationResult> {
    if (!operation.adId) {
      return {
        operation: 'UPDATE',
        success: false,
        errorMessage: 'Ad ID is required for update operation',
      };
    }

    const updateData: Record<string, any> = { updated_at: new Date() };
    
    if (operation.adName) updateData.ad_name = operation.adName;
    if (operation.bidAmount !== undefined) updateData.bid_amount = operation.bidAmount;
    if (operation.bidStrategy) updateData.bid_strategy = operation.bidStrategy;
    if (operation.dailyBudget !== undefined) updateData.daily_budget = operation.dailyBudget;
    if (operation.targeting) updateData.targeting = JSON.stringify(operation.targeting);
    if (operation.status) updateData.status = operation.status;

    await db('cf_advertisement')
      .where({ id: operation.adId, tenant_id: tenantId })
      .update(updateData);

    logger.info(`[${this.name}] Ad updated: adId=${operation.adId}, traceId=${traceId}`);
    
    return {
      operation: 'UPDATE',
      adId: operation.adId,
      success: true,
    };
  }

  private async pauseAd(
    tenantId: string,
    shopId: string,
    operation: AdOperationItem,
    traceId: string
  ): Promise<SingleAdOperationResult> {
    if (!operation.adId) {
      return {
        operation: 'PAUSE',
        success: false,
        errorMessage: 'Ad ID is required for pause operation',
      };
    }

    await db('cf_advertisement')
      .where({ id: operation.adId, tenant_id: tenantId })
      .update({ status: 'PAUSED', updated_at: new Date() });

    logger.info(`[${this.name}] Ad paused: adId=${operation.adId}, traceId=${traceId}`);
    
    return {
      operation: 'PAUSE',
      adId: operation.adId,
      success: true,
    };
  }

  private async resumeAd(
    tenantId: string,
    shopId: string,
    operation: AdOperationItem,
    traceId: string
  ): Promise<SingleAdOperationResult> {
    if (!operation.adId) {
      return {
        operation: 'RESUME',
        success: false,
        errorMessage: 'Ad ID is required for resume operation',
      };
    }

    await db('cf_advertisement')
      .where({ id: operation.adId, tenant_id: tenantId })
      .update({ status: 'ACTIVE', updated_at: new Date() });

    logger.info(`[${this.name}] Ad resumed: adId=${operation.adId}, traceId=${traceId}`);
    
    return {
      operation: 'RESUME',
      adId: operation.adId,
      success: true,
    };
  }

  private async deleteAd(
    tenantId: string,
    shopId: string,
    operation: AdOperationItem,
    traceId: string
  ): Promise<SingleAdOperationResult> {
    if (!operation.adId) {
      return {
        operation: 'DELETE',
        success: false,
        errorMessage: 'Ad ID is required for delete operation',
      };
    }

    await db('cf_advertisement')
      .where({ id: operation.adId, tenant_id: tenantId })
      .update({ status: 'ARCHIVED', updated_at: new Date() });

    logger.info(`[${this.name}] Ad deleted (archived): adId=${operation.adId}, traceId=${traceId}`);
    
    return {
      operation: 'DELETE',
      adId: operation.adId,
      success: true,
    };
  }

  async syncAdData(payload: AdDataSyncPayload): Promise<AdDataSyncResult> {
    const { tenantId, shopId, traceId, adIds, dateRange } = payload;
    
    logger.info(`[${this.name}] Syncing ad data: tenantId=${tenantId}, adIds=${adIds?.length || 'all'}, traceId=${traceId}`);

    const result: AdDataSyncResult = {
      success: true,
      syncedCount: 0,
      ads: [],
      errors: [],
      syncedAt: new Date(),
    };

    try {
      let query = db('cf_advertisement')
        .where('tenant_id', tenantId)
        .where('shop_id', shopId);

      if (adIds && adIds.length > 0) {
        query = query.whereIn('id', adIds);
      }

      const ads = await query;

      for (const ad of ads) {
        try {
          const mockMetrics = this.generateMockMetrics();
          
          await db('cf_ad_metrics').insert({
            id: this.generateId(),
            tenant_id: tenantId,
            shop_id: shopId,
            ad_id: ad.id,
            record_date: new Date(),
            ...mockMetrics,
          });

          await db('cf_advertisement')
            .where({ id: ad.id })
            .update({
              impressions: db.raw('impressions + ?', [mockMetrics.impressions]),
              clicks: db.raw('clicks + ?', [mockMetrics.clicks]),
              spend: db.raw('spend + ?', [mockMetrics.spend]),
              sales: db.raw('sales + ?', [mockMetrics.sales]),
              orders: db.raw('orders + ?', [mockMetrics.orders]),
              conversions: db.raw('conversions + ?', [mockMetrics.conversions]),
              updated_at: new Date(),
            });

          result.ads.push({
            adId: ad.id,
            platformAdId: ad.platform_ad_id || ad.id,
            adName: ad.ad_name,
            campaignId: ad.campaign_id || '',
            campaignName: ad.campaign_name || '',
            adGroupId: ad.ad_group_id || '',
            adGroupName: ad.ad_group_name || '',
            productId: ad.product_id,
            productName: ad.product_name,
            status: ad.status,
            ...mockMetrics,
            ctr: mockMetrics.impressions > 0 ? (mockMetrics.clicks / mockMetrics.impressions) * 100 : 0,
            cpc: mockMetrics.clicks > 0 ? mockMetrics.spend / mockMetrics.clicks : 0,
            roas: mockMetrics.spend > 0 ? mockMetrics.sales / mockMetrics.spend : 0,
            acos: mockMetrics.sales > 0 ? (mockMetrics.spend / mockMetrics.sales) * 100 : 0,
            date: new Date(),
          });

          result.syncedCount++;
        } catch (error: any) {
          result.errors.push(`Failed to sync ad ${ad.id}: ${error.message}`);
        }
      }

      logger.info(`[${this.name}] Ad data sync completed: synced=${result.syncedCount}, errors=${result.errors.length}, traceId=${traceId}`);
    } catch (error: any) {
      result.success = false;
      result.errors.push(`Sync failed: ${error.message}`);
    }

    return result;
  }

  async getAdPerformance(tenantId: string, shopId: string, adId: string): Promise<AdPerformanceMetrics> {
    const metrics = await db('cf_ad_metrics')
      .where('tenant_id', tenantId)
      .where('shop_id', shopId)
      .where('ad_id', adId)
      .sum({
        impressions: 'impressions',
        clicks: 'clicks',
        spend: 'spend',
        sales: 'sales',
        orders: 'orders',
        conversions: 'conversions',
      })
      .first();

    const totalImpressions = Number(metrics?.impressions || 0);
    const totalClicks = Number(metrics?.clicks || 0);
    const totalSpend = Number(metrics?.spend || 0);
    const totalSales = Number(metrics?.sales || 0);
    const totalOrders = Number(metrics?.orders || 0);

    return {
      totalImpressions,
      totalClicks,
      totalSpend,
      totalSales,
      totalOrders,
      averageCTR: totalImpressions > 0 ? (totalClicks / totalImpressions) * 100 : 0,
      averageCPC: totalClicks > 0 ? totalSpend / totalClicks : 0,
      averageROAS: totalSpend > 0 ? totalSales / totalSpend : 0,
      averageACOS: totalSales > 0 ? (totalSpend / totalSales) * 100 : 0,
    };
  }

  async getActiveAds(tenantId: string, shopId: string): Promise<any[]> {
    return await db('cf_advertisement')
      .where('tenant_id', tenantId)
      .where('shop_id', shopId)
      .where('status', 'ACTIVE')
      .orderBy('updated_at', 'desc');
  }

  private generateMockMetrics(): Record<string, any> {
    const impressions = Math.floor(Math.random() * 10000);
    const ctr = 0.5 + Math.random() * 3;
    const clicks = Math.floor(impressions * ctr / 100);
    const cpc = 0.5 + Math.random() * 2;
    const spend = clicks * cpc;
    const roas = 1 + Math.random() * 4;
    const sales = spend * roas;
    const conversionRate = 2 + Math.random() * 8;
    const orders = Math.floor(clicks * conversionRate / 100);

    return {
      impressions,
      clicks,
      spend: Math.round(spend * 100) / 100,
      sales: Math.round(sales * 100) / 100,
      orders,
      conversions: orders,
    };
  }

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

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

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