import { Injectable } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { OnEvent } from '@nestjs/event-emitter';
import { SendNotificationCommand } from '@modules/notifications';
import { PrismaService, LoggerService } from '@modules/shared';

/**
 * When a new listing is approved, check all saved searches with alerts enabled
 * and notify users whose filters match the new listing.
 */
@Injectable()
export class SavedSearchAlertHandler {
  constructor(
    private readonly prisma: PrismaService,
    private readonly commandBus: CommandBus,
    private readonly logger: LoggerService,
  ) {}

  @OnEvent('listing.approved')
  async handle(payload: { listingId: string }): Promise<void> {
    this.logger.log(
      `Checking saved search alerts for approved listing ${payload.listingId}`,
      'SavedSearchAlertHandler',
    );

    try {
      // Fetch the listing with property details
      const listing = await this.prisma.listing.findUnique({
        where: { id: payload.listingId },
        include: { property: true },
      });

      if (!listing || !listing.property) {
        return;
      }

      // Find all saved searches with alerts enabled
      const savedSearches = await this.prisma.savedSearch.findMany({
        where: { alertEnabled: true },
        include: {
          user: { select: { id: true, email: true, fullName: true } },
        },
      });

      let matchCount = 0;

      for (const search of savedSearches) {
        // Skip if search belongs to the listing owner
        if (search.userId === listing.sellerId) {
          continue;
        }

        const filters = search.filters as Record<string, unknown>;
        if (this.matchesFilters(listing, listing.property, filters)) {
          matchCount++;
          await this.sendAlert(search, listing, listing.property);
        }
      }

      if (matchCount > 0) {
        this.logger.log(
          `Sent ${matchCount} saved search alerts for listing ${payload.listingId}`,
          'SavedSearchAlertHandler',
        );
      }
    } catch (err) {
      this.logger.warn(
        `Saved search alert processing failed for listing ${payload.listingId}: ${err instanceof Error ? err.message : String(err)}`,
        'SavedSearchAlertHandler',
      );
    }
  }

  /**
   * Check if a listing matches the saved search filters.
   * Filters are a flexible JSON object matching SearchPropertiesDto fields.
   */
  private matchesFilters(
    listing: { transactionType: string; priceVND: bigint; sellerId: string },
    property: {
      propertyType: string;
      areaM2: number;
      bedrooms: number | null;
      district: string;
      city: string;
    },
    filters: Record<string, unknown>,
  ): boolean {
    if (filters['transactionType'] && filters['transactionType'] !== listing.transactionType) {
      return false;
    }

    if (filters['propertyType'] && filters['propertyType'] !== property.propertyType) {
      return false;
    }

    if (filters['district'] && filters['district'] !== property.district) {
      return false;
    }

    if (filters['city'] && filters['city'] !== property.city) {
      return false;
    }

    const price = Number(listing.priceVND);

    if (filters['priceMin'] && price < Number(filters['priceMin'])) {
      return false;
    }

    if (filters['priceMax'] && price > Number(filters['priceMax'])) {
      return false;
    }

    if (filters['areaMin'] && property.areaM2 < Number(filters['areaMin'])) {
      return false;
    }

    if (filters['areaMax'] && property.areaM2 > Number(filters['areaMax'])) {
      return false;
    }

    if (filters['bedrooms'] && property.bedrooms !== null && property.bedrooms < Number(filters['bedrooms'])) {
      return false;
    }

    return true;
  }

  private async sendAlert(
    search: { id: string; name: string; user: { id: string; email: string | null; fullName: string | null } },
    listing: { id: string; priceVND: bigint },
    property: { title: string; district: string; city: string },
  ): Promise<void> {
    if (!search.user.email) {
      this.logger.warn(
        `User ${search.user.id} has no email, skipping saved search alert`,
        'SavedSearchAlertHandler',
      );
      return;
    }

    try {
      await this.commandBus.execute(
        new SendNotificationCommand(
          search.user.id,
          'EMAIL',
          'saved_search_alert',
          {
            userName: search.user.fullName ?? 'Người dùng',
            searchName: search.name,
            listingTitle: property.title,
            listingPrice: Number(listing.priceVND).toLocaleString('vi-VN'),
            listingDistrict: property.district,
            listingCity: property.city,
            listingUrl: `/listings/${listing.id}`,
          },
          search.user.email,
        ),
      );

      // Update lastAlertAt
      await this.prisma.savedSearch.update({
        where: { id: search.id },
        data: { lastAlertAt: new Date() },
      });
    } catch (err) {
      this.logger.warn(
        `Failed to send saved search alert to user ${search.user.id}: ${err instanceof Error ? err.message : String(err)}`,
        'SavedSearchAlertHandler',
      );
    }
  }
}
