import { DataTypes } from 'sequelize';
import { Migration } from '../migrate';

export const up: Migration = async ({ context }) => {
  const isColumnInteger = async (tableName: string, columnName: string): Promise<boolean> => {
    const [results] = await context.sequelize.query(`PRAGMA table_info(${tableName})`);
    const columnInfo = (results as any[]).find((col: any) => col.name === columnName);
    return columnInfo && columnInfo.type.toUpperCase() === 'INTEGER';
  };

  const migrateDateToInteger = async (
    tableName: string,
    columnName: string,
    options: { allowNull: boolean; convertAllRows?: boolean } = { allowNull: true }
  ) => {
    if (await isColumnInteger(tableName, columnName)) {
      return;
    }

    const tempColumnName = `${columnName}_new`;

    await context.addColumn(tableName, tempColumnName, {
      type: DataTypes.INTEGER,
      allowNull: true,
    });

    const updateQuery = options.convertAllRows
      ? `UPDATE ${tableName} SET ${tempColumnName} = strftime('%s', ${columnName})`
      : `UPDATE ${tableName} SET ${tempColumnName} = strftime('%s', ${columnName}) WHERE ${columnName} IS NOT NULL`;

    await context.sequelize.query(updateQuery);

    // Replace old column
    await context.removeColumn(tableName, columnName);
    await context.renameColumn(tableName, tempColumnName, columnName);

    // Apply final column type and constraints
    await context.changeColumn(tableName, columnName, {
      type: DataTypes.INTEGER,
      allowNull: options.allowNull,
    });
  };

  // Define columns to migrate
  const migrations = [
    { table: 'promotion_codes', column: 'expires_at', allowNull: true },
    { table: 'coupons', column: 'redeem_by', allowNull: true },
    { table: 'discounts', column: 'start', allowNull: false, convertAllRows: true },
    { table: 'discounts', column: 'end', allowNull: true },
  ];

  // Execute migrations
  for (const migration of migrations) {
    // eslint-disable-next-line no-await-in-loop
    await migrateDateToInteger(migration.table, migration.column, {
      allowNull: migration.allowNull,
      convertAllRows: migration.convertAllRows,
    });
  }
};

export const down: Migration = async ({ context }) => {
  const isColumnDate = async (tableName: string, columnName: string): Promise<boolean> => {
    const [results] = await context.sequelize.query(`PRAGMA table_info(${tableName})`);
    const columnInfo = (results as any[]).find((col: any) => col.name === columnName);
    return columnInfo && (columnInfo.type.toUpperCase() === 'DATE' || columnInfo.type.toUpperCase() === 'DATETIME');
  };

  const migrateIntegerToDate = async (
    tableName: string,
    columnName: string,
    options: { allowNull: boolean; convertAllRows?: boolean } = { allowNull: true }
  ) => {
    if (await isColumnDate(tableName, columnName)) {
      return;
    }

    const tempColumnName = `${columnName}_old`;

    // Add temporary DATE column
    await context.addColumn(tableName, tempColumnName, {
      type: DataTypes.DATE,
      allowNull: true,
    });

    const updateQuery = options.convertAllRows
      ? `UPDATE ${tableName} SET ${tempColumnName} = datetime(${columnName}, 'unixepoch')`
      : `UPDATE ${tableName} SET ${tempColumnName} = datetime(${columnName}, 'unixepoch') WHERE ${columnName} IS NOT NULL`;

    await context.sequelize.query(updateQuery);

    // Replace old column
    await context.removeColumn(tableName, columnName);
    await context.renameColumn(tableName, tempColumnName, columnName);

    await context.changeColumn(tableName, columnName, {
      type: DataTypes.DATE,
      allowNull: options.allowNull,
    });
  };

  // Define columns to rollback (reverse order)
  const rollbacks = [
    { table: 'discounts', column: 'end', allowNull: true },
    { table: 'discounts', column: 'start', allowNull: false, convertAllRows: true },
    { table: 'coupons', column: 'redeem_by', allowNull: true },
    { table: 'promotion_codes', column: 'expires_at', allowNull: true },
  ];
  for (const rollback of rollbacks) {
    // eslint-disable-next-line no-await-in-loop
    await migrateIntegerToDate(rollback.table, rollback.column, {
      allowNull: rollback.allowNull,
      convertAllRows: rollback.convertAllRows,
    });
  }
};
