/**
 * This file is part of the NocoBase (R) project.
 * Copyright (c) 2020-2024 NocoBase Co., Ltd.
 * Authors: NocoBase Team.
 *
 * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
 * For more information, please refer to: https://www.nocobase.com/agreement.
 */

import { Migration } from '@nocobase/server';
import _ from 'lodash';

export default class extends Migration {
  appVersion = '<1.6.0';
  async up() {
    const rolesDesktopRoutesRepo = this.db.getRepository('rolesDesktopRoutes');
    const count = await rolesDesktopRoutesRepo.count();
    if (!count) {
      return;
    }
    const desktopRoutesRepo = this.db.getRepository('desktopRoutes');

    try {
      await this.db.sequelize.transaction(async (transaction) => {
        const rolesDesktopRoutes = await rolesDesktopRoutesRepo.find({ transaction });
        const rolesDesktopRouteIds = rolesDesktopRoutes.map((x) => x.get('desktopRouteId'));
        const desktopRoutes = await desktopRoutesRepo.find({
          filter: { $or: [{ id: rolesDesktopRouteIds }, { parentId: rolesDesktopRouteIds }] },
          transaction,
        });
        const records = findMissingRoutes(rolesDesktopRoutes, desktopRoutes);
        await rolesDesktopRoutesRepo.createMany({ records, transaction });
      });
    } catch (error) {
      console.error('Migration failed:', error);
      throw error;
    }
  }
}

function findMissingRoutes(rolesDesktopRoutes, desktopRoutes) {
  const routeMap = _.keyBy(desktopRoutes, 'id');
  const group = (id, roleName) => `${id}_${roleName}`;
  const existingMap = new Set(rolesDesktopRoutes.map((x) => group(x.desktopRouteId, x.roleName)));

  const missingRoutes = [];
  rolesDesktopRoutes.forEach((route) => {
    const parentRoute = routeMap[route.desktopRouteId];

    desktopRoutes.forEach((child) => {
      if (child.hidden && child.parentId === parentRoute.id) {
        const key = group(child.id, route.roleName);
        if (!existingMap.has(key)) {
          missingRoutes.push({ desktopRouteId: child.id, roleName: route.roleName });
        }
      }
    });
  });

  return missingRoutes;
}
