/**
 * FCM Service Account key generation via gcloud CLI.
 *
 * Finds the Firebase Admin SDK service account (firebase-adminsdk-*@project.iam.gserviceaccount.com)
 * and generates a new private key JSON. This is equivalent to:
 *   Firebase Console → Project Settings → Service Accounts → "Generate new private key"
 *
 * Used by Supabase and API backends only.
 * Firebase backend uses Application Default Credentials (ADC) automatically — no key needed.
 */

const { exec } = require('node:child_process');
const { promisify } = require('node:util');
const fs = require('fs-extra');
const path = require('node:path');
const os = require('node:os');

const execAsync = promisify(exec);

async function run(cmd) {
  try {
    const { stdout, stderr } = await execAsync(cmd, { maxBuffer: 5 * 1024 * 1024 });
    return { ok: true, stdout: stdout.trim(), stderr: stderr.trim() };
  } catch (err) {
    return {
      ok: false,
      error: err.message,
      stdout: (err.stdout || '').trim(),
      stderr: (err.stderr || '').trim(),
    };
  }
}

/**
 * Find the Firebase Admin SDK service account email for a GCP project.
 * Firebase creates this account automatically when Firebase is added to a project.
 * Email pattern: firebase-adminsdk-XXXXX@{projectId}.iam.gserviceaccount.com
 *
 * @param {string} projectId
 * @returns {{ ok: boolean, email?: string, error?: string }}
 */
async function findFirebaseAdminSdkSA(projectId) {
  const result = await run(
    `gcloud iam service-accounts list --project=${projectId}` +
    ` --filter="email~firebase-adminsdk"` +
    ` --format="value(email)" --limit=1`
  );
  if (!result.ok) return { ok: false, error: result.error };
  const email = result.stdout.split('\n')[0].trim();
  if (!email) return { ok: false, error: 'Firebase Admin SDK service account not found' };
  return { ok: true, email };
}

/**
 * Grants the roles/firebasecloudmessaging.admin IAM role to the Firebase Admin SDK
 * service account. Required for the FCM HTTP v1 API to accept messages.send requests.
 * Safe to call multiple times (gcloud is idempotent for IAM bindings).
 *
 * @param {string} projectId
 * @param {string} saEmail
 * @returns {{ ok: boolean, error?: string }}
 */
async function grantFcmAdminRole(projectId, saEmail) {
  const result = await run(
    `gcloud projects add-iam-policy-binding ${projectId}` +
    ` --member="serviceAccount:${saEmail}"` +
    ` --role="roles/firebasecloudmessaging.admin"` +
    ` --condition=None`
  );
  if (!result.ok) {
    return { ok: false, error: `Failed to grant FCM admin role: ${result.error}` };
  }
  return { ok: true };
}

/**
 * Generates a new private key for the Firebase Admin SDK service account and returns
 * the JSON content as a string. The temporary key file is deleted immediately after reading.
 * Also ensures the service account has the roles/firebasecloudmessaging.admin IAM role.
 *
 * @param {string} projectId - Firebase/GCP project ID
 * @returns {{ ok: boolean, json?: string, saEmail?: string, error?: string }}
 */
async function createFcmServiceAccountKey(projectId) {
  if (!projectId || !projectId.trim()) {
    return { ok: false, error: 'projectId is required' };
  }

  const saResult = await findFirebaseAdminSdkSA(projectId.trim());
  if (!saResult.ok) {
    return { ok: false, error: saResult.error };
  }

  const saEmail = saResult.email;

  // Ensure the service account can send FCM messages (idempotent)
  const roleResult = await grantFcmAdminRole(projectId.trim(), saEmail);
  if (!roleResult.ok) {
    return { ok: false, error: roleResult.error };
  }

  const tmpPath = path.join(os.tmpdir(), `kasy-fcm-${Date.now()}.json`);

  const keyResult = await run(
    `gcloud iam service-accounts keys create "${tmpPath}"` +
    ` --iam-account="${saEmail}"` +
    ` --project=${projectId.trim()}`
  );

  if (!keyResult.ok) {
    await fs.remove(tmpPath).catch(() => {});
    return { ok: false, error: `Failed to generate key: ${keyResult.error}` };
  }

  try {
    const jsonContent = await fs.readFile(tmpPath, 'utf8');
    await fs.remove(tmpPath).catch(() => {});
    JSON.parse(jsonContent); // validate
    return { ok: true, json: jsonContent.trim(), saEmail };
  } catch (err) {
    await fs.remove(tmpPath).catch(() => {});
    return { ok: false, error: `Failed to read generated key: ${err.message}` };
  }
}

module.exports = { createFcmServiceAccountKey };
