import admin from 'firebase-admin';
import * as path from 'path';
import {
  PushNotification,
  NotificationStatus,
  NotificationPriority,
  NotificationRecipient,
  NotificationChannel,
} from '../types/notification.types';
import { FCMConfig, APNsConfig } from '../config/notification.config';

export class PushService {
  private fcm?: admin.app.App;
  private apnsConfig?: APNsConfig;

  constructor(fcmConfig?: FCMConfig, apnsConfig?: APNsConfig) {
    if (fcmConfig) {
      // Use named app instance for multi-tenant support
      const appName = fcmConfig.keyPath 
        ? `fcm_${fcmConfig.projectId}`
        : 'fcm_default';
      
      // Check if app with this name already exists
      const existingApp = admin.app(appName);
      
      if (!existingApp) {
        this.fcm = admin.initializeApp({
          credential: admin.credential.cert({
            projectId: fcmConfig.projectId,
            privateKey: fcmConfig.privateKey.replace(/\\n/g, '\n'),
            clientEmail: fcmConfig.clientEmail,
          }),
          ...(fcmConfig.keyPath && {
            storageBucket: `${fcmConfig.projectId}.appspot.com`,
          }),
        }, appName);
      } else {
        this.fcm = existingApp;
      }
    }

    this.apnsConfig = apnsConfig;
  }

  /**
   * Send push notification to FCM device
   */
  async sendFCMPush(
    recipient: NotificationRecipient,
    title: string,
    body: string,
    data?: Record<string, unknown>,
    badge?: number,
    sound?: string
  ): Promise<PushNotification> {
    const notification: PushNotification = {
      id: `push_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      userId: recipient.userId,
      channel: NotificationChannel.PUSH,
      templateId: 'custom',
      priority: NotificationPriority.NORMAL,
      status: NotificationStatus.PENDING,
      title,
      body,
      data,
      badge,
      sound,
      fcmToken: recipient.fcmToken,
      createdAt: new Date(),
    };

    if (!this.fcm || !recipient.fcmToken) {
      notification.status = NotificationStatus.FAILED;
      notification.failedAt = new Date();
      notification.errorMessage = !this.fcm ? 'FCM not configured' : 'Missing FCM token';
      return notification;
    }

    try {
      const message: admin.messaging.Message = {
        token: recipient.fcmToken,
        notification: {
          title,
          body,
        },
        data: data
          ? Object.fromEntries(
              Object.entries(data).map(([key, value]) => [key, String(value)])
            )
          : undefined,
        apns: {
          payload: {
            aps: {
              badge,
              sound: sound || 'default',
            },
          },
        },
        android: {
          priority: 'high',
          notification: {
            title,
            body,
            clickAction: 'FLUTTER_NOTIFICATION_CLICK',
          },
        },
      };

      const response = await this.fcm.messaging().send(message);

      notification.status = NotificationStatus.SENT;
      notification.sentAt = new Date();

      return notification;
    } catch (error) {
      notification.status = NotificationStatus.FAILED;
      notification.failedAt = new Date();
      notification.errorMessage = error instanceof Error ? error.message : 'Unknown error';
      return notification;
    }
  }

  /**
   * Send push notification using APNs
   */
  async sendAPNSPush(
    recipient: NotificationRecipient,
    title: string,
    body: string,
    data?: Record<string, unknown>,
    badge?: number
  ): Promise<PushNotification> {
    const notification: PushNotification = {
      id: `push_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      userId: recipient.userId,
      channel: NotificationChannel.PUSH,
      templateId: 'custom',
      priority: NotificationPriority.NORMAL,
      status: NotificationStatus.PENDING,
      title,
      body,
      data,
      badge,
      apnsToken: recipient.apnsToken,
      createdAt: new Date(),
    };

    if (!this.apnsConfig || !recipient.apnsToken) {
      notification.status = NotificationStatus.FAILED;
      notification.failedAt = new Date();
      notification.errorMessage = !this.apnsConfig ? 'APNs not configured' : 'Missing APNs token';
      return notification;
    }

    // FCM supports sending to APNs tokens (iOS devices)
    // This leverages FCM's unified push infrastructure for iOS
    // APNs token format: device-specific token from iOS
    if (this.fcm && recipient.apnsToken) {
      const message: admin.messaging.Message = {
        token: recipient.apnsToken,
        notification: {
          title,
          body,
        },
        data: data
          ? Object.fromEntries(
              Object.entries(data).map(([key, value]) => [key, String(value)])
            )
          : undefined,
        apns: {
          payload: {
            aps: {
              badge,
              sound: 'default',
              contentAvailable: true,
            },
          },
        },
      };

      try {
        await this.fcm.messaging().send(message);
        notification.status = NotificationStatus.SENT;
        notification.sentAt = new Date();
      } catch (error) {
        notification.status = NotificationStatus.FAILED;
        notification.failedAt = new Date();
        notification.errorMessage = error instanceof Error ? error.message : 'Unknown error';
      }
    }

    return notification;
  }

  /**
   * Send push notification (auto-detect platform)
   */
  async sendPush(
    recipient: NotificationRecipient,
    title: string,
    body: string,
    data?: Record<string, unknown>,
    badge?: number,
    sound?: string
  ): Promise<PushNotification> {
    // Prefer APNs for iOS tokens, FCM for Android
    if (recipient.apnsToken) {
      return this.sendAPNSPush(recipient, title, body, data, badge);
    } else if (recipient.fcmToken) {
      return this.sendFCMPush(recipient, title, body, data, badge, sound);
    } else {
      return {
        id: `push_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        userId: recipient.userId,
        channel: NotificationChannel.PUSH,
        templateId: 'custom',
        priority: NotificationPriority.NORMAL,
        status: NotificationStatus.FAILED,
        title,
        body,
        data,
        badge,
        sound,
        createdAt: new Date(),
        failedAt: new Date(),
        errorMessage: 'No push token available',
      };
    }
  }

  /**
   * Send broadcast push to multiple devices
   */
  async sendBroadcastPush(
    recipients: Array<NotificationRecipient>,
    title: string,
    body: string,
    data?: Record<string, unknown>
  ): Promise<PushNotification[]> {
    const promises = recipients.map((recipient) =>
      this.sendPush(recipient, title, body, data)
    );

    return Promise.all(promises);
  }

  /**
   * Check if FCM is properly configured
   */
  isFCMConfigured(): boolean {
    return !!this.fcm;
  }

/**
    * Shutdown FCM app
    */
    async shutdown(): Promise<void> {
      if (this.fcm) {
        await this.fcm.delete();
      }
    }
}
