/* eslint-disable no-console */
/* eslint-disable no-await-in-loop */
import type { QueryInterface } from 'sequelize';
import { SequelizeStorage, Umzug } from 'umzug';

import logger from '../libs/logger';
import { sequelize } from './sequelize';

const umzug = new Umzug({
  migrations: {
    glob: ['**/migrations/*.{ts,js}', { cwd: __dirname }],
    resolve: ({ name, path, context }) => {
      // eslint-disable-next-line import/no-dynamic-require, global-require
      const migration = require(path!);
      return {
        name: name.replace(/\.ts$/, '.js'),
        up: () => migration.up({ context }),
        down: () => migration.down({ context }),
      };
    },
  },
  context: sequelize.getQueryInterface(),
  storage: new SequelizeStorage({ sequelize }),
  logger,
});

export default function migrate() {
  return umzug.up();
}

type ColumnChanges = Record<string, { name: string; field: any }[]>;
export async function safeApplyColumnChanges(context: QueryInterface, changes: ColumnChanges) {
  for (const [table, columns] of Object.entries(changes)) {
    const schema = await context.describeTable(table);
    for (const { name, field } of columns) {
      if (!schema[name]) {
        await context.addColumn(table, name, field);
        console.info('safeApplyColumnChanges.addColumn', { table, name, field });
        if (field.defaultValue) {
          await context.bulkUpdate(table, { [name]: field.defaultValue }, {});
        }
      } else {
        console.info('safeApplyColumnChanges.skip', { table, name, field });
      }
    }
  }
}

const indexExists = async (table: string, indexName: string, queryInterface: QueryInterface) => {
  const indexes = await queryInterface.showIndex(table);
  return indexes && Array.isArray(indexes) && indexes.some((index: { name: string }) => index.name === indexName);
};

export const createIndexIfNotExists = async (
  queryInterface: QueryInterface,
  table: string,
  columns: string[],
  indexName: string,
  extra?: any
) => {
  if (await indexExists(table, indexName, queryInterface)) {
    /* eslint-disable no-console */
    console.log(`Index ${indexName} already exists on ${table}, skipping...`);
    return;
  }
  await queryInterface.addIndex(table, columns, { name: indexName, ...(extra || {}) });
};

export type Migration = typeof umzug._types.migration;
