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 aggregated = this.aggregateByInterval(snapshots, interval);

    const data: OnlineHistoryDataPoint[] = aggregated.map((s) => ({
      timestamp: s.ts.toISOString(),
      onlineCount: s.onlineCount,
      windowSeconds: s.windowSeconds,
    }));

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

  private aggregateByInterval(snapshots: OnlineSnapshot[], interval: OnlineHistoryInterval): OnlineSnapshot[] {
    if (snapshots.length === 0) return [];
    const intervalMs = interval === '1m' ? 60000 : interval === '5m' ? 300000 : 3600000;
    const buckets = new Map<number, { total: number; count: number; windowSeconds: number }>();

    for (const snapshot of snapshots) {
      const key = Math.floor(snapshot.ts.getTime() / intervalMs) * intervalMs;
      const existing = buckets.get(key);
      if (existing) {
        existing.total += snapshot.onlineCount;
        existing.count++;
      } else {
        buckets.set(key, { total: snapshot.onlineCount, count: 1, windowSeconds: snapshot.windowSeconds });
      }
    }

    return Array.from(buckets.keys())
      .sort((a, b) => a - b)
      .map((key) => {
        const bucket = buckets.get(key)!;
        return OnlineSnapshot.reconstitute({
          id: BigInt(0),
          ts: new Date(key),
          onlineCount: Math.round(bucket.total / bucket.count),
          windowSeconds: bucket.windowSeconds,
        });
      });
  }

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

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

    for (const s of snapshots) {
      totalOnline += s.onlineCount;
      if (s.onlineCount > maxOnline) { maxOnline = s.onlineCount; maxTimestamp = s.ts; }
      if (s.onlineCount < minOnline) { minOnline = s.onlineCount; minTimestamp = s.ts; }
    }

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