import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { Inject, Injectable } from '@nestjs/common';
import {
  GetOnlineHistoryQuery,
  OnlineHistoryInterval,
} from './get-online-history.query';
import {
  IOnlineSnapshotRepository,
  ONLINE_SNAPSHOT_REPOSITORY,
} from '../../../domain/repositories/online-snapshot.repository.interface';
import { OnlineSnapshot } from '../../../domain/entities/online-snapshot.entity';

export interface OnlineHistoryDataPoint {
  timestamp: string;
  onlineCount: number;
  windowSeconds: number;
}

export interface OnlineHistoryResult {
  data: OnlineHistoryDataPoint[];
  interval: OnlineHistoryInterval;
  startTime: string;
  endTime: string;
  total: number;
  summary: {
    maxOnline: number;
    minOnline: number;
    avgOnline: number;
    maxTimestamp: string | null;
    minTimestamp: string | null;
  };
}

@Injectable()
@QueryHandler(GetOnlineHistoryQuery)
export class GetOnlineHistoryHandler
  implements IQueryHandler<GetOnlineHistoryQuery>
{
  constructor(
    @Inject(ONLINE_SNAPSHOT_REPOSITORY)
    private readonly snapshotRepository: IOnlineSnapshotRepository,
  ) {}

  async execute(query: GetOnlineHistoryQuery): Promise<OnlineHistoryResult> {
    const { startTime, endTime, interval } = query;

    // 从仓储获取快照数据
    const snapshots = await this.snapshotRepository.findByTimeRange(
      startTime,
      endTime,
      interval,
    );

    // 按间隔聚合数据
    const aggregatedSnapshots = this.aggregateByInterval(snapshots, interval);

    // 转换为 DTO
    const data: OnlineHistoryDataPoint[] = aggregatedSnapshots.map(
      (snapshot) => ({
        timestamp: snapshot.ts.toISOString(),
        onlineCount: snapshot.onlineCount,
        windowSeconds: snapshot.windowSeconds,
      }),
    );

    // 计算汇总统计
    const summary = this.calculateSummary(aggregatedSnapshots);

    return {
      data,
      interval,
      startTime: startTime.toISOString(),
      endTime: endTime.toISOString(),
      total: data.length,
      summary,
    };
  }

  /**
   * 按时间间隔聚合快照数据
   * 每个时间桶取该桶内在线人数的平均值
   */
  private aggregateByInterval(
    snapshots: OnlineSnapshot[],
    interval: OnlineHistoryInterval,
  ): OnlineSnapshot[] {
    if (snapshots.length === 0) {
      return [];
    }

    const intervalMs = this.getIntervalMs(interval);
    const buckets = new Map<
      number,
      { total: number; count: number; windowSeconds: number }
    >();

    for (const snapshot of snapshots) {
      const bucketKey =
        Math.floor(snapshot.ts.getTime() / intervalMs) * intervalMs;

      const existing = buckets.get(bucketKey);
      if (existing) {
        existing.total += snapshot.onlineCount;
        existing.count += 1;
      } else {
        buckets.set(bucketKey, {
          total: snapshot.onlineCount,
          count: 1,
          windowSeconds: snapshot.windowSeconds,
        });
      }
    }

    // 将聚合结果转换回快照格式
    const aggregated: OnlineSnapshot[] = [];
    const sortedKeys = Array.from(buckets.keys()).sort((a, b) => a - b);

    for (const key of sortedKeys) {
      const bucket = buckets.get(key)!;
      const avgCount = Math.round(bucket.total / bucket.count);

      aggregated.push(
        OnlineSnapshot.reconstitute({
          id: BigInt(0),
          ts: new Date(key),
          onlineCount: avgCount,
          windowSeconds: bucket.windowSeconds,
        }),
      );
    }

    return aggregated;
  }

  private getIntervalMs(interval: OnlineHistoryInterval): number {
    switch (interval) {
      case '1m':
        return 60 * 1000;
      case '5m':
        return 5 * 60 * 1000;
      case '1h':
        return 60 * 60 * 1000;
      default:
        return 5 * 60 * 1000;
    }
  }

  private calculateSummary(snapshots: OnlineSnapshot[]): OnlineHistoryResult['summary'] {
    if (snapshots.length === 0) {
      return {
        maxOnline: 0,
        minOnline: 0,
        avgOnline: 0,
        maxTimestamp: null,
        minTimestamp: null,
      };
    }

    let maxOnline = -Infinity;
    let minOnline = Infinity;
    let totalOnline = 0;
    let maxTimestamp: Date | null = null;
    let minTimestamp: Date | null = null;

    for (const snapshot of snapshots) {
      totalOnline += snapshot.onlineCount;

      if (snapshot.onlineCount > maxOnline) {
        maxOnline = snapshot.onlineCount;
        maxTimestamp = snapshot.ts;
      }

      if (snapshot.onlineCount < minOnline) {
        minOnline = snapshot.onlineCount;
        minTimestamp = snapshot.ts;
      }
    }

    return {
      maxOnline,
      minOnline,
      avgOnline: Math.round(totalOnline / snapshots.length),
      maxTimestamp: maxTimestamp?.toISOString() || null,
      minTimestamp: minTimestamp?.toISOString() || null,
    };
  }
}
