import type { QueryInterface } from 'sequelize';
import { ROLE_NAMES } from '@/shared/constants/roles';

type NamedRow = {
  readonly id: string;
  readonly name: string;
};

function isNamedRow(value: unknown): value is NamedRow {
  return (
    value !== null
    && typeof value === 'object'
    && 'id' in value
    && typeof value.id === 'string'
    && 'name' in value
    && typeof value.name === 'string'
  );
}

function rowsOf(value: unknown): readonly unknown[] {
  return Array.isArray(value) ? value : [];
}

export default {
  async up(queryInterface: QueryInterface) {
    const now = new Date();
    const [roleRows] = await queryInterface.sequelize.query(
      `SELECT "id", "name" FROM "roles" WHERE "name" = :name LIMIT 1`,
      { replacements: { name: ROLE_NAMES.SYSTEM_ADMIN } },
    );
    const systemAdminRole = rowsOf(roleRows).find(isNamedRow);

    if (!systemAdminRole) {
      return;
    }

    const [permissionRows] = await queryInterface.sequelize.query(
      `SELECT "id", "name" FROM "permissions"`,
    );
    const permissions = rowsOf(permissionRows).filter(isNamedRow);

    if (permissions.length === 0) {
      return;
    }

    const [existingRows] = await queryInterface.sequelize.query(
      `SELECT "permissionId"
       FROM "rolesPermissionsPermissions"
       WHERE "roles_permissionsId" = :roleId`,
      { replacements: { roleId: systemAdminRole.id } },
    );
    const existingPermissionIds = new Set(
      rowsOf(existingRows)
        .map((row) =>
          row !== null
          && typeof row === 'object'
          && 'permissionId' in row
          && typeof row.permissionId === 'string'
            ? row.permissionId
            : null,
        )
        .filter((id): id is string => id !== null),
    );

    const links = permissions
      .filter((permission) => !existingPermissionIds.has(permission.id))
      .map((permission) => ({
        createdAt: now,
        updatedAt: now,
        roles_permissionsId: systemAdminRole.id,
        permissionId: permission.id,
      }));

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

  async down(queryInterface: QueryInterface) {
    const [roleRows] = await queryInterface.sequelize.query(
      `SELECT "id", "name" FROM "roles" WHERE "name" = :name LIMIT 1`,
      { replacements: { name: ROLE_NAMES.SYSTEM_ADMIN } },
    );
    const systemAdminRole = rowsOf(roleRows).find(isNamedRow);

    if (!systemAdminRole) {
      return;
    }

    await queryInterface.bulkDelete('rolesPermissionsPermissions', {
      roles_permissionsId: systemAdminRole.id,
    });
  },
};
