import { type Kysely } from 'kysely';
import _ from 'lodash';

import { inject } from '../iocContainer/utils.js';
import { type ManualReviewToolServicePg } from '../services/manualReviewToolService/index.js';
import { makeKyselyTransactionWithRetry } from '../utils/kyselyTransactionWithRetry.js';
import { MINUTE_MS } from '../utils/time.js';

export default inject(
  ['closeSharedResourcesForShutdown', 'KyselyPg'],
  (sharedResourceShutdown, pgQuery: Kysely<ManualReviewToolServicePg>) => {
    const transactionWithRetry = makeKyselyTransactionWithRetry(pgQuery);
    return {
      type: 'Job' as const,
      async run() {
        await transactionWithRetry(async (trx) => {
          const lastTimestamp = await trx
            .selectFrom('public.view_maintenance_metadata')
            .select('last_insert')
            .where(
              'table_name',
              '=',
              'manual_review_tool.dim_mrt_decisions_materialized',
            )
            .executeTakeFirst();

          if (!lastTimestamp) {
            throw new Error('No last_insert timestamp found for the table');
          }

          // On a fresh deploy the row is seeded with Postgres `-infinity`,
          // which `pg-types` parses to the JS number `-Infinity`. In that case
          // `new Date(-Infinity - MINUTE_MS)` would be an Invalid Date, so fall
          // back to epoch and do a full backfill.
          const lastInsertMs = lastTimestamp.last_insert.valueOf();
          const oneMinutePrevious = Number.isFinite(lastInsertMs)
            ? new Date(lastInsertMs - MINUTE_MS)
            : new Date(0);

          const insertedRows = await trx
            .insertInto('manual_review_tool.dim_mrt_decisions_materialized')
            .expression(
              trx
                .selectFrom('manual_review_tool.dim_mrt_decisions')
                .selectAll()
                .where('decided_at', '>', oneMinutePrevious),
            )
            // since we are going back in time by 1 minute, we will likely be
            // re-inserting some rows, which should lead to unique constraint
            // violations. In this case we just want to ignore those rows to avoid
            // duplicate data
            .onConflict((oc) => oc.doNothing())
            .returning('decided_at')
            .execute();

          if (insertedRows.length === 0) {
            return;
          }

          const latestDecidedAt = insertedRows.reduce(
            (max, row) => (row.decided_at > max ? row.decided_at : max),
            insertedRows[0].decided_at,
          );

          await trx
            .updateTable('public.view_maintenance_metadata')
            .set({
              last_insert: latestDecidedAt,
            })
            .where(
              'table_name',
              '=',
              'manual_review_tool.dim_mrt_decisions_materialized',
            )
            .execute();
        });
      },
      async shutdown() {
        await sharedResourceShutdown();
      },
    };
  },
);
