/*!
 * Copyright (c) 2024 PLANKA Software GmbH
 * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
 */

const mime = require('mime-types');

exports.up = async (knex) => {
  await knex.schema.createTable('storage_usage', (table) => {
    /* Columns */

    table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));

    table.bigInteger('total').notNullable();
    table.bigInteger('user_avatars').notNullable();
    table.bigInteger('background_images').notNullable();
    table.bigInteger('attachments').notNullable();

    table.timestamp('created_at', true);
    table.timestamp('updated_at', true);
  });

  await knex.schema.alterTable('file_reference', (table) => {
    table.dropPrimary();
    table.dropIndex('total');
  });

  await knex.schema.renameTable('file_reference', 'uploaded_file');

  await knex.schema.alterTable('uploaded_file', (table) => {
    /* Columns */

    table.text('type').notNullable().defaultTo('attachment');
    table.text('mime_type');
    table.bigInteger('size').notNullable().defaultTo(0);

    /* Modifications */

    table.text('id').primary().defaultTo(knex.raw('next_id()')).alter();
    table.renameColumn('total', 'references_total');

    /* Indexes */

    table.index('type');
    table.index('references_total');
  });

  await knex.schema.alterTable('uploaded_file', (table) => {
    table.text('type').notNullable().alter();
    table.bigInteger('size').notNullable().alter();
  });

  await knex.raw(`
    UPDATE user_account
    SET avatar = avatar - 'dirname' - 'sizeInBytes' || jsonb_build_object('uploadedFileId', avatar->'dirname', 'size', avatar->'sizeInBytes')
    WHERE avatar IS NOT NULL;
  `);

  await knex.schema.alterTable('background_image', (table) => {
    table.renameColumn('dirname', 'uploaded_file_id');
    table.renameColumn('size_in_bytes', 'size');
  });

  await knex.raw(`
    UPDATE attachment
    SET data = data - 'fileReferenceId' - 'sizeInBytes' || jsonb_build_object('uploadedFileId', data->'fileReferenceId', 'size', data->'sizeInBytes')
    WHERE type = 'file';
  `);

  await knex.raw(`
    UPDATE uploaded_file
    SET
      type = 'attachment',
      mime_type = attachment.data->>'mimeType',
      size = (attachment.data->>'size')::bigint
    FROM attachment
    WHERE (attachment.data->>'uploadedFileId')::text = uploaded_file.id AND attachment.type = 'file';
  `);

  const users = await knex('user_account').whereNotNull('avatar');
  const createdAt = new Date().toISOString();

  await knex.batchInsert(
    'uploaded_file',
    users.map(({ avatar }) => ({
      createdAt,
      id: avatar.uploadedFileId,
      type: 'userAvatar',
      referencesTotal: 1,
      mimeType: mime.lookup(avatar.extension) || null,
      size: avatar.size,
    })),
  );

  const backgroundImages = await knex('background_image');

  await knex.batchInsert(
    'uploaded_file',
    backgroundImages.map((backgroundImage) => ({
      id: backgroundImage.uploaded_file_id,
      type: 'backgroundImage',
      referencesTotal: 1,
      mimeType: mime.lookup(backgroundImage.extension) || null,
      size: backgroundImage.size,
      createdAt: backgroundImage.created_at,
    })),
  );

  return knex.raw(`
    INSERT INTO storage_usage (id, total, user_avatars, background_images, attachments, created_at)
    SELECT
      1 AS id,
      COALESCE(SUM(size), 0) AS total,
      COALESCE(SUM(CASE WHEN type = 'userAvatar' THEN size ELSE 0 END), 0) AS user_avatars,
      COALESCE(SUM(CASE WHEN type = 'backgroundImage' THEN size ELSE 0 END), 0) AS background_images,
      COALESCE(SUM(CASE WHEN type = 'attachment' THEN size ELSE 0 END), 0) AS attachments,
      timezone('UTC', now()) AS created_at
    FROM uploaded_file;
  `);
};

exports.down = async (knex) => {
  await knex.schema.dropTable('storage_usage');

  await knex('uploaded_file').delete().whereNot('type', 'attachment');

  await knex.schema.alterTable('uploaded_file', (table) => {
    table.dropPrimary();
    table.dropIndex('references_total');
  });

  await knex.schema.renameTable('uploaded_file', 'file_reference');

  await knex.schema.alterTable('file_reference', (table) => {
    table.dropColumn('type');
    table.dropColumn('mime_type');
    table.dropColumn('size');

    table.bigInteger('id').primary().defaultTo(knex.raw('next_id()')).alter();
    table.renameColumn('references_total', 'total');

    table.index('total');
  });

  await knex.raw(`
    UPDATE user_account
    SET avatar = avatar - 'uploadedFileId' - 'size' || jsonb_build_object('dirname', avatar->'uploadedFileId', 'sizeInBytes', avatar->'size')
    WHERE avatar IS NOT NULL;
  `);

  await knex.schema.alterTable('background_image', (table) => {
    table.renameColumn('uploaded_file_id', 'dirname');
    table.renameColumn('size', 'size_in_bytes');
  });

  return knex.raw(`
    UPDATE attachment
    SET data = data - 'uploadedFileId' - 'size' || jsonb_build_object('fileReferenceId', data->'uploadedFileId', 'sizeInBytes', data->'size')
    WHERE type = 'file';
  `);
};
