'use strict';

const { v4: uuid } = require('uuid');
const { QueryTypes } = require('sequelize');

const BUSINESS_ROLES = {
  SUPER_ADMIN: 'Super Administrator',
  ADMIN: 'Administrator',
  CONCIERGE: 'Concierge Coordinator',
  CUSTOMER: 'Customer',
  PLATFORM_OWNER: 'Platform Owner',
  OPERATIONS_DIRECTOR: 'Operations Director',
  RESERVATIONS_LEAD: 'Reservations Lead',
  FINANCE_CONTROLLER: 'Finance Controller',
};

const rolePermissionMatrix = {
  [BUSINESS_ROLES.ADMIN]: [
    'CREATE_BOOKING_REQUESTS',
    'READ_BOOKING_REQUESTS',
    'UPDATE_BOOKING_REQUESTS',
    'READ_APPROVAL_STEPS',
    'UPDATE_APPROVAL_STEPS',
    'CREATE_RESERVATIONS',
    'READ_RESERVATIONS',
    'UPDATE_RESERVATIONS',
    'CREATE_SERVICE_REQUESTS',
    'READ_SERVICE_REQUESTS',
    'UPDATE_SERVICE_REQUESTS',
    'READ_INVOICES',
    'CREATE_DOCUMENTS',
    'READ_DOCUMENTS',
    'UPDATE_DOCUMENTS',
    'READ_PROPERTIES',
    'READ_UNITS',
    'READ_NEGOTIATED_RATES',
  ],
  [BUSINESS_ROLES.CONCIERGE]: [
    'CREATE_BOOKING_REQUESTS',
    'READ_BOOKING_REQUESTS',
    'UPDATE_BOOKING_REQUESTS',
    'READ_RESERVATIONS',
    'CREATE_SERVICE_REQUESTS',
    'READ_SERVICE_REQUESTS',
    'UPDATE_SERVICE_REQUESTS',
    'CREATE_DOCUMENTS',
    'READ_DOCUMENTS',
    'UPDATE_DOCUMENTS',
    'READ_PROPERTIES',
    'READ_UNITS',
  ],
  [BUSINESS_ROLES.CUSTOMER]: [
    'CREATE_BOOKING_REQUESTS',
    'READ_BOOKING_REQUESTS',
    'UPDATE_BOOKING_REQUESTS',
    'READ_RESERVATIONS',
    'CREATE_SERVICE_REQUESTS',
    'READ_SERVICE_REQUESTS',
    'UPDATE_SERVICE_REQUESTS',
    'READ_DOCUMENTS',
  ],
  [BUSINESS_ROLES.PLATFORM_OWNER]: [
    'CREATE_BOOKING_REQUESTS',
    'READ_BOOKING_REQUESTS',
    'UPDATE_BOOKING_REQUESTS',
    'READ_APPROVAL_STEPS',
    'UPDATE_APPROVAL_STEPS',
    'CREATE_RESERVATIONS',
    'READ_RESERVATIONS',
    'UPDATE_RESERVATIONS',
    'CREATE_SERVICE_REQUESTS',
    'READ_SERVICE_REQUESTS',
    'UPDATE_SERVICE_REQUESTS',
    'READ_INVOICES',
    'CREATE_DOCUMENTS',
    'READ_DOCUMENTS',
    'UPDATE_DOCUMENTS',
    'READ_ORGANIZATIONS',
    'READ_PROPERTIES',
    'READ_UNITS',
    'READ_NEGOTIATED_RATES',
  ],
  [BUSINESS_ROLES.OPERATIONS_DIRECTOR]: [
    'CREATE_BOOKING_REQUESTS',
    'READ_BOOKING_REQUESTS',
    'UPDATE_BOOKING_REQUESTS',
    'READ_APPROVAL_STEPS',
    'UPDATE_APPROVAL_STEPS',
    'CREATE_RESERVATIONS',
    'READ_RESERVATIONS',
    'UPDATE_RESERVATIONS',
    'CREATE_SERVICE_REQUESTS',
    'READ_SERVICE_REQUESTS',
    'UPDATE_SERVICE_REQUESTS',
    'CREATE_DOCUMENTS',
    'READ_DOCUMENTS',
    'UPDATE_DOCUMENTS',
    'READ_PROPERTIES',
    'READ_UNITS',
    'READ_NEGOTIATED_RATES',
  ],
  [BUSINESS_ROLES.RESERVATIONS_LEAD]: [
    'CREATE_BOOKING_REQUESTS',
    'READ_BOOKING_REQUESTS',
    'UPDATE_BOOKING_REQUESTS',
    'READ_APPROVAL_STEPS',
    'UPDATE_APPROVAL_STEPS',
    'CREATE_RESERVATIONS',
    'READ_RESERVATIONS',
    'UPDATE_RESERVATIONS',
    'CREATE_SERVICE_REQUESTS',
    'READ_SERVICE_REQUESTS',
    'UPDATE_SERVICE_REQUESTS',
    'READ_DOCUMENTS',
    'READ_PROPERTIES',
    'READ_UNITS',
    'READ_NEGOTIATED_RATES',
  ],
  [BUSINESS_ROLES.FINANCE_CONTROLLER]: [
    'READ_BOOKING_REQUESTS',
    'READ_RESERVATIONS',
    'READ_INVOICES',
    'UPDATE_INVOICES',
    'READ_DOCUMENTS',
  ],
};

async function findRoleByName(queryInterface, name) {
  const rows = await queryInterface.sequelize.query(
    'SELECT "id", "name" FROM "roles" WHERE "name" = :name LIMIT 1',
    {
      replacements: { name },
      type: QueryTypes.SELECT,
    },
  );

  return rows[0] || null;
}

async function ensureRole(queryInterface, name, now) {
  const existingRole = await findRoleByName(queryInterface, name);

  if (existingRole) {
    return existingRole.id;
  }

  const id = uuid();
  await queryInterface.bulkInsert('roles', [
    {
      id,
      name,
      globalAccess: false,
      createdAt: now,
      updatedAt: now,
    },
  ]);

  return id;
}

async function getPermissionIdMap(queryInterface, permissionNames) {
  const permissions = await queryInterface.sequelize.query(
    'SELECT "id", "name" FROM "permissions" WHERE "name" IN (:permissionNames)',
    {
      replacements: { permissionNames },
      type: QueryTypes.SELECT,
    },
  );

  return new Map(permissions.map((permission) => [permission.name, permission.id]));
}

module.exports = {
  async up(queryInterface) {
    const now = new Date();

    const customerRoleId = await ensureRole(queryInterface, BUSINESS_ROLES.CUSTOMER, now);

    await queryInterface.bulkUpdate('roles', { globalAccess: true, updatedAt: now }, { name: BUSINESS_ROLES.SUPER_ADMIN });
    await queryInterface.sequelize.query(
      'UPDATE "roles" SET "globalAccess" = false, "updatedAt" = :updatedAt WHERE "name" IN (:roleNames)',
      {
        replacements: {
          updatedAt: now,
          roleNames: [
            BUSINESS_ROLES.ADMIN,
            BUSINESS_ROLES.CONCIERGE,
            BUSINESS_ROLES.CUSTOMER,
            BUSINESS_ROLES.PLATFORM_OWNER,
            BUSINESS_ROLES.OPERATIONS_DIRECTOR,
            BUSINESS_ROLES.RESERVATIONS_LEAD,
            BUSINESS_ROLES.FINANCE_CONTROLLER,
          ],
        },
      },
    );

    const roleIds = {};
    for (const roleName of Object.keys(rolePermissionMatrix)) {
      const role = await findRoleByName(queryInterface, roleName);
      if (!role) {
        throw new Error(`Role '${roleName}' was not found while aligning the business role matrix.`);
      }
      roleIds[roleName] = role.id;
    }
    roleIds[BUSINESS_ROLES.CUSTOMER] = customerRoleId;

    const permissionNames = [...new Set(Object.values(rolePermissionMatrix).flat())];
    const permissionIdMap = await getPermissionIdMap(queryInterface, permissionNames);
    const missingPermissions = permissionNames.filter((permissionName) => !permissionIdMap.get(permissionName));

    if (missingPermissions.length > 0) {
      throw new Error(`Missing permissions for role matrix alignment: ${missingPermissions.join(', ')}`);
    }

    const impactedRoleIds = Object.values(roleIds);
    await queryInterface.sequelize.query(
      'DELETE FROM "rolesPermissionsPermissions" WHERE "roles_permissionsId" IN (:roleIds)',
      {
        replacements: { roleIds: impactedRoleIds },
      },
    );

    const rows = Object.entries(rolePermissionMatrix).flatMap(([roleName, permissions]) =>
      permissions.map((permissionName) => ({
        createdAt: now,
        updatedAt: now,
        roles_permissionsId: roleIds[roleName],
        permissionId: permissionIdMap.get(permissionName),
      })),
    );

    if (rows.length > 0) {
      await queryInterface.bulkInsert('rolesPermissionsPermissions', rows);
    }

    await queryInterface.bulkUpdate('users', { app_roleId: customerRoleId, updatedAt: now }, { email: 'client@hello.com' });
    await queryInterface.bulkUpdate('users', { app_roleId: roleIds[BUSINESS_ROLES.CONCIERGE], updatedAt: now }, { email: 'john@doe.com' });
  },

  async down() {
    // Intentionally left blank. This seeder aligns live business roles and should not blindly revert production data.
  },
};
