// src/services/flyerPersistenceService.server.ts
import type { Logger } from 'pino';
import type { PoolClient } from 'pg';
import { withTransaction as defaultWithTransaction } from './db/connection.db';
import { createFlyerAndItems } from './db/flyer.db';
import { AdminRepository } from './db/admin.db';
import { GamificationRepository } from './db/gamification.db';
import { cacheService } from './cacheService.server';
import type { FlyerInsert, FlyerItemInsert, Flyer } from '../types';

export type WithTransactionFn = <T>(callback: (client: PoolClient) => Promise<T>) => Promise<T>;

export class FlyerPersistenceService {
  private withTransaction: WithTransactionFn;

  constructor(withTransactionFn: WithTransactionFn = defaultWithTransaction) {
    this.withTransaction = withTransactionFn;
  }

  /**
   * Allows replacing the withTransaction function at runtime.
   * This is primarily used for testing to inject mock implementations.
   * Pass null to reset to the default implementation.
   * @internal
   */
  _setWithTransaction(fn: WithTransactionFn | null): void {
    this.withTransaction = fn ?? defaultWithTransaction;
  }

  /**
   * Saves the flyer and its items to the database within a transaction.
   * Also logs the activity and invalidates related cache entries.
   */
  async saveFlyer(
    flyerData: FlyerInsert,
    itemsForDb: FlyerItemInsert[],
    userId: string | undefined,
    logger: Logger,
  ): Promise<Flyer> {
    const flyer = await this.withTransaction(async (client) => {
      const { flyer, items } = await createFlyerAndItems(flyerData, itemsForDb, logger, client);

      logger.info(
        `Successfully processed flyer: ${flyer.file_name} (ID: ${flyer.flyer_id}) with ${items.length} items.`,
      );

      // Log activity if a user uploaded it
      if (userId) {
        const transactionalAdminRepo = new AdminRepository(client);
        await transactionalAdminRepo.logActivity(
          {
            userId: userId,
            action: 'flyer_processed',
            displayText: `Processed a new flyer for ${flyerData.store_name}.`,
            details: { flyerId: flyer.flyer_id, storeName: flyerData.store_name },
          },
          logger,
        );

        // Award 'First-Upload' achievement
        const gamificationRepo = new GamificationRepository(client);
        await gamificationRepo.awardAchievement(userId, 'First-Upload', logger);
      }
      return flyer;
    });

    // Invalidate flyer list cache after successful creation (fire-and-forget)
    cacheService.invalidateFlyers(logger).catch(() => {
      // Error already logged in invalidateFlyers
    });

    return flyer;
  }
}
