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

async function createUsersTable(query: { context: any }) {
  await query.context.createTable($db.tableName('users'), {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    username: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true
    },
    email: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false
    },
    firstName: {
      type: DataTypes.STRING,
    },
    lastName: {
      type: DataTypes.STRING,
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: () => new Date(),
    },
  });
}

async function createRolesTable(query: { context: any }) {
  await query.context.createTable($db.tableName('roles'), {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    name: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: () => new Date(),
    },
  });
}

async function createUsersRolesTable(query: { context: any }) {
  await query.context.createTable($db.tableName('users_roles'), {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    userId: {
      type: DataTypes.INTEGER,
      allowNull: false,
      unique: 'user_role_userId_roleId_unique',
      references: {
        model: $db.tableName('users'),
        key: 'id',
        onDelete: 'CASCADE',
        onUpdate: 'CASCADE',
      }
    },
    roleId: {
      type: DataTypes.INTEGER,
      allowNull: false,
      unique: 'user_role_userId_roleId_unique',
      references: {
        model: $db.tableName('roles'),
        key: 'id',
        onDelete: 'CASCADE',
        onUpdate: 'CASCADE',
      }
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: () => new Date(),
    },
  });
}

async function createUserSessionsTable(query: { context: any }) {
  await query.context.createTable($db.tableName('user_sessions'), {
    id: {
      type: DataTypes.UUID,
      primaryKey: true,
      allowNull: false,
      defaultValue: () => uuidv4(),
    },
    userId: {
      type: DataTypes.INTEGER,
      allowNull: false,
      unique: 'user_sessions_userId_name_unique',
      references: {
        model: $db.tableName('users'),
        key: 'id',
        onDelete: 'CASCADE',
        onUpdate: 'CASCADE',
      }
    },
    name: {
      type: DataTypes.STRING,
      allowNull: true,
      unique: 'user_sessions_userId_name_unique',
    },
    isApi: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
      defaultValue: false,
    },
    expiresAt: {
      type: DataTypes.DATE,
      allowNull: true,
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: () => new Date(),
    },
  });
}

async function createUserDevicesTable(query: { context: any }) {
  await query.context.createTable($db.tableName('user_devices'), {
    id: {
      type: DataTypes.UUID,
      primaryKey: true,
      defaultValue: () => uuidv4(),
    },
    userId: {
      type: DataTypes.INTEGER,
      allowNull: false,
      unique: 'user_device_userId_name_unique',
      references: {
        model: $db.tableName('users'),
        key: 'id',
        onDelete: 'CASCADE',
        onUpdate: 'CASCADE',
      }
    },
    name: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: 'user_device_userId_name_unique',
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: () => new Date(),
    },
  });
}

async function createLocationHistoryTable(query: { context: any }) {
  // Only create the table if locationUrl == appUrl (i.e. the location is stored
  // on the app managed database and not on an external one)
  if ($db.locationUrl !== $db.url) {
    console.log('The location history table is stored on an external database, skipping creation');
    return;
  }

  await query.context.createTable($db.locationTable, {
    [$db.locationTableColumns['id']]: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true,
      allowNull: false,
    },
    [$db.locationTableColumns['deviceId']]: {
      type: DataTypes.UUID,
      allowNull: false,
      references: {
        model: $db.tableName('user_devices'),
        key: 'id',
        onDelete: 'CASCADE',
        onUpdate: 'CASCADE',
      },
    },
    [$db.locationTableColumns['latitude']]: {
      type: DataTypes.FLOAT,
      allowNull: false
    },
    [$db.locationTableColumns['longitude']]: {
      type: DataTypes.FLOAT,
      allowNull: false
    },
    [$db.locationTableColumns['altitude']]: {
      type: DataTypes.FLOAT,
      allowNull: true
    },
    [$db.locationTableColumns['address']]: {
      type: DataTypes.STRING,
      allowNull: true
    },
    [$db.locationTableColumns['locality']]: {
      type: DataTypes.STRING,
      allowNull: true
    },
    [$db.locationTableColumns['country']]: {
      type: DataTypes.STRING,
      allowNull: true
    },
    [$db.locationTableColumns['postalCode']]: {
      type: DataTypes.STRING,
      allowNull: true
    },
    [$db.locationTableColumns['timestamp']]: {
      type: DataTypes.DATE,
      defaultValue: DataTypes.NOW,
      allowNull: false
    },
  })
}

async function up(query: { context: any }) {
  await createRolesTable({ context: query.context });
  await createUsersTable({ context: query.context });
  await createUserDevicesTable({ context: query.context });
  await createUsersRolesTable({ context: query.context });
  await createUserSessionsTable({ context: query.context });
  await createLocationHistoryTable({ context: query.context });
}

async function down(query: { context: any }) {
  await query.context.dropTable($db.tableName('users'));
  await query.context.dropTable($db.tableName('roles'));
  await query.context.dropTable($db.tableName('user_devices'));
  await query.context.dropTable($db.tableName('users_roles'));
  await query.context.dropTable($db.tableName('user_sessions'));

  if ($db.locationUrl !== $db.url) {
    console.log('The location history table is stored on an external database, skipping deletion');
    return;
  }

  await query.context.dropTable($db.locationTable);
}

module.exports = {
  up,
  down
}