import { Injectable, Inject, Logger } from '@nestjs/common';
import { TelemetryEvent } from '../../domain/entities/telemetry-event.entity';
import { TELEMETRY_EVENT_REPOSITORY, ITelemetryEventRepository, EventListFilters } from '../../domain/repositories/telemetry-event.repository.interface';
import { ONLINE_SNAPSHOT_REPOSITORY, IOnlineSnapshotRepository } from '../../domain/repositories/online-snapshot.repository.interface';
import { DAILY_ACTIVE_STATS_REPOSITORY, IDailyActiveStatsRepository } from '../../domain/repositories/daily-active-stats.repository.interface';
import { PRESENCE_SERVICE, IPresenceService } from '../../domain/ports/presence.service.interface';
import { TELEMETRY_METRICS_SERVICE, ITelemetryMetricsService } from '../../domain/ports/telemetry-metrics.service.interface';
import { TELEMETRY_PRODUCER_SERVICE, ITelemetryProducerService } from '../../domain/ports/telemetry-producer.service.interface';

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

  constructor(
    @Inject(TELEMETRY_EVENT_REPOSITORY) private readonly eventRepo: ITelemetryEventRepository,
    @Inject(ONLINE_SNAPSHOT_REPOSITORY) private readonly snapshotRepo: IOnlineSnapshotRepository,
    @Inject(DAILY_ACTIVE_STATS_REPOSITORY) private readonly dauRepo: IDailyActiveStatsRepository,
    @Inject(PRESENCE_SERVICE) private readonly presenceService: IPresenceService,
    @Inject(TELEMETRY_METRICS_SERVICE) private readonly metrics: ITelemetryMetricsService,
    @Inject(TELEMETRY_PRODUCER_SERVICE) private readonly kafkaProducer: ITelemetryProducerService,
  ) {}

  /** Batch insert telemetry events */
  async recordEvents(events: Array<{
    userId?: string;
    installId: string;
    eventName: string;
    clientTs: number;
    deviceBrand?: string;
    deviceModel?: string;
    deviceOs?: string;
    appVersion?: string;
    locale?: string;
    properties?: Record<string, any>;
  }>): Promise<{ recorded: number }> {
    const timer = this.metrics.startBatchTimer();

    const entities = events.map((e) => {
      const event = new TelemetryEvent();
      event.userId = e.userId || null;
      event.installId = e.installId;
      event.eventName = e.eventName;
      event.eventTime = new Date(e.clientTs * 1000);
      event.deviceBrand = e.deviceBrand || null;
      event.deviceModel = e.deviceModel || null;
      event.deviceOs = e.deviceOs || null;
      event.appVersion = e.appVersion || null;
      event.locale = e.locale || null;
      event.properties = e.properties || {};
      return event;
    });

    await this.eventRepo.saveBatch(entities);

    // Increment event counters
    for (const e of events) {
      this.metrics.incEventsTotal(e.eventName);
    }

    // Update HyperLogLog for DAU on session_start events
    const today = new Date().toISOString().slice(0, 10);
    for (const e of events) {
      if (e.eventName === 'app_session_start') {
        const identifier = e.userId || e.installId;
        await this.presenceService.addDauIdentifier(today, identifier);

        // Publish session started event to Kafka
        await this.kafkaProducer.publishSessionStarted({
          userId: e.userId,
          installId: e.installId,
          timestamp: e.clientTs,
          properties: e.properties,
        });
      }
    }

    timer();
    return { recorded: entities.length };
  }

  /** Record heartbeat */
  async recordHeartbeat(userId: string, installId: string, appVersion: string): Promise<void> {
    await this.presenceService.updatePresence(userId);
    this.metrics.incHeartbeatTotal(appVersion);
    await this.kafkaProducer.publishHeartbeat(userId, installId, appVersion);
  }

  /** Get current online count */
  async getOnlineCount(): Promise<{ count: number; windowSeconds: number; queriedAt: string }> {
    const count = await this.presenceService.countOnline();
    this.metrics.setOnlineUsers(count);
    return {
      count,
      windowSeconds: this.presenceService.getWindowSeconds(),
      queriedAt: new Date().toISOString(),
    };
  }

  /** Get online history with interval aggregation */
  async getOnlineHistory(startTime: Date, endTime: Date, interval: '1m' | '5m' | '1h' = '5m') {
    const snapshots = await this.snapshotRepo.findByTimeRange(startTime, endTime);

    // Aggregate by interval
    const intervalMs = interval === '1m' ? 60000 : interval === '5m' ? 300000 : 3600000;
    const buckets = new Map<number, number[]>();

    for (const snap of snapshots) {
      const ts = snap.ts.getTime();
      const bucketKey = Math.floor(ts / intervalMs) * intervalMs;
      if (!buckets.has(bucketKey)) buckets.set(bucketKey, []);
      buckets.get(bucketKey)!.push(snap.onlineCount);
    }

    const dataPoints = Array.from(buckets.entries()).map(([ts, counts]) => ({
      timestamp: new Date(ts).toISOString(),
      onlineCount: Math.round(counts.reduce((a, b) => a + b, 0) / counts.length),
      windowSeconds: 180,
    }));

    const allCounts = dataPoints.map((d) => d.onlineCount);
    const maxOnline = allCounts.length > 0 ? Math.max(...allCounts) : 0;
    const minOnline = allCounts.length > 0 ? Math.min(...allCounts) : 0;
    const avgOnline = allCounts.length > 0 ? Math.round(allCounts.reduce((a, b) => a + b, 0) / allCounts.length) : 0;

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

  /** Get DAU stats for date range */
  async getDauStats(startDate: string, endDate: string) {
    const stats = await this.dauRepo.findByDateRange(startDate, endDate);

    return {
      data: stats.map((s) => ({
        day: s.day,
        dauCount: s.dauCount,
        byPlatform: s.dauByPlatform,
        byRegion: s.dauByRegion,
      })),
      total: stats.length,
    };
  }

  /** List telemetry events with pagination and filters */
  async listEvents(filters: EventListFilters) {
    const [items, total] = await this.eventRepo.findPaginated(filters);
    return { items, total, page: filters.page, limit: filters.limit };
  }

  /** Get realtime analytics dashboard data */
  async getRealtimeData() {
    const today = new Date().toISOString().slice(0, 10);
    const todayStart = new Date(`${today}T00:00:00Z`);

    const [onlineCount, approxDau, eventsToday] = await Promise.all([
      this.presenceService.countOnline(),
      this.presenceService.getApproxDau(today),
      this.eventRepo.countSince(todayStart),
    ]);

    return {
      onlineUsers: onlineCount,
      dauToday: approxDau,
      eventsToday,
      windowSeconds: 180,
      queriedAt: new Date().toISOString(),
    };
  }
}
