import { InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, EventBus, type ICommandHandler } from '@nestjs/cqrs';
import { createId } from '@paralleldrive/cuid2';
import { type SavedSearch, type Prisma } from '@prisma/client';
import { DomainException, ValidationException, PrismaService, LoggerService } from '@modules/shared';
import { SavedSearchCreatedEvent } from '../../../domain/events/saved-search-created.event';
import { CreateSavedSearchCommand } from './create-saved-search.command';

export interface CreateSavedSearchResult {
  id: string;
  name: string;
  filters: unknown;
  alertEnabled: boolean;
  createdAt: Date;
}

/**
 * Note: quota enforcement (`searches_saved` metric) lives at the controller
 * via `@RequireQuota('searches_saved')` + `QuotaGuard`. Usage metering
 * happens in subscriptions via the `SavedSearchCreatedEvent` listener.
 * This handler must NOT call `CheckQuotaQuery` or `MeterUsageCommand`
 * directly — see A-11.
 */
@CommandHandler(CreateSavedSearchCommand)
export class CreateSavedSearchHandler implements ICommandHandler<CreateSavedSearchCommand> {
  constructor(
    private readonly prisma: PrismaService,
    private readonly eventBus: EventBus,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: CreateSavedSearchCommand): Promise<CreateSavedSearchResult> {
    try {
      // Validate name
      if (!command.name || command.name.trim().length === 0) {
        throw new ValidationException('Tên tìm kiếm không được để trống');
      }

      if (command.name.trim().length > 100) {
        throw new ValidationException('Tên tìm kiếm không được vượt quá 100 ký tự');
      }

      const id = createId();
      const savedSearch: SavedSearch = await this.prisma.savedSearch.create({
        data: {
          id,
          userId: command.userId,
          name: command.name.trim(),
          filters: command.filters as Prisma.InputJsonValue,
          alertEnabled: command.alertEnabled,
        },
      });

      // Publish domain event so subscriptions can meter usage out-of-band.
      this.eventBus.publish(new SavedSearchCreatedEvent(id, command.userId));

      this.logger.log(`Saved search created: id=${id}, user=${command.userId}`, 'CreateSavedSearchHandler');

      return {
        id: savedSearch.id,
        name: savedSearch.name,
        filters: savedSearch.filters,
        alertEnabled: savedSearch.alertEnabled,
        createdAt: savedSearch.createdAt,
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to create saved search: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể tạo tìm kiếm đã lưu');
    }
  }
}
