#!/usr/bin/env node
/**
 * One-shot, idempotent migration for Task #121.
 *
 * Adds the `executive_meetings.access` permission, grants it to admin + all
 * executive_* roles, and links it to the `executive-meetings` app via
 * `app_permissions` so the home-screen icon is hidden from users who don't
 * have an executive role.
 *
 * Safe to run multiple times — every insert uses ON CONFLICT DO NOTHING.
 *
 * Usage (from repo root):
 *   DATABASE_URL=... node artifacts/api-server/scripts/gate-executive-meetings.mjs
 */
import pg from "pg";

const DATABASE_URL = process.env.DATABASE_URL;
if (!DATABASE_URL) {
  console.error("DATABASE_URL must be set");
  process.exit(1);
}

const pool = new pg.Pool({ connectionString: DATABASE_URL });

const EXEC_ROLE_NAMES = [
  "admin",
  "executive_ceo",
  "executive_office_manager",
  "executive_coord_lead",
  "executive_coordinator",
  "executive_viewer",
];

async function main() {
  const client = await pool.connect();
  try {
    await client.query("BEGIN");

    // 1) Permission row.
    await client.query(
      `INSERT INTO permissions (name, description_ar, description_en)
       VALUES ($1, $2, $3)
       ON CONFLICT (name) DO NOTHING`,
      [
        "executive_meetings.access",
        "الوصول إلى وحدة الاجتماعات التنفيذية",
        "Access the Executive Meetings module",
      ],
    );

    const permRes = await client.query(
      `SELECT id FROM permissions WHERE name = $1`,
      ["executive_meetings.access"],
    );
    if (permRes.rowCount === 0) {
      throw new Error("Failed to find executive_meetings.access permission");
    }
    const permId = permRes.rows[0].id;

    // 2) Role grants.
    const grantRes = await client.query(
      `INSERT INTO role_permissions (role_id, permission_id)
       SELECT r.id, $1 FROM roles r WHERE r.name = ANY($2::text[])
       ON CONFLICT DO NOTHING
       RETURNING role_id`,
      [permId, EXEC_ROLE_NAMES],
    );

    // 3) App link.
    const appRes = await client.query(
      `SELECT id FROM apps WHERE slug = $1`,
      ["executive-meetings"],
    );
    if (appRes.rowCount === 0) {
      throw new Error(
        "executive-meetings app not found in the apps table; aborting",
      );
    }
    const appId = appRes.rows[0].id;
    const appLinkRes = await client.query(
      `INSERT INTO app_permissions (app_id, permission_id)
       VALUES ($1, $2)
       ON CONFLICT DO NOTHING
       RETURNING app_id`,
      [appId, permId],
    );

    await client.query("COMMIT");

    // Final state for the operator.
    const totalGrants = await client.query(
      `SELECT COUNT(*)::int AS c
         FROM role_permissions rp
         JOIN roles r ON r.id = rp.role_id
        WHERE rp.permission_id = $1 AND r.name = ANY($2::text[])`,
      [permId, EXEC_ROLE_NAMES],
    );
    const totalLinks = await client.query(
      `SELECT COUNT(*)::int AS c
         FROM app_permissions
        WHERE permission_id = $1`,
      [permId],
    );

    console.log(
      JSON.stringify(
        {
          ok: true,
          permissionId: permId,
          executiveAppId: appId,
          newRoleGrantsThisRun: grantRes.rowCount ?? 0,
          newAppLinksThisRun: appLinkRes.rowCount ?? 0,
          executiveRoleGrantsTotal: totalGrants.rows[0].c,
          appPermissionLinksTotal: totalLinks.rows[0].c,
        },
        null,
        2,
      ),
    );
  } catch (err) {
    await client.query("ROLLBACK").catch(() => {});
    throw err;
  } finally {
    client.release();
    await pool.end();
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
