import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { Inject, Injectable } from '@nestjs/common';
import { RecordEventsCommand, EventItemDto } from './record-events.command';
import { EventLog } from '../../../domain/entities/event-log.entity';
import { InstallId } from '../../../domain/value-objects/install-id.vo';
import { EventName } from '../../../domain/value-objects/event-name.vo';
import { EventProperties } from '../../../domain/value-objects/event-properties.vo';
import {
  IEventLogRepository,
  EVENT_LOG_REPOSITORY,
} from '../../../domain/repositories/event-log.repository.interface';
import { RedisService } from '../../../infrastructure/redis/redis.service';
import { EventPublisherService } from '../../../infrastructure/kafka/event-publisher.service';
import { MetricsService } from '../../../infrastructure/metrics/metrics.service';
import { SessionStartedEvent } from '../../../domain/events/session-started.event';
import { formatToDateKey } from '../../../shared/utils/timezone.util';

export interface RecordEventsResult {
  accepted: number;
  failed: number;
  errors?: string[];
}

@Injectable()
@CommandHandler(RecordEventsCommand)
export class RecordEventsHandler implements ICommandHandler<RecordEventsCommand> {
  constructor(
    @Inject(EVENT_LOG_REPOSITORY)
    private readonly eventLogRepository: IEventLogRepository,
    private readonly redisService: RedisService,
    private readonly eventPublisher: EventPublisherService,
    private readonly metricsService: MetricsService,
  ) {}

  async execute(command: RecordEventsCommand): Promise<RecordEventsResult> {
    const startTime = Date.now();
    const { events } = command;
    const errors: string[] = [];
    const validLogs: EventLog[] = [];

    // 1. 验证并转换事件
    for (let i = 0; i < events.length; i++) {
      try {
        const log = this.toEventLog(events[i]);
        validLogs.push(log);
      } catch (e) {
        errors.push(`Event[${i}]: ${e.message}`);
      }
    }

    if (validLogs.length === 0) {
      return { accepted: 0, failed: events.length, errors };
    }

    // 2. 批量写入数据库
    await this.eventLogRepository.batchInsert(validLogs);

    // 3. 更新实时DAU (HyperLogLog)
    const todayKey = formatToDateKey(new Date());
    for (const log of validLogs) {
      if (log.eventName.isDauEvent()) {
        await this.redisService.pfadd(
          `analytics:dau:${todayKey}`,
          log.dauIdentifier,
        );

        // 4. 发布领域事件
        await this.eventPublisher.publish(
          SessionStartedEvent.EVENT_NAME,
          new SessionStartedEvent(
            log.userId,
            log.installId.value,
            log.eventTime,
            {
              appVersion: log.properties.appVersion,
              os: log.properties.os,
              osVersion: log.properties.osVersion,
            },
          ),
        );
      }
    }

    // 5. 记录 Prometheus 指标
    for (const log of validLogs) {
      this.metricsService.recordEvent(log.eventName.value);
    }
    const durationSeconds = (Date.now() - startTime) / 1000;
    this.metricsService.recordEventBatchDuration(durationSeconds);

    return {
      accepted: validLogs.length,
      failed: events.length - validLogs.length,
      errors: errors.length > 0 ? errors : undefined,
    };
  }

  private toEventLog(dto: EventItemDto): EventLog {
    return EventLog.create({
      userId: dto.userId ?? null,  // userSerialNum string, e.g. "D25121400005"
      installId: InstallId.fromString(dto.installId),
      eventName: EventName.fromString(dto.eventName),
      eventTime: new Date(dto.clientTs * 1000),
      properties: EventProperties.fromData(dto.properties ?? {}),
    });
  }
}
