import { Injectable, Logger, Inject } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { CommandBus } from '@nestjs/cqrs';
import { subDays } from 'date-fns';
import { CalculateDauCommand } from '../commands/calculate-dau/calculate-dau.command';
import { PresenceRedisRepository } from '../../infrastructure/redis/presence-redis.repository';
import { OnlineDetectionService } from '../../domain/services/online-detection.service';
import { OnlineSnapshot } from '../../domain/entities/online-snapshot.entity';
import { IOnlineSnapshotRepository, ONLINE_SNAPSHOT_REPOSITORY } from '../../domain/repositories/online-snapshot.repository.interface';

@Injectable()
export class AnalyticsScheduler {
  private readonly logger = new Logger(AnalyticsScheduler.name);

  constructor(
    private readonly commandBus: CommandBus,
    private readonly presenceRedisRepository: PresenceRedisRepository,
    private readonly onlineDetectionService: OnlineDetectionService,
    @Inject(ONLINE_SNAPSHOT_REPOSITORY)
    private readonly snapshotRepository: IOnlineSnapshotRepository,
  ) {}

  @Cron(CronExpression.EVERY_MINUTE)
  async recordOnlineSnapshot(): Promise<void> {
    try {
      const now = new Date();
      const threshold = this.onlineDetectionService.getOnlineThreshold(now);
      const count = await this.presenceRedisRepository.countOnlineUsers(threshold);
      const snapshot = OnlineSnapshot.create({
        ts: now,
        onlineCount: count,
        windowSeconds: this.onlineDetectionService.getWindowSeconds(),
      });
      await this.snapshotRepository.insert(snapshot);
      this.logger.debug(`Online snapshot: ${count} users`);
    } catch (error) {
      this.logger.error('Failed to record online snapshot', error);
    }
  }

  @Cron(CronExpression.EVERY_HOUR)
  async cleanupExpiredPresence(): Promise<void> {
    try {
      const threshold = Math.floor(Date.now() / 1000) - 86400;
      await this.presenceRedisRepository.removeExpiredUsers(threshold);
    } catch (error) {
      this.logger.error('Failed to cleanup expired presence', error);
    }
  }

  @Cron('0 0 1 * * *', { timeZone: 'Asia/Shanghai' })
  async calculateYesterdayDau(): Promise<void> {
    try {
      const yesterday = subDays(new Date(), 1);
      await this.commandBus.execute(new CalculateDauCommand(yesterday));
      this.logger.log('Yesterday DAU calculated');
    } catch (error) {
      this.logger.error('Failed to calculate yesterday DAU', error);
    }
  }

  @Cron(CronExpression.EVERY_HOUR)
  async calculateTodayDauRolling(): Promise<void> {
    try {
      await this.commandBus.execute(new CalculateDauCommand(new Date()));
    } catch (error) {
      this.logger.error('Failed to calculate today DAU', error);
    }
  }
}
