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

export interface SKUData {
  id: string;
  tenantId: string;
  shopId: string;
  skuId: string;
  skuName: string;
  productName: string;
  returnRate: number;
  returnCount: number;
  salesCount: number;
  returnReasons: { reason: string; count: number }[];
  status: 'NORMAL' | 'WARNING' | 'CRITICAL';
  createdAt: string;
  updatedAt: string;
}

export interface ReturnData {
  id: string;
  tenantId: string;
  shopId: string;
  skuId: string;
  skuName: string;
  productName: string;
  orderId: string;
  returnReason: string;
  returnType: 'REFUND' | 'EXCHANGE' | 'RETURN';
  status: 'PENDING' | 'APPROVED' | 'PROCESSING' | 'COMPLETED' | 'REJECTED';
  amount: number;
  createdAt: string;
  updatedAt: string;
}

export interface ReturnTrend {
  date: string;
  returnCount: number;
  returnRate: number;
  refundAmount: number;
}

export class ReturnService {
  private static SKU_TABLE = 'cf_return_sku';
  private static RETURN_TABLE = 'cf_return_record';

  static async initTables(): Promise<void> {
    await this.initSKUTable();
    await this.initReturnTable();
  }

  private static async initSKUTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.SKU_TABLE);
    if (!hasTable) {
      await db.schema.createTable(this.SKU_TABLE, (table) => {
        table.string('id', 64).primary();
        table.string('tenant_id', 64).notNullable();
        table.string('shop_id', 64).notNullable();
        table.string('sku_id', 64).notNullable();
        table.string('sku_name', 256);
        table.string('product_name', 256);
        table.decimal('return_rate', 5, 2).defaultTo(0);
        table.integer('return_count').defaultTo(0);
        table.integer('sales_count').defaultTo(0);
        table.json('return_reasons');
        table.enum('status', ['NORMAL', 'WARNING', 'CRITICAL']).defaultTo('NORMAL');
        table.timestamps(true, true);
        table.index(['tenant_id', 'shop_id']);
        table.index(['sku_id']);
        table.unique(['shop_id', 'sku_id']);
      });
    }
  }

  private static async initReturnTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.RETURN_TABLE);
    if (!hasTable) {
      await db.schema.createTable(this.RETURN_TABLE, (table) => {
        table.string('id', 64).primary();
        table.string('tenant_id', 64).notNullable();
        table.string('shop_id', 64).notNullable();
        table.string('sku_id', 64);
        table.string('sku_name', 256);
        table.string('product_name', 256);
        table.string('order_id', 64);
        table.string('return_reason', 256);
        table.enum('return_type', ['REFUND', 'EXCHANGE', 'RETURN']).defaultTo('RETURN');
        table.enum('status', ['PENDING', 'APPROVED', 'PROCESSING', 'COMPLETED', 'REJECTED']).defaultTo('PENDING');
        table.decimal('amount', 10, 2);
        table.timestamps(true, true);
        table.index(['tenant_id', 'shop_id']);
        table.index(['sku_id']);
        table.index(['status']);
      });
    }
  }

  static async fetchSKUData(tenantId: string, params?: { status?: string; shopId?: string }): Promise<SKUData[]> {
    let query = db(this.SKU_TABLE).where('tenant_id', tenantId);
    if (params?.status) query = query.where('status', params.status);
    if (params?.shopId) query = query.where('shop_id', params.shopId);
    return query.orderBy('return_rate', 'desc');
  }

  static async updateSKUStatus(id: string, status: 'NORMAL' | 'WARNING' | 'CRITICAL'): Promise<SKUData> {
    await db(this.SKU_TABLE).where({ id }).update({ status, updated_at: new Date().toISOString() });
    const sku = await db(this.SKU_TABLE).where({ id }).first();
    await DomainEventBus.publish('return.sku_status_updated', { skuId: id, status });
    return sku;
  }

  static async fetchReturns(tenantId: string, params?: { status?: string; shopId?: string; skuId?: string }): Promise<ReturnData[]> {
    let query = db(this.RETURN_TABLE).where('tenant_id', tenantId);
    if (params?.status) query = query.where('status', params.status);
    if (params?.shopId) query = query.where('shop_id', params.shopId);
    if (params?.skuId) query = query.where('sku_id', params.skuId);
    return query.orderBy('created_at', 'desc');
  }

  static async createReturn(tenantId: string, data: {
    shopId: string;
    skuId: string;
    skuName: string;
    productName: string;
    orderId: string;
    returnReason: string;
    returnType: 'REFUND' | 'EXCHANGE' | 'RETURN';
    amount: number;
  }): Promise<ReturnData> {
    const id = this.generateId('ret');
    const now = new Date().toISOString();
    const returnData: ReturnData = {
      id,
      tenantId,
      shopId: data.shopId,
      skuId: data.skuId,
      skuName: data.skuName,
      productName: data.productName,
      orderId: data.orderId,
      returnReason: data.returnReason,
      returnType: data.returnType,
      status: 'PENDING',
      amount: data.amount,
      createdAt: now,
      updatedAt: now,
    };
    await db(this.RETURN_TABLE).insert({
      id: returnData.id,
      tenant_id: returnData.tenantId,
      shop_id: returnData.shopId,
      sku_id: returnData.skuId,
      sku_name: returnData.skuName,
      product_name: returnData.productName,
      order_id: returnData.orderId,
      return_reason: returnData.returnReason,
      return_type: returnData.returnType,
      status: returnData.status,
      amount: returnData.amount,
      created_at: returnData.createdAt,
      updated_at: returnData.updatedAt,
    });
    await this.updateSKUStats(data.shopId, data.skuId);
    await DomainEventBus.publish('return.created', { returnId: id, shopId: data.shopId });
    return returnData;
  }

  static async updateReturnStatus(id: string, status: ReturnData['status']): Promise<ReturnData> {
    await db(this.RETURN_TABLE).where({ id }).update({ status, updated_at: new Date().toISOString() });
    const returnData = await db(this.RETURN_TABLE).where({ id }).first();
    await DomainEventBus.publish('return.status_updated', { returnId: id, status });
    return returnData;
  }

  static async getReturnTrend(tenantId: string, params: { shopId?: string; startDate: string; endDate: string }): Promise<ReturnTrend[]> {
    let query = db(this.RETURN_TABLE)
      .where('tenant_id', tenantId)
      .whereBetween('created_at', [params.startDate, params.endDate]);
    if (params.shopId) query = query.where('shop_id', params.shopId);
    const returns = await query;
    const trendMap = new Map<string, { count: number; amount: number }>();
    for (const r of returns) {
      const date = r.created_at.split('T')[0];
      const existing = trendMap.get(date) || { count: 0, amount: 0 };
      existing.count += 1;
      existing.amount += parseFloat(r.amount || 0);
      trendMap.set(date, existing);
    }
    const trends: ReturnTrend[] = [];
    for (const [date, data] of trendMap) {
      trends.push({
        date,
        returnCount: data.count,
        returnRate: 0,
        refundAmount: data.amount,
      });
    }
    return trends.sort((a, b) => a.date.localeCompare(b.date));
  }

  static async getReturnStats(tenantId: string, params?: { shopId?: string }): Promise<{
    totalReturns: number;
    pendingReturns: number;
    completedReturns: number;
    totalRefundAmount: number;
    avgReturnRate: number;
  }> {
    let query = db(this.RETURN_TABLE).where('tenant_id', tenantId);
    if (params?.shopId) query = query.where('shop_id', params.shopId);
    const returns = await query;
    const skuStats = await this.fetchSKUData(tenantId, params);
    return {
      totalReturns: returns.length,
      pendingReturns: returns.filter((r) => r.status === 'PENDING').length,
      completedReturns: returns.filter((r) => r.status === 'COMPLETED').length,
      totalRefundAmount: returns.reduce((sum, r) => sum + parseFloat(r.amount || 0), 0),
      avgReturnRate: skuStats.length > 0 ? skuStats.reduce((sum, s) => sum + (s.returnRate || 0), 0) / skuStats.length : 0,
    };
  }

  private static async updateSKUStats(shopId: string, skuId: string): Promise<void> {
    const returns = await db(this.RETURN_TABLE).where({ shop_id: shopId, sku_id: skuId });
    const returnCount = returns.length;
    const salesCount = returnCount * 10;
    const returnRate = salesCount > 0 ? (returnCount / salesCount) * 100 : 0;
    const reasonMap = new Map<string, number>();
    for (const r of returns) {
      const count = reasonMap.get(r.return_reason) || 0;
      reasonMap.set(r.return_reason, count + 1);
    }
    const returnReasons = Array.from(reasonMap.entries()).map(([reason, count]) => ({ reason, count }));
    let status: 'NORMAL' | 'WARNING' | 'CRITICAL' = 'NORMAL';
    if (returnRate > 10) status = 'WARNING';
    if (returnRate > 20) status = 'CRITICAL';
    await db(this.SKU_TABLE)
      .insert({
        id: this.generateId('sku'),
        tenant_id: returns[0]?.tenant_id || '',
        shop_id: shopId,
        sku_id: skuId,
        return_rate: returnRate,
        return_count: returnCount,
        sales_count: salesCount,
        return_reasons: JSON.stringify(returnReasons),
        status,
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      })
      .onConflict(['shop_id', 'sku_id'])
      .merge();
  }

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