import { Repository, FindOptionsWhere, LessThan, Between, In } from 'typeorm';
import { WhatsAppAccount, WhatsAppContact, WhatsAppMessage, WhatsAppTemplate } from '../entities';

export interface MessageFilters {
  contactId?: string;
  direction?: string;
  messageType?: string;
  status?: string;
  startDate?: Date;
  endDate?: Date;
}

export interface ContactFilters {
  conversationStatus?: string;
  optedIn?: boolean;
  tag?: string;
}

export class WhatsAppService {
  constructor(
    private readonly accountRepository: Repository<WhatsAppAccount>,
    private readonly contactRepository: Repository<WhatsAppContact>,
    private readonly messageRepository: Repository<WhatsAppMessage>,
    private readonly templateRepository: Repository<WhatsAppTemplate>
  ) {}

  // ============================================
  // ACCOUNTS
  // ============================================

  async findAllAccounts(tenantId: string): Promise<WhatsAppAccount[]> {
    return this.accountRepository.find({
      where: { tenantId },
      order: { name: 'ASC' },
    });
  }

  async findActiveAccounts(tenantId: string): Promise<WhatsAppAccount[]> {
    return this.accountRepository.find({
      where: { tenantId, status: 'active' },
      order: { name: 'ASC' },
    });
  }

  async findAccount(id: string): Promise<WhatsAppAccount | null> {
    return this.accountRepository.findOne({ where: { id } });
  }

  async findAccountByPhoneNumber(
    tenantId: string,
    phoneNumber: string
  ): Promise<WhatsAppAccount | null> {
    return this.accountRepository.findOne({ where: { tenantId, phoneNumber } });
  }

  async createAccount(
    tenantId: string,
    data: Partial<WhatsAppAccount>,
    createdBy?: string
  ): Promise<WhatsAppAccount> {
    const account = this.accountRepository.create({
      ...data,
      tenantId,
      createdBy,
      status: 'pending',
    });
    return this.accountRepository.save(account);
  }

  async updateAccount(id: string, data: Partial<WhatsAppAccount>): Promise<WhatsAppAccount | null> {
    const account = await this.findAccount(id);
    if (!account) return null;

    Object.assign(account, data);
    return this.accountRepository.save(account);
  }

  async updateAccountStatus(id: string, status: string): Promise<boolean> {
    const result = await this.accountRepository.update(id, { status: status as any });
    return (result.affected ?? 0) > 0;
  }

  async incrementMessageCount(accountId: string, direction: 'sent' | 'received'): Promise<void> {
    const field = direction === 'sent' ? 'totalMessagesSent' : 'totalMessagesReceived';
    await this.accountRepository
      .createQueryBuilder()
      .update()
      .set({ [field]: () => `${field} + 1` })
      .where('id = :id', { id: accountId })
      .execute();
  }

  // ============================================
  // CONTACTS
  // ============================================

  async findContacts(
    tenantId: string,
    accountId: string,
    filters: ContactFilters = {},
    limit: number = 50
  ): Promise<WhatsAppContact[]> {
    const where: FindOptionsWhere<WhatsAppContact> = { tenantId, accountId };

    if (filters.conversationStatus) {
      where.conversationStatus = filters.conversationStatus as any;
    }
    if (filters.optedIn !== undefined) {
      where.optedIn = filters.optedIn;
    }

    return this.contactRepository.find({
      where,
      order: { lastMessageAt: 'DESC' },
      take: limit,
    });
  }

  async findContact(id: string): Promise<WhatsAppContact | null> {
    return this.contactRepository.findOne({ where: { id } });
  }

  async findContactByPhone(accountId: string, phoneNumber: string): Promise<WhatsAppContact | null> {
    return this.contactRepository.findOne({ where: { accountId, phoneNumber } });
  }

  async createContact(
    tenantId: string,
    accountId: string,
    data: Partial<WhatsAppContact>
  ): Promise<WhatsAppContact> {
    const contact = this.contactRepository.create({
      ...data,
      tenantId,
      accountId,
    });
    return this.contactRepository.save(contact);
  }

  async updateContact(id: string, data: Partial<WhatsAppContact>): Promise<WhatsAppContact | null> {
    const contact = await this.findContact(id);
    if (!contact) return null;

    Object.assign(contact, data);
    return this.contactRepository.save(contact);
  }

  async updateContactConversationWindow(id: string, expiresAt: Date): Promise<void> {
    await this.contactRepository.update(id, {
      conversationWindowExpiresAt: expiresAt,
      canSendTemplateOnly: false,
    });
  }

  async expireConversationWindows(): Promise<number> {
    const now = new Date();
    const result = await this.contactRepository.update(
      { conversationWindowExpiresAt: LessThan(now), canSendTemplateOnly: false },
      { canSendTemplateOnly: true }
    );
    return result.affected ?? 0;
  }

  async optInContact(id: string): Promise<boolean> {
    const result = await this.contactRepository.update(id, {
      optedIn: true,
      optedInAt: new Date(),
      optedOut: false,
      optedOutAt: undefined,
    });
    return (result.affected ?? 0) > 0;
  }

  async optOutContact(id: string): Promise<boolean> {
    const result = await this.contactRepository.update(id, {
      optedOut: true,
      optedOutAt: new Date(),
    });
    return (result.affected ?? 0) > 0;
  }

  async addTagToContact(id: string, tag: string): Promise<WhatsAppContact | null> {
    const contact = await this.findContact(id);
    if (!contact) return null;

    if (!contact.tags.includes(tag)) {
      contact.tags.push(tag);
      return this.contactRepository.save(contact);
    }
    return contact;
  }

  async removeTagFromContact(id: string, tag: string): Promise<WhatsAppContact | null> {
    const contact = await this.findContact(id);
    if (!contact) return null;

    contact.tags = contact.tags.filter((t) => t !== tag);
    return this.contactRepository.save(contact);
  }

  // ============================================
  // MESSAGES
  // ============================================

  async findMessages(
    accountId: string,
    filters: MessageFilters = {},
    limit: number = 50
  ): Promise<WhatsAppMessage[]> {
    const where: FindOptionsWhere<WhatsAppMessage> = { accountId };

    if (filters.contactId) where.contactId = filters.contactId;
    if (filters.direction) where.direction = filters.direction as any;
    if (filters.messageType) where.messageType = filters.messageType as any;
    if (filters.status) where.status = filters.status as any;

    if (filters.startDate && filters.endDate) {
      where.createdAt = Between(filters.startDate, filters.endDate);
    }

    return this.messageRepository.find({
      where,
      order: { createdAt: 'DESC' },
      take: limit,
      relations: ['contact'],
    });
  }

  async findMessage(id: string): Promise<WhatsAppMessage | null> {
    return this.messageRepository.findOne({
      where: { id },
      relations: ['contact'],
    });
  }

  async findMessageByWaId(waMessageId: string): Promise<WhatsAppMessage | null> {
    return this.messageRepository.findOne({ where: { waMessageId } });
  }

  async findConversationMessages(
    contactId: string,
    limit: number = 100
  ): Promise<WhatsAppMessage[]> {
    return this.messageRepository.find({
      where: { contactId },
      order: { createdAt: 'ASC' },
      take: limit,
    });
  }

  async createMessage(
    tenantId: string,
    accountId: string,
    contactId: string,
    data: Partial<WhatsAppMessage>
  ): Promise<WhatsAppMessage> {
    const message = this.messageRepository.create({
      ...data,
      tenantId,
      accountId,
      contactId,
      status: 'pending',
    });

    const savedMessage = await this.messageRepository.save(message);

    // Update contact stats
    const direction = data.direction;
    if (direction) {
      const field = direction === 'outbound' ? 'totalMessagesSent' : 'totalMessagesReceived';
      await this.contactRepository
        .createQueryBuilder()
        .update()
        .set({
          [field]: () => `${field} + 1`,
          lastMessageAt: new Date(),
          lastMessageDirection: direction,
        })
        .where('id = :id', { id: contactId })
        .execute();

      // Update account stats
      await this.incrementMessageCount(accountId, direction === 'outbound' ? 'sent' : 'received');
    }

    return savedMessage;
  }

  async updateMessageStatus(
    id: string,
    status: string,
    timestamp?: Date
  ): Promise<WhatsAppMessage | null> {
    const message = await this.findMessage(id);
    if (!message) return null;

    message.status = status as any;
    message.statusUpdatedAt = timestamp || new Date();

    if (status === 'sent' && !message.sentAt) message.sentAt = timestamp || new Date();
    if (status === 'delivered' && !message.deliveredAt) message.deliveredAt = timestamp || new Date();
    if (status === 'read' && !message.readAt) message.readAt = timestamp || new Date();

    return this.messageRepository.save(message);
  }

  async updateMessageError(id: string, errorCode: string, errorMessage: string): Promise<void> {
    await this.messageRepository.update(id, {
      status: 'failed',
      errorCode,
      errorMessage,
      statusUpdatedAt: new Date(),
    });
  }

  // ============================================
  // TEMPLATES
  // ============================================

  async findTemplates(
    tenantId: string,
    accountId: string,
    category?: string
  ): Promise<WhatsAppTemplate[]> {
    const where: FindOptionsWhere<WhatsAppTemplate> = { tenantId, accountId, isActive: true };
    if (category) where.category = category as any;

    return this.templateRepository.find({
      where,
      order: { name: 'ASC' },
    });
  }

  async findApprovedTemplates(
    tenantId: string,
    accountId: string
  ): Promise<WhatsAppTemplate[]> {
    return this.templateRepository.find({
      where: { tenantId, accountId, metaStatus: 'APPROVED', isActive: true },
      order: { name: 'ASC' },
    });
  }

  async findTemplate(id: string): Promise<WhatsAppTemplate | null> {
    return this.templateRepository.findOne({ where: { id } });
  }

  async findTemplateByName(
    accountId: string,
    name: string,
    language: string = 'es_MX'
  ): Promise<WhatsAppTemplate | null> {
    return this.templateRepository.findOne({ where: { accountId, name, language } });
  }

  async createTemplate(
    tenantId: string,
    accountId: string,
    data: Partial<WhatsAppTemplate>
  ): Promise<WhatsAppTemplate> {
    const template = this.templateRepository.create({
      ...data,
      tenantId,
      accountId,
      metaStatus: 'PENDING',
    });
    return this.templateRepository.save(template);
  }

  async updateTemplate(
    id: string,
    data: Partial<WhatsAppTemplate>
  ): Promise<WhatsAppTemplate | null> {
    const template = await this.findTemplate(id);
    if (!template) return null;

    // Increment version on update
    Object.assign(template, data, { version: template.version + 1 });
    return this.templateRepository.save(template);
  }

  async updateTemplateStatus(
    id: string,
    metaStatus: string,
    metaTemplateId?: string,
    rejectionReason?: string
  ): Promise<WhatsAppTemplate | null> {
    const template = await this.findTemplate(id);
    if (!template) return null;

    template.metaStatus = metaStatus as any;
    if (metaTemplateId) template.metaTemplateId = metaTemplateId;
    if (rejectionReason) template.rejectionReason = rejectionReason;
    if (metaStatus === 'APPROVED') template.approvedAt = new Date();

    return this.templateRepository.save(template);
  }

  async incrementTemplateUsage(id: string): Promise<void> {
    await this.templateRepository
      .createQueryBuilder()
      .update()
      .set({
        usageCount: () => 'usage_count + 1',
        lastUsedAt: new Date(),
      })
      .where('id = :id', { id })
      .execute();
  }

  async deactivateTemplate(id: string): Promise<boolean> {
    const result = await this.templateRepository.update(id, { isActive: false });
    return (result.affected ?? 0) > 0;
  }

  // ============================================
  // STATISTICS
  // ============================================

  async getAccountStats(
    accountId: string,
    startDate: Date,
    endDate: Date
  ): Promise<{
    totalMessages: number;
    sent: number;
    received: number;
    delivered: number;
    read: number;
    failed: number;
  }> {
    const stats = await this.messageRepository
      .createQueryBuilder('msg')
      .select('COUNT(*)', 'total')
      .addSelect("SUM(CASE WHEN msg.direction = 'outbound' THEN 1 ELSE 0 END)", 'sent')
      .addSelect("SUM(CASE WHEN msg.direction = 'inbound' THEN 1 ELSE 0 END)", 'received')
      .addSelect("SUM(CASE WHEN msg.status = 'delivered' THEN 1 ELSE 0 END)", 'delivered')
      .addSelect("SUM(CASE WHEN msg.status = 'read' THEN 1 ELSE 0 END)", 'readCount')
      .addSelect("SUM(CASE WHEN msg.status = 'failed' THEN 1 ELSE 0 END)", 'failed')
      .where('msg.account_id = :accountId', { accountId })
      .andWhere('msg.created_at BETWEEN :startDate AND :endDate', { startDate, endDate })
      .getRawOne();

    return {
      totalMessages: parseInt(stats?.total) || 0,
      sent: parseInt(stats?.sent) || 0,
      received: parseInt(stats?.received) || 0,
      delivered: parseInt(stats?.delivered) || 0,
      read: parseInt(stats?.readCount) || 0,
      failed: parseInt(stats?.failed) || 0,
    };
  }

  async getContactsWithExpiredWindow(accountId: string): Promise<WhatsAppContact[]> {
    const now = new Date();
    return this.contactRepository.find({
      where: {
        accountId,
        conversationWindowExpiresAt: LessThan(now),
        canSendTemplateOnly: false,
      },
    });
  }
}
