import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TwilioWebhookEvent, TwilioSmsEventType } from './entities/twilio-webhook-event.entity';
import { TwilioWebhookEventDto } from './dto/webhook-event.dto';

@Injectable()
export class TwilioWebhookService {
  private readonly logger = new Logger(TwilioWebhookService.name);

  constructor(
    @InjectRepository(TwilioWebhookEvent)
    private readonly webhookEventRepository: Repository<TwilioWebhookEvent>,
  ) {}

  /**
   * Traite et enregistre un événement webhook reçu de Twilio
   */
  async processWebhookEvent(eventData: TwilioWebhookEventDto | Record<string, unknown>): Promise<TwilioWebhookEvent> {
    try {
      // Normaliser les données selon le format Twilio
      const normalizedData = this.normalizeEventData(eventData);

      // Mapper le statut vers un enum valide
      const eventType = this.mapEventType(normalizedData.status);

      const webhookEvent = this.webhookEventRepository.create({
        messageSid: normalizedData.messageSid,
        event: eventType,
        phoneNumber: normalizedData.to,
        accountSid: normalizedData.accountSid,
        status: normalizedData.status,
        errorCode: normalizedData.errorCode,
        errorMessage: normalizedData.errorMessage,
        from: normalizedData.from,
        to: normalizedData.to,
        body: normalizedData.body,
        rawData: eventData as Record<string, unknown>,
      });

      const savedEvent = await this.webhookEventRepository.save(webhookEvent);

      // Log amélioré avec plus de détails
      this.logger.log(
        `📱 Twilio SMS Webhook Event: ${savedEvent.event.toUpperCase()} | ` +
        `MessageSid: ${savedEvent.messageSid || 'unknown'} | ` +
        `Phone: ${savedEvent.phoneNumber || 'N/A'} | ` +
        `Status: ${savedEvent.status || 'N/A'}` +
        (savedEvent.errorCode ? ` | ErrorCode: ${savedEvent.errorCode}` : '') +
        (savedEvent.errorMessage ? ` | Error: ${savedEvent.errorMessage}` : ''),
      );

      return savedEvent;
    } catch (error) {
      this.logger.error('Failed to process Twilio webhook event:', error);
      throw error;
    }
  }

  /**
   * Mappe un statut Twilio vers un TwilioSmsEventType valide
   */
  private mapEventType(status: string): TwilioSmsEventType {
    const normalized = status?.toLowerCase().trim();

    // Mapping des statuts Twilio
    const eventMap: Record<string, TwilioSmsEventType> = {
      queued: TwilioSmsEventType.QUEUED,
      sent: TwilioSmsEventType.SENT,
      delivered: TwilioSmsEventType.DELIVERED,
      failed: TwilioSmsEventType.FAILED,
      undelivered: TwilioSmsEventType.UNDELIVERED,
      receiving: TwilioSmsEventType.RECEIVING,
      received: TwilioSmsEventType.RECEIVED,
      read: TwilioSmsEventType.READ,
    };

    return eventMap[normalized] || (normalized as TwilioSmsEventType) || TwilioSmsEventType.QUEUED;
  }

  /**
   * Normalise les données d'événement selon le format Twilio
   * Twilio envoie les données en format application/x-www-form-urlencoded
   */
  private normalizeEventData(data: TwilioWebhookEventDto | Record<string, unknown>): {
    messageSid?: string;
    status: string;
    accountSid?: string;
    from?: string;
    to?: string;
    body?: string;
    errorCode?: string;
    errorMessage?: string;
  } {
    const rawData = data as Record<string, unknown>;

    // Twilio envoie les données avec des clés en PascalCase
    const normalized = {
      messageSid:
        (data as TwilioWebhookEventDto).MessageSid ||
        (rawData.MessageSid as string) ||
        (rawData.message_sid as string) ||
        undefined,
      status:
        (data as TwilioWebhookEventDto).MessageStatus ||
        (rawData.MessageStatus as string) ||
        (rawData.message_status as string) ||
        (rawData.status as string) ||
        '',
      accountSid:
        (data as TwilioWebhookEventDto).AccountSid ||
        (rawData.AccountSid as string) ||
        (rawData.account_sid as string) ||
        undefined,
      from:
        (data as TwilioWebhookEventDto).From ||
        (rawData.From as string) ||
        (rawData.from as string) ||
        undefined,
      to:
        (data as TwilioWebhookEventDto).To ||
        (rawData.To as string) ||
        (rawData.to as string) ||
        undefined,
      body:
        (data as TwilioWebhookEventDto).Body ||
        (rawData.Body as string) ||
        (rawData.body as string) ||
        undefined,
      errorCode:
        (data as TwilioWebhookEventDto).ErrorCode ||
        (rawData.ErrorCode as string) ||
        (rawData.error_code as string) ||
        undefined,
      errorMessage:
        (data as TwilioWebhookEventDto).ErrorMessage ||
        (rawData.ErrorMessage as string) ||
        (rawData.error_message as string) ||
        undefined,
    };

    return normalized;
  }

  /**
   * Récupère tous les événements webhook
   */
  async getAllEvents(limit: number = 100, offset: number = 0): Promise<TwilioWebhookEvent[]> {
    return this.webhookEventRepository.find({
      order: { createdAt: 'DESC' },
      take: limit,
      skip: offset,
    });
  }

  /**
   * Récupère les événements pour un messageSid spécifique
   */
  async getEventsByMessageSid(messageSid: string): Promise<TwilioWebhookEvent[]> {
    return this.webhookEventRepository.find({
      where: { messageSid },
      order: { createdAt: 'ASC' },
    });
  }

  /**
   * Récupère les événements par type
   */
  async getEventsByType(eventType: TwilioSmsEventType, limit: number = 100): Promise<TwilioWebhookEvent[]> {
    return this.webhookEventRepository.find({
      where: { event: eventType },
      order: { createdAt: 'DESC' },
      take: limit,
    });
  }

  /**
   * Récupère les événements par numéro de téléphone
   */
  async getEventsByPhoneNumber(phoneNumber: string, limit: number = 100): Promise<TwilioWebhookEvent[]> {
    return this.webhookEventRepository.find({
      where: { phoneNumber },
      order: { createdAt: 'DESC' },
      take: limit,
    });
  }
}

