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

export interface OmnichannelMessage {
  id: string;
  tenantId: string;
  channelId: string;
  channelType: 'email' | 'chat' | 'social' | 'phone' | 'sms';
  customerId: string;
  customerName: string;
  content: string;
  direction: 'inbound' | 'outbound';
  status: 'pending' | 'processing' | 'completed' | 'failed';
  metadata?: Record<string, any>;
  createdAt: Date;
}

export interface CustomerProfile {
  id: string;
  tenantId: string;
  customerId: string;
  channels: string[];
  tags: string[];
  preferences: Record<string, any>;
  lastInteractionAt?: Date;
  totalInteractions: number;
  createdAt: Date;
  updatedAt: Date;
}

export interface TeamTask {
  id: string;
  tenantId: string;
  assignedTo: string;
  customerId: string;
  taskType: 'inquiry' | 'complaint' | 'follow_up' | 'escalation';
  priority: 'low' | 'medium' | 'high' | 'urgent';
  status: 'pending' | 'in_progress' | 'completed';
  description: string;
  dueDate?: Date;
  createdAt: Date;
}

export class OmnichannelCommunicationService {
  private static MESSAGE_TABLE = 'cf_omnichannel_message';
  private static PROFILE_TABLE = 'cf_customer_profile';
  private static TASK_TABLE = 'cf_team_task';

  static async initTables(): Promise<void> {
    await this.initMessageTable();
    await this.initProfileTable();
    await this.initTaskTable();
  }

  private static async initMessageTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.MESSAGE_TABLE);
    if (!hasTable) {
      logger.info(`[OmnichannelCommunication] Creating ${this.MESSAGE_TABLE} table...`);
      await db.schema.createTable(this.MESSAGE_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 36).notNullable().index();
        table.string('channel_id', 64).notNullable();
        table.enum('channel_type', ['email', 'chat', 'social', 'phone', 'sms']).notNullable();
        table.string('customer_id', 36).notNullable().index();
        table.string('customer_name', 128);
        table.text('content').notNullable();
        table.enum('direction', ['inbound', 'outbound']).notNullable();
        table.enum('status', ['pending', 'processing', 'completed', 'failed']).defaultTo('pending');
        table.json('metadata');
        table.datetime('created_at').notNullable().defaultTo(db.fn.now());
        table.index(['tenant_id', 'channel_type'], 'idx_tenant_channel');
        table.index(['tenant_id', 'customer_id'], 'idx_tenant_customer');
      });
      logger.info(`[OmnichannelCommunication] Table ${this.MESSAGE_TABLE} created`);
    }
  }

  private static async initProfileTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.PROFILE_TABLE);
    if (!hasTable) {
      logger.info(`[OmnichannelCommunication] Creating ${this.PROFILE_TABLE} table...`);
      await db.schema.createTable(this.PROFILE_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 36).notNullable().index();
        table.string('customer_id', 36).notNullable();
        table.json('channels');
        table.json('tags');
        table.json('preferences');
        table.datetime('last_interaction_at');
        table.integer('total_interactions').defaultTo(0);
        table.datetime('created_at').notNullable().defaultTo(db.fn.now());
        table.datetime('updated_at').notNullable().defaultTo(db.fn.now());
        table.unique(['tenant_id', 'customer_id'], 'uk_tenant_customer');
      });
      logger.info(`[OmnichannelCommunication] Table ${this.PROFILE_TABLE} created`);
    }
  }

  private static async initTaskTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.TASK_TABLE);
    if (!hasTable) {
      logger.info(`[OmnichannelCommunication] Creating ${this.TASK_TABLE} table...`);
      await db.schema.createTable(this.TASK_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 36).notNullable().index();
        table.string('assigned_to', 36).notNullable();
        table.string('customer_id', 36).notNullable().index();
        table.enum('task_type', ['inquiry', 'complaint', 'follow_up', 'escalation']).notNullable();
        table.enum('priority', ['low', 'medium', 'high', 'urgent']).defaultTo('medium');
        table.enum('status', ['pending', 'in_progress', 'completed']).defaultTo('pending');
        table.text('description').notNullable();
        table.datetime('due_date');
        table.datetime('created_at').notNullable().defaultTo(db.fn.now());
        table.index(['tenant_id', 'assigned_to'], 'idx_tenant_assignee');
      });
      logger.info(`[OmnichannelCommunication] Table ${this.TASK_TABLE} created`);
    }
  }

  static async aggregateMessages(
    tenantId: string,
    channels: Array<{ type: string; id: string }>
  ): Promise<OmnichannelMessage[]> {
    const messages: OmnichannelMessage[] = [];

    for (const channel of channels) {
      const rows = await db(this.MESSAGE_TABLE)
        .where({ tenant_id: tenantId, channel_type: channel.type, channel_id: channel.id })
        .select('*')
        .orderBy('created_at', 'desc')
        .limit(100);

      messages.push(...rows.map((row) => this.rowToMessage(row)));
    }

    messages.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());

    logger.info(`[OmnichannelCommunication] Aggregated ${messages.length} messages from ${channels.length} channels`);
    return messages;
  }

  static async autoReply(
    tenantId: string,
    customerId: string,
    question: string,
    context: Record<string, any>
  ): Promise<string> {
    const keywords: Record<string, string> = {
      '订单状态': '您的订单正在处理中，预计3-5个工作日送达。',
      '退款': '退款申请已收到，我们将在1-2个工作日内处理。',
      '发货': '您的订单已发货，物流信息已发送至您的邮箱。',
      '退货': '退货申请已提交，请按照指引寄回商品。',
      '支付': '支付成功，订单已确认。',
    };

    let reply = '感谢您的咨询，我们的客服团队将尽快为您处理。';

    for (const [keyword, response] of Object.entries(keywords)) {
      if (question.includes(keyword)) {
        reply = response;
        break;
      }
    }

    DomainEventBus.publish('customer.auto_reply', {
      customerId,
      question,
      reply,
      tenantId,
      timestamp: new Date(),
    });

    logger.info(`[OmnichannelCommunication] Auto-replied to customer ${customerId}`);
    return reply;
  }

  static async createTeamTask(
    tenantId: string,
    task: Omit<TeamTask, 'id' | 'createdAt'>
  ): Promise<TeamTask> {
    const id = this.generateId();
    const newTask: TeamTask = {
      ...task,
      id,
      createdAt: new Date(),
    };

    await db(this.TASK_TABLE).insert({
      id: newTask.id,
      tenant_id: newTask.tenantId,
      assigned_to: newTask.assignedTo,
      customer_id: newTask.customerId,
      task_type: newTask.taskType,
      priority: newTask.priority,
      status: newTask.status,
      description: newTask.description,
      due_date: newTask.dueDate,
      created_at: newTask.createdAt,
    });

    logger.info(`[OmnichannelCommunication] Created team task ${id}`);
    return newTask;
  }

  static async updateCustomerProfile(
    tenantId: string,
    customerId: string,
    updates: Partial<CustomerProfile>
  ): Promise<CustomerProfile> {
    const existing = await db(this.PROFILE_TABLE)
      .where({ tenant_id: tenantId, customer_id: customerId })
      .first();

    const now = new Date();
    const profileData = {
      id: existing?.id || this.generateId(),
      tenant_id: tenantId,
      customer_id: customerId,
      channels: JSON.stringify(updates.channels || existing?.channels || []),
      tags: JSON.stringify(updates.tags || existing?.tags || []),
      preferences: JSON.stringify(updates.preferences || existing?.preferences || {}),
      last_interaction_at: updates.lastInteractionAt || now,
      total_interactions: (existing?.total_interactions || 0) + 1,
      updated_at: now,
    };

    if (existing) {
      await db(this.PROFILE_TABLE)
        .where({ id: existing.id })
        .update(profileData);
    } else {
      await db(this.PROFILE_TABLE).insert({
        ...profileData,
        created_at: now,
      });
    }

    logger.info(`[OmnichannelCommunication] Updated customer profile for ${customerId}`);
    return this.rowToProfile(profileData);
  }

  static async translateMessage(
    tenantId: string,
    content: string,
    targetLanguage: string
  ): Promise<string> {
    const translations: Record<string, Record<string, string>> = {
      'zh-en': {
        '订单': 'Order',
        '退款': 'Refund',
        '发货': 'Shipping',
        '退货': 'Return',
      },
      'en-zh': {
        'Order': '订单',
        'Refund': '退款',
        'Shipping': '发货',
        'Return': '退货',
      },
    };

    const key = `${targetLanguage}`;
    const dict = translations[key] || {};

    let translated = content;
    for (const [source, target] of Object.entries(dict)) {
      translated = translated.replace(new RegExp(source, 'g'), target);
    }

    logger.info(`[OmnichannelCommunication] Translated message to ${targetLanguage}`);
    return translated;
  }

  private static rowToMessage(row: any): OmnichannelMessage {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      channelId: row.channel_id,
      channelType: row.channel_type,
      customerId: row.customer_id,
      customerName: row.customer_name,
      content: row.content,
      direction: row.direction,
      status: row.status,
      metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
      createdAt: row.created_at,
    };
  }

  private static rowToProfile(row: any): CustomerProfile {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      customerId: row.customer_id,
      channels: row.channels ? JSON.parse(row.channels) : [],
      tags: row.tags ? JSON.parse(row.tags) : [],
      preferences: row.preferences ? JSON.parse(row.preferences) : {},
      lastInteractionAt: row.last_interaction_at,
      totalInteractions: row.total_interactions || 0,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }

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