
import "dotenv/config";
import { initializeApp, cert, App, ServiceAccount } from "firebase-admin/app";
import { getMessaging, Message } from "firebase-admin/messaging";
import path from "path";

// --- KONFIGURASI ---
const CONFIG = {
  /**
   * Konten notifikasi default yang akan dikirim.
   */
  notificationPayload: {
    title: "Sistem Desa Mandiri",
    body: "Ada informasi baru untuk Anda, silakan periksa aplikasi.",
  },

  /**
   * Pengaturan untuk mekanisme coba lagi (retry) jika terjadi kegagalan jaringan.
   */
  retry: {
    maxRetries: 3, // Jumlah maksimal percobaan ulang
    delay: 2000,   // Waktu tunda antar percobaan dalam milidetik (ms)
  },
};
// --- AKHIR KONFIGURASI ---

/**
 * Membangun objek service account dari variabel lingkungan.
 */
function getServiceAccount(): ServiceAccount {
  const privateKey = process.env.GOOGLE_PRIVATE_KEY?.replace(/\n/g, '\n');

  if (!process.env.GOOGLE_PROJECT_ID || !process.env.GOOGLE_CLIENT_EMAIL || !privateKey) {
    console.error("KRITIS: Variabel lingkungan Firebase (PROJECT_ID, CLIENT_EMAIL, PRIVATE_KEY) tidak lengkap.");
    process.exit(1);
  }

  return {
    projectId: process.env.GOOGLE_PROJECT_ID,
    clientEmail: process.env.GOOGLE_CLIENT_EMAIL,
    privateKey,
  };
}

/**
 * Inisialisasi Firebase Admin SDK.
 * Hanya akan menginisialisasi satu kali.
 */
let firebaseApp: App | null = null;
function initializeFirebase(): App {
  if (firebaseApp) {
    return firebaseApp;
  }
  try {
    const serviceAccount = getServiceAccount();
    firebaseApp = initializeApp({
      credential: cert(serviceAccount),
    });
    console.log("Firebase Admin SDK berhasil diinisialisasi.");
    return firebaseApp;
  } catch (error: any) {
    console.error("KRITIS: Gagal inisialisasi Firebase. Pastikan variabel lingkungan sudah benar.");
    console.error(`Detail Error: ${error.message}`);
    process.exit(1); // Keluar dari proses jika Firebase gagal diinisialisasi
  }
}

/**
 * Mengambil daftar token perangkat dari database.
 * TODO: Ganti fungsi ini dengan logika untuk mengambil token dari database Anda.
 * @returns {Promise<string[]>} Daftar token FCM.
 */
async function getDeviceTokens(): Promise<string[]> {
  console.log("Mengambil token perangkat dari sumber data...");
  // CONTOH: Ini adalah data dummy. Seharusnya Anda mengambilnya dari database.
  // Misalnya: const users = await prisma.user.findMany({ where: { fcmToken: { not: null } } });
  // return users.map(user => user.fcmToken);
  const exampleTokens: string[] = [
    "c89yuexsS_uc1tOErVPu5a:APA91bEb6tEKXAfReZjFVJ2mMyOzoW_RXryLSnSJTpbIVV3G0L_DCNkLuRvJ02Ip-Erz88QCQBAt-C2SN8eCRxu3-v1sBzXzKPtDv-huXpkjXsyrkifqvUo", // Valid
    "cRz96GHKTRaQaRJ35e8Hxa:APA91bEUSxE0VPbqKSzseQ_zGhbYsDofMexKykRw7o_3z2aPM9YFmZbeA2enrmb3qjdZ2g4-QQtiNHAyaZqAT1ITOrwo9jVJlShTeABmEFYP5GLEUZ3dlLc", // Valid
    "token_tidak_valid_ini_pasti_gagal_12345", // Contoh token tidak valid
  ];
  console.log(`Berhasil mendapatkan ${exampleTokens.length} token.`);
  return exampleTokens;
}

/**
 * Membuat array pesan yang akan dikirim ke setiap token.
 * @param {string[]} tokens - Daftar token FCM.
 * @returns {Message[]} Array objek pesan untuk `sendEach`.
 */
function constructMessages(tokens: string[]): Message[] {
  return tokens.map((token) => ({
    token,
    notification: {
      title: CONFIG.notificationPayload.title,
      body: CONFIG.notificationPayload.body,
    },
    data: {
      // Anda bisa menambahkan data tambahan di sini
      // Contoh: click_action: "FLUTTER_NOTIFICATION_CLICK"
      timestamp: String(Date.now()),
    },
    android: {
      priority: "high",
      notification: {
        sound: "default",
        channelId: "default_channel", // Pastikan channel ini ada di aplikasi Android Anda
      },
    },
    apns: {
      payload: {
        aps: {
          sound: "default",
        },
      },
    },
  }));
}

/**
 * Menangani respons dari `sendEach` untuk mencatat keberhasilan dan kegagalan.
 * @param response - Objek respons dari `sendEach`.
 * @param originalTokens - Daftar token asli yang dikirimi pesan.
 */
function handleSendResponse(response: any, originalTokens: string[]) {
  console.log("Total notifikasi berhasil dikirim:", response.successCount);
  console.log("Total notifikasi gagal:", response.failureCount);

  const tokensToRemove: string[] = [];

  response.responses.forEach((resp: any, index: number) => {
    const token = originalTokens[index];
    if (resp.success) {
      // console.log(`Pesan ke token ...${token.slice(-6)} berhasil:`, resp.messageId);
    } else {
      console.error(`Pesan ke token ...${token.slice(-6)} GAGAL:`, resp.error.code);
      // Jika token tidak lagi terdaftar, tandai untuk dihapus
      if (
        resp.error.code === "messaging/registration-token-not-registered" ||
        resp.error.code === "messaging/invalid-registration-token"
      ) {
        tokensToRemove.push(token);
      }
    }
  });

  if (tokensToRemove.length > 0) {
    console.warn("Token berikut tidak valid dan harus dihapus dari database:");
    tokensToRemove.forEach(token => console.log(`- ${token}`));
    // TODO: Implementasikan logika untuk menghapus token-token di atas dari database Anda.
    // Misalnya: await prisma.user.updateMany({ where: { fcmToken: { in: tokensToRemove } }, data: { fcmToken: null } });
  }
}

/**
 * Fungsi utama untuk mengirim notifikasi ke banyak perangkat dengan mekanisme retry.
 * @param {Message[]} messages - Array pesan yang akan dikirim.
 * @param {string[]} tokens - Daftar token asli untuk logging.
 */
async function sendNotifications(messages: Message[], tokens: string[]) {
  let lastError: any;

  for (let attempt = 1; attempt <= CONFIG.retry.maxRetries; attempt++) {
    try {
      const response = await getMessaging().sendEach(messages);
      handleSendResponse(response, tokens);
      return; // Berhasil, keluar dari fungsi
    } catch (error: any) {
      lastError = error;
      console.error(`Percobaan pengiriman ke-${attempt} gagal:`, error.message);

      // Hanya coba lagi jika error berhubungan dengan jaringan
      const isNetworkError = error.code === "app/network-error" || error.code?.includes("network");
      if (isNetworkError && attempt < CONFIG.retry.maxRetries) {
        console.log(`Menunggu ${CONFIG.retry.delay}ms sebelum mencoba lagi...`);
        await new Promise((resolve) => setTimeout(resolve, CONFIG.retry.delay));
      } else {
        // Jika bukan error jaringan atau sudah mencapai batas retry, lempar error
        throw new Error(`Gagal mengirim notifikasi setelah ${attempt} percobaan: ${error.message}`);
      }
    }
  }
  throw lastError;
}

/**
 * Fungsi orchestrator untuk menjalankan seluruh proses.
 */
export async function sendMultiple() {
  try {
    initializeFirebase();

    const tokens = await getDeviceTokens();
    if (tokens.length === 0) {
      console.log("Tidak ada token perangkat yang ditemukan. Proses dihentikan.");
      return;
    }

    const messages = constructMessages(tokens);

    console.log(`
Mempersiapkan pengiriman ${messages.length} notifikasi...`);
    await sendNotifications(messages, tokens);

    console.log("Proses pengiriman notifikasi selesai.");
  } catch (error: any) {
    console.error("Terjadi error fatal dalam proses pengiriman:", error.message);
    process.exit(1);
  }
}

sendMultiple();

