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

interface CollectionTask {
  id: string;
  tenantId: string;
  shopId: string;
  platform: string;
  type: 'product' | 'order';
  status: 'pending' | 'running' | 'completed' | 'failed';
  params: Record<string, any>;
  result?: {
    total: number;
    success: number;
    failed: number;
  };
  error?: string;
  createdAt: Date;
  completedAt?: Date;
}

interface CollectionResult {
  taskId: string;
  platform: string;
  type: 'product' | 'order';
  total: number;
  collected: number;
  failed: number;
  items: any[];
}

export class CollectionAdapterService {
  private static readonly TABLE_NAME = 'cf_collection_tasks';
  private static readonly PRODUCT_TABLE = 'cf_collected_products';
  private static readonly ORDER_TABLE = 'cf_collected_orders';

  private static tiktokConnector = new TikTokConnector();
  private static temuConnector = new TemuConnector();

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

  private static async initTasksTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.TABLE_NAME);
    if (!hasTable) {
      await db.schema.createTable(this.TABLE_NAME, (table) => {
        table.string('id', 50).primary();
        table.string('tenant_id', 50).notNullable().index();
        table.string('shop_id', 50).notNullable();
        table.string('platform', 20).notNullable();
        table.enum('type', ['product', 'order']).notNullable();
        table.enum('status', ['pending', 'running', 'completed', 'failed']).defaultTo('pending');
        table.json('params');
        table.json('result');
        table.text('error');
        table.timestamps(true, true);
        table.timestamp('completed_at');
        table.index(['tenant_id', 'platform', 'status']);
      });
      logger.info(`✅ Table ${this.TABLE_NAME} created`);
    }
  }

  private static async initProductTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.PRODUCT_TABLE);
    if (!hasTable) {
      await db.schema.createTable(this.PRODUCT_TABLE, (table) => {
        table.increments('id').primary();
        table.string('tenant_id', 50).notNullable().index();
        table.string('shop_id', 50).notNullable();
        table.string('platform', 20).notNullable();
        table.string('external_id', 100).notNullable();
        table.string('title', 500).notNullable();
        table.text('description');
        table.decimal('price', 10, 2);
        table.string('currency', 10);
        table.json('images');
        table.json('skus');
        table.json('attributes');
        table.string('category');
        table.enum('status', ['active', 'inactive', 'delisted']).defaultTo('active');
        table.timestamp('collected_at').defaultTo(db.fn.now());
        table.timestamps(true, true);
        table.unique(['tenant_id', 'platform', 'external_id']);
      });
      logger.info(`✅ Table ${this.PRODUCT_TABLE} created`);
    }
  }

  private static async initOrderTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.ORDER_TABLE);
    if (!hasTable) {
      await db.schema.createTable(this.ORDER_TABLE, (table) => {
        table.string('id', 50).primary();
        table.string('tenant_id', 50).notNullable().index();
        table.string('shop_id', 50).notNullable();
        table.string('platform', 20).notNullable();
        table.string('external_id', 100).notNullable();
        table.enum('status', ['pending', 'processing', 'shipped', 'delivered', 'cancelled']);
        table.timestamp('order_time');
        table.json('buyer_info');
        table.json('items');
        table.json('shipping_info');
        table.decimal('total_amount', 10, 2);
        table.string('currency', 10);
        table.timestamp('collected_at').defaultTo(db.fn.now());
        table.timestamps(true, true);
        table.unique(['tenant_id', 'platform', 'external_id']);
      });
      logger.info(`✅ Table ${this.ORDER_TABLE} created`);
    }
  }

  static async createCollectionTask(params: {
    tenantId: string;
    shopId: string;
    platform: string;
    type: 'product' | 'order';
    params?: Record<string, any>;
  }): Promise<CollectionTask> {
    const id = `COL-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`;

    await db(this.TABLE_NAME).insert({
      id,
      tenant_id: params.tenantId,
      shop_id: params.shopId,
      platform: params.platform,
      type: params.type,
      status: 'pending',
      params: JSON.stringify(params.params || {}),
      created_at: new Date(),
      updated_at: new Date()
    });

    return {
      id,
      tenantId: params.tenantId,
      shopId: params.shopId,
      platform: params.platform,
      type: params.type,
      status: 'pending',
      params: params.params || {},
      createdAt: new Date()
    };
  }

  static async executeCollectionTask(taskId: string): Promise<CollectionResult> {
    const task = await db(this.TABLE_NAME).where({ id: taskId }).first();
    if (!task) {
      throw new Error(`Task ${taskId} not found`);
    }

    await db(this.TABLE_NAME).where({ id: taskId }).update({
      status: 'running',
      updated_at: new Date()
    });

    try {
      let result: CollectionResult;

      if (task.type === 'product') {
        result = await this.collectProducts(
          task.tenant_id,
          task.shop_id,
          task.platform,
          JSON.parse(task.params || '{}')
        );
      } else {
        result = await this.collectOrders(
          task.tenant_id,
          task.shop_id,
          task.platform,
          JSON.parse(task.params || '{}')
        );
      }

      await db(this.TABLE_NAME).where({ id: taskId }).update({
        status: 'completed',
        result: JSON.stringify({
          total: result.total,
          success: result.collected,
          failed: result.failed
        }),
        completed_at: new Date(),
        updated_at: new Date()
      });

      return result;
    } catch (error: any) {
      await db(this.TABLE_NAME).where({ id: taskId }).update({
        status: 'failed',
        error: error.message,
        updated_at: new Date()
      });

      throw error;
    }
  }

  static async collectProducts(
    tenantId: string,
    shopId: string,
    platform: string,
    params: Record<string, any> = {}
  ): Promise<CollectionResult> {
    logger.info(`[CollectionAdapter] Collecting products from ${platform} for shop ${shopId}`);

    const taskId = `COL-${Date.now()}`;
    let products: PlatformProduct[] = [];

    switch (platform.toUpperCase()) {
      case 'TIKTOK':
        products = await this.tiktokConnector.pullProducts(shopId, params);
        break;
      case 'TEMU':
        products = await this.temuConnector.pullProducts(shopId, params);
        break;
      default:
        throw new Error(`Unsupported platform: ${platform}`);
    }

    let collected = 0;
    let failed = 0;

    for (const product of products) {
      try {
        await db(this.PRODUCT_TABLE)
          .insert({
            tenant_id: tenantId,
            shop_id: shopId,
            platform: platform.toUpperCase(),
            external_id: product.externalId,
            title: product.title,
            description: product.description,
            price: product.price,
            currency: product.currency,
            images: JSON.stringify(product.images),
            skus: JSON.stringify(product.skus),
            attributes: JSON.stringify(product.attributes || {}),
            category: product.category,
            status: 'active',
            collected_at: new Date(),
            created_at: new Date(),
            updated_at: new Date()
          })
          .onConflict(['tenant_id', 'platform', 'external_id'])
          .merge([
            'title',
            'description',
            'price',
            'currency',
            'images',
            'skus',
            'attributes',
            'category',
            'status',
            'updated_at'
          ]);
        collected++;
      } catch (error: any) {
        logger.error(`[CollectionAdapter] Failed to save product ${product.externalId}: ${error.message}`);
        failed++;
      }
    }

    return {
      taskId,
      platform,
      type: 'product',
      total: products.length,
      collected,
      failed,
      items: products
    };
  }

  static async collectOrders(
    tenantId: string,
    shopId: string,
    platform: string,
    params: Record<string, any> = {}
  ): Promise<CollectionResult> {
    logger.info(`[CollectionAdapter] Collecting orders from ${platform} for shop ${shopId}`);

    const taskId = `COL-${Date.now()}`;
    let orders: any[] = [];

    switch (platform.toUpperCase()) {
      case 'TIKTOK':
        orders = await this.tiktokConnector.pullOrders(shopId, params);
        break;
      case 'TEMU':
        orders = await this.temuConnector.pullOrders(shopId, params);
        break;
      default:
        throw new Error(`Unsupported platform: ${platform}`);
    }

    let collected = 0;
    let failed = 0;

    for (const order of orders) {
      try {
        const orderId = order.order_id || order.order_sn;
        const orderTime = order.create_time ? new Date(order.create_time * 1000) : new Date();

        await db(this.ORDER_TABLE)
          .insert({
            id: `${platform.toUpperCase()}-${orderId}`,
            tenant_id: tenantId,
            shop_id: shopId,
            platform: platform.toUpperCase(),
            external_id: orderId,
            status: this.mapOrderStatus(order.order_status),
            order_time: orderTime,
            buyer_info: JSON.stringify(order.buyer_info),
            items: JSON.stringify(order.items || order.item_list),
            shipping_info: JSON.stringify(order.shipping_info),
            total_amount: (order.total_amount || order.order_amount) / 100,
            currency: order.currency || 'USD',
            collected_at: new Date(),
            created_at: new Date(),
            updated_at: new Date()
          })
          .onConflict(['tenant_id', 'platform', 'external_id'])
          .merge([
            'status',
            'buyer_info',
            'items',
            'shipping_info',
            'total_amount',
            'updated_at'
          ]);
        collected++;
      } catch (error: any) {
        logger.error(`[CollectionAdapter] Failed to save order: ${error.message}`);
        failed++;
      }
    }

    return {
      taskId,
      platform,
      type: 'order',
      total: orders.length,
      collected,
      failed,
      items: orders
    };
  }

  private static mapOrderStatus(status: string | number): string {
    if (typeof status === 'number') {
      switch (status) {
        case 0: return 'pending';
        case 1: return 'processing';
        case 2: return 'shipped';
        case 3: return 'delivered';
        case 4: return 'cancelled';
        default: return 'pending';
      }
    }

    const statusMap: Record<string, string> = {
      'PENDING': 'pending',
      'PROCESSING': 'processing',
      'SHIPPED': 'shipped',
      'DELIVERED': 'delivered',
      'CANCELLED': 'cancelled',
      'AWAITING_SHIPMENT': 'processing',
      'AWAITING_COLLECTION': 'processing',
      'IN_TRANSIT': 'shipped',
      'DELIVERED_TO_BUYER': 'delivered'
    };

    return statusMap[status.toUpperCase()] || 'pending';
  }

  static async getCollectedProducts(
    tenantId: string,
    platform?: string,
    limit: number = 50,
    offset: number = 0
  ): Promise<any[]> {
    let query = db(this.PRODUCT_TABLE).where({ tenant_id: tenantId });

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

    return query
      .orderBy('collected_at', 'desc')
      .limit(limit)
      .offset(offset);
  }

  static async getCollectedOrders(
    tenantId: string,
    platform?: string,
    status?: string,
    limit: number = 50,
    offset: number = 0
  ): Promise<any[]> {
    let query = db(this.ORDER_TABLE).where({ tenant_id: tenantId });

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

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

    return query
      .orderBy('order_time', 'desc')
      .limit(limit)
      .offset(offset);
  }

  static async getCollectionTasks(
    tenantId: string,
    status?: string,
    limit: number = 50,
    offset: number = 0
  ): Promise<CollectionTask[]> {
    let query = db(this.TABLE_NAME).where({ tenant_id: tenantId });

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

    const tasks = await query
      .orderBy('created_at', 'desc')
      .limit(limit)
      .offset(offset);

    return tasks.map(t => ({
      id: t.id,
      tenantId: t.tenant_id,
      shopId: t.shop_id,
      platform: t.platform,
      type: t.type,
      status: t.status,
      params: JSON.parse(t.params || '{}'),
      result: t.result ? JSON.parse(t.result) : undefined,
      error: t.error,
      createdAt: t.created_at,
      completedAt: t.completed_at
    }));
  }

  static async getCollectionStats(tenantId: string): Promise<{
    totalProducts: number;
    totalOrders: number;
    byPlatform: Record<string, { products: number; orders: number }>;
    recentTasks: number;
  }> {
    const [productCount] = await db(this.PRODUCT_TABLE)
      .where({ tenant_id: tenantId })
      .count('* as total');

    const [orderCount] = await db(this.ORDER_TABLE)
      .where({ tenant_id: tenantId })
      .count('* as total');

    const productByPlatform = await db(this.PRODUCT_TABLE)
      .where({ tenant_id: tenantId })
      .select('platform')
      .count('* as count')
      .groupBy('platform');

    const orderByPlatform = await db(this.ORDER_TABLE)
      .where({ tenant_id: tenantId })
      .select('platform')
      .count('* as count')
      .groupBy('platform');

    const [recentTasksCount] = await db(this.TABLE_NAME)
      .where({ tenant_id: tenantId })
      .where('created_at', '>=', new Date(Date.now() - 24 * 60 * 60 * 1000))
      .count('* as total');

    const byPlatform: Record<string, { products: number; orders: number }> = {};

    for (const p of productByPlatform) {
      byPlatform[p.platform] = { products: Number(p.count), orders: 0 };
    }

    for (const o of orderByPlatform) {
      if (!byPlatform[o.platform]) {
        byPlatform[o.platform] = { products: 0, orders: 0 };
      }
      byPlatform[o.platform].orders = Number(o.count);
    }

    return {
      totalProducts: Number(productCount?.total || 0),
      totalOrders: Number(orderCount?.total || 0),
      byPlatform,
      recentTasks: Number(recentTasksCount?.total || 0)
    };
  }
}

export default CollectionAdapterService;
