// src/services/db/reaction.db.ts
import type { Pool, PoolClient } from 'pg';
import type { Logger } from 'pino';
import { getPool, withTransaction } from './connection.db';
import { handleDbError } from './errors.db';
import type { UserReaction } from '../../types';

export class ReactionRepository {
  private db: Pick<Pool | PoolClient, 'query'>;

  constructor(db: Pick<Pool | PoolClient, 'query'> = getPool()) {
    this.db = db;
  }

  /**
   * Fetches user reactions based on query filters.
   * Supports filtering by user_id, entity_type, and entity_id.
   */
  async getReactions(
    filters: {
      userId?: string;
      entityType?: string;
      entityId?: string;
    },
    logger: Logger,
  ): Promise<UserReaction[]> {
    const { userId, entityType, entityId } = filters;
    try {
      let query = 'SELECT * FROM public.user_reactions WHERE 1=1';
      const params: (string | number)[] = [];
      let paramCount = 1;

      if (userId) {
        query += ` AND user_id = $${paramCount++}`;
        params.push(userId);
      }

      if (entityType) {
        query += ` AND entity_type = $${paramCount++}`;
        params.push(entityType);
      }

      if (entityId) {
        query += ` AND entity_id = $${paramCount++}`;
        params.push(entityId);
      }

      query += ' ORDER BY created_at DESC';

      const result = await this.db.query<UserReaction>(query, params);
      return result.rows;
    } catch (error) {
      handleDbError(
        error,
        logger,
        'Database error in getReactions',
        { filters },
        {
          defaultMessage: 'Failed to retrieve user reactions.',
        },
      );
    }
  }

  /**
   * Toggles a user's reaction to an entity.
   * If the reaction exists, it's deleted. If it doesn't, it's created.
   * @returns The created UserReaction if a reaction was added, or null if it was removed.
   */
  async toggleReaction(
    reactionData: Omit<UserReaction, 'reaction_id' | 'created_at' | 'updated_at'>,
    logger: Logger,
  ): Promise<UserReaction | null> {
    const { user_id, entity_type, entity_id, reaction_type } = reactionData;

    try {
      return await withTransaction(async (client) => {
        const deleteRes = await client.query(
          'DELETE FROM public.user_reactions WHERE user_id = $1 AND entity_type = $2 AND entity_id = $3 AND reaction_type = $4',
          [user_id, entity_type, entity_id, reaction_type],
        );

        if ((deleteRes.rowCount ?? 0) > 0) {
          logger.debug({ reactionData }, 'Reaction removed.');
          return null;
        }

        const insertRes = await client.query<UserReaction>(
          'INSERT INTO public.user_reactions (user_id, entity_type, entity_id, reaction_type) VALUES ($1, $2, $3, $4) RETURNING *',
          [user_id, entity_type, entity_id, reaction_type],
        );

        logger.debug({ reaction: insertRes.rows[0] }, 'Reaction added.');
        return insertRes.rows[0];
      });
    } catch (error) {
      handleDbError(
        error,
        logger,
        'Database error in toggleReaction',
        { reactionData },
        {
          fkMessage: 'The specified user or entity does not exist.',
          defaultMessage: 'Failed to toggle user reaction.',
        },
      );
    }
  }

  /**
   * Gets a summary of reactions for a specific entity.
   * Counts the number of each reaction_type.
   * @param entityType The type of the entity (e.g., 'recipe').
   * @param entityId The ID of the entity.
   * @param logger The pino logger instance.
   * @returns A promise that resolves to an array of reaction summaries.
   */
  async getReactionSummary(
    entityType: string,
    entityId: string,
    logger: Logger,
  ): Promise<{ reaction_type: string; count: number }[]> {
    try {
      const query = `
        SELECT
          reaction_type,
          COUNT(*)::int as count
        FROM public.user_reactions
        WHERE entity_type = $1 AND entity_id = $2
        GROUP BY reaction_type
        ORDER BY count DESC;
      `;
      const result = await getPool().query<{ reaction_type: string; count: number }>(query, [
        entityType,
        entityId,
      ]);
      return result.rows;
    } catch (error) {
      handleDbError(
        error,
        logger,
        'Database error in getReactionSummary',
        { entityType, entityId },
        {
          defaultMessage: 'Failed to retrieve reaction summary.',
        },
      );
    }
  }
}

export const reactionRepo = new ReactionRepository();
