/**
 * WhatsApp Conversation Service
 * Service for tracking and managing WhatsApp conversations
 *
 * @module WhatsApp
 */

import { Repository, FindOptionsWhere, In, IsNull } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import {
  WhatsAppConversation,
  WAConversationStatus,
  WAConversationPriority,
} from '../entities';
import { ServiceContext } from './account.service';

export interface CreateConversationDto {
  accountId: string;
  contactId: string;
  status?: WAConversationStatus;
  priority?: WAConversationPriority;
  assignedTo?: string;
  teamId?: string;
  category?: string;
  tags?: string[];
  contextType?: string;
  contextId?: string;
  metadata?: Record<string, any>;
}

export interface UpdateConversationDto {
  status?: WAConversationStatus;
  priority?: WAConversationPriority;
  assignedTo?: string;
  teamId?: string;
  category?: string;
  tags?: string[];
  metadata?: Record<string, any>;
}

export interface ConversationFilters {
  accountId?: string;
  contactId?: string;
  status?: WAConversationStatus;
  priority?: WAConversationPriority;
  assignedTo?: string;
  teamId?: string;
  category?: string;
  unassigned?: boolean;
}

export class WhatsAppConversationService {
  private repository: Repository<WhatsAppConversation>;

  constructor() {
    this.repository = AppDataSource.getRepository(WhatsAppConversation);
  }

  async findAll(
    ctx: ServiceContext,
    filters?: ConversationFilters,
    pagination?: { page?: number; limit?: number }
  ): Promise<{ data: WhatsAppConversation[]; total: number }> {
    const where: FindOptionsWhere<WhatsAppConversation> = {
      tenantId: ctx.tenantId,
    };

    if (filters?.accountId) {
      where.accountId = filters.accountId;
    }
    if (filters?.contactId) {
      where.contactId = filters.contactId;
    }
    if (filters?.status) {
      where.status = filters.status;
    }
    if (filters?.priority) {
      where.priority = filters.priority;
    }
    if (filters?.assignedTo) {
      where.assignedTo = filters.assignedTo;
    }
    if (filters?.teamId) {
      where.teamId = filters.teamId;
    }
    if (filters?.category) {
      where.category = filters.category;
    }
    if (filters?.unassigned) {
      where.assignedTo = IsNull();
    }

    const page = pagination?.page || 1;
    const limit = pagination?.limit || 50;
    const skip = (page - 1) * limit;

    const [data, total] = await this.repository.findAndCount({
      where,
      relations: ['account', 'contact'],
      order: { updatedAt: 'DESC' },
      skip,
      take: limit,
    });

    return { data, total };
  }

  async findById(ctx: ServiceContext, id: string): Promise<WhatsAppConversation | null> {
    return this.repository.findOne({
      where: { id, tenantId: ctx.tenantId },
      relations: ['account', 'contact'],
    });
  }

  async findByContact(ctx: ServiceContext, contactId: string): Promise<WhatsAppConversation | null> {
    return this.repository.findOne({
      where: { contactId, tenantId: ctx.tenantId, status: In(['open', 'pending']) },
      relations: ['account', 'contact'],
    });
  }

  async findOpen(ctx: ServiceContext, filters?: Omit<ConversationFilters, 'status'>): Promise<WhatsAppConversation[]> {
    const where: FindOptionsWhere<WhatsAppConversation> = {
      tenantId: ctx.tenantId,
      status: In(['open', 'pending']),
    };

    if (filters?.accountId) {
      where.accountId = filters.accountId;
    }
    if (filters?.assignedTo) {
      where.assignedTo = filters.assignedTo;
    }
    if (filters?.teamId) {
      where.teamId = filters.teamId;
    }

    return this.repository.find({
      where,
      relations: ['account', 'contact'],
      order: { updatedAt: 'DESC' },
    });
  }

  async findUnassigned(ctx: ServiceContext, accountId?: string): Promise<WhatsAppConversation[]> {
    const where: FindOptionsWhere<WhatsAppConversation> = {
      tenantId: ctx.tenantId,
      assignedTo: IsNull(),
      status: In(['open', 'pending']),
    };

    if (accountId) {
      where.accountId = accountId;
    }

    return this.repository.find({
      where,
      relations: ['account', 'contact'],
      order: { createdAt: 'ASC' },
    });
  }

  async findByAgent(ctx: ServiceContext, agentId: string): Promise<WhatsAppConversation[]> {
    return this.repository.find({
      where: {
        tenantId: ctx.tenantId,
        assignedTo: agentId,
        status: In(['open', 'pending']),
      },
      relations: ['account', 'contact'],
      order: { updatedAt: 'DESC' },
    });
  }

  async create(ctx: ServiceContext, data: CreateConversationDto): Promise<WhatsAppConversation> {
    const conversation = this.repository.create({
      ...data,
      tenantId: ctx.tenantId,
      status: data.status || 'open',
      priority: data.priority || 'normal',
    });

    return this.repository.save(conversation);
  }

  async getOrCreate(ctx: ServiceContext, data: CreateConversationDto): Promise<WhatsAppConversation> {
    const existing = await this.findByContact(ctx, data.contactId);
    if (existing) {
      return existing;
    }
    return this.create(ctx, data);
  }

  async update(
    ctx: ServiceContext,
    id: string,
    data: UpdateConversationDto
  ): Promise<WhatsAppConversation | null> {
    const conversation = await this.findById(ctx, id);
    if (!conversation) {
      return null;
    }

    if (data.assignedTo && !conversation.assignedTo) {
      conversation.assignedAt = new Date();
    }

    Object.assign(conversation, data);
    return this.repository.save(conversation);
  }

  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const result = await this.repository.delete({ id, tenantId: ctx.tenantId });
    return result.affected ? result.affected > 0 : false;
  }

  async assign(
    ctx: ServiceContext,
    id: string,
    agentId: string,
    teamId?: string
  ): Promise<WhatsAppConversation | null> {
    const conversation = await this.findById(ctx, id);
    if (!conversation) {
      return null;
    }

    conversation.assignedTo = agentId;
    conversation.assignedAt = new Date();
    if (teamId) {
      conversation.teamId = teamId;
    }

    return this.repository.save(conversation);
  }

  async unassign(ctx: ServiceContext, id: string): Promise<WhatsAppConversation | null> {
    const conversation = await this.findById(ctx, id);
    if (!conversation) {
      return null;
    }

    conversation.assignedTo = null as any;
    conversation.assignedAt = null as any;

    return this.repository.save(conversation);
  }

  async resolve(ctx: ServiceContext, id: string): Promise<WhatsAppConversation | null> {
    const conversation = await this.findById(ctx, id);
    if (!conversation) {
      return null;
    }

    conversation.status = 'resolved';
    conversation.resolvedAt = new Date();

    return this.repository.save(conversation);
  }

  async reopen(ctx: ServiceContext, id: string): Promise<WhatsAppConversation | null> {
    const conversation = await this.findById(ctx, id);
    if (!conversation) {
      return null;
    }

    conversation.status = 'open';
    conversation.resolvedAt = null as any;

    return this.repository.save(conversation);
  }

  async close(ctx: ServiceContext, id: string): Promise<WhatsAppConversation | null> {
    return this.update(ctx, id, { status: 'closed' });
  }

  async setPriority(
    ctx: ServiceContext,
    id: string,
    priority: WAConversationPriority
  ): Promise<WhatsAppConversation | null> {
    return this.update(ctx, id, { priority });
  }

  async addTag(ctx: ServiceContext, id: string, tag: string): Promise<WhatsAppConversation | null> {
    const conversation = await this.findById(ctx, id);
    if (!conversation) {
      return null;
    }

    if (!conversation.tags.includes(tag)) {
      conversation.tags = [...conversation.tags, tag];
      return this.repository.save(conversation);
    }
    return conversation;
  }

  async removeTag(ctx: ServiceContext, id: string, tag: string): Promise<WhatsAppConversation | null> {
    const conversation = await this.findById(ctx, id);
    if (!conversation) {
      return null;
    }

    conversation.tags = conversation.tags.filter(t => t !== tag);
    return this.repository.save(conversation);
  }

  async incrementMessageCount(ctx: ServiceContext, id: string): Promise<void> {
    await this.repository.increment({ id, tenantId: ctx.tenantId }, 'messageCount', 1);
  }

  async incrementUnreadCount(ctx: ServiceContext, id: string): Promise<void> {
    await this.repository.increment({ id, tenantId: ctx.tenantId }, 'unreadCount', 1);
  }

  async markAsRead(ctx: ServiceContext, id: string): Promise<void> {
    await this.repository.update({ id, tenantId: ctx.tenantId }, { unreadCount: 0 });
  }

  async recordFirstResponse(ctx: ServiceContext, id: string): Promise<void> {
    const conversation = await this.findById(ctx, id);
    if (conversation && !conversation.firstResponseAt) {
      conversation.firstResponseAt = new Date();
      await this.repository.save(conversation);
    }
  }

  async getStatistics(ctx: ServiceContext, accountId?: string): Promise<{
    total: number;
    open: number;
    pending: number;
    resolved: number;
    closed: number;
    unassigned: number;
    avgResponseTime: number | null;
    byPriority: Record<WAConversationPriority, number>;
  }> {
    const where: FindOptionsWhere<WhatsAppConversation> = { tenantId: ctx.tenantId };
    if (accountId) {
      where.accountId = accountId;
    }

    const conversations = await this.repository.find({ where });

    const responseTimes = conversations
      .filter(c => c.firstResponseAt && c.createdAt)
      .map(c => c.firstResponseAt!.getTime() - c.createdAt.getTime());

    const avgResponseTime = responseTimes.length > 0
      ? responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length
      : null;

    const byPriority: Record<string, number> = {};
    conversations.forEach(c => {
      byPriority[c.priority] = (byPriority[c.priority] || 0) + 1;
    });

    return {
      total: conversations.length,
      open: conversations.filter(c => c.status === 'open').length,
      pending: conversations.filter(c => c.status === 'pending').length,
      resolved: conversations.filter(c => c.status === 'resolved').length,
      closed: conversations.filter(c => c.status === 'closed').length,
      unassigned: conversations.filter(c => !c.assignedTo).length,
      avgResponseTime,
      byPriority: byPriority as Record<WAConversationPriority, number>,
    };
  }
}
