import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TelemetryEvent } from '../../domain/entities/telemetry-event.entity';
import { ITelemetryEventRepository, DauBreakdown, EventListFilters } from '../../domain/repositories/telemetry-event.repository.interface';

@Injectable()
export class TelemetryEventRepository implements ITelemetryEventRepository {
  constructor(
    @InjectRepository(TelemetryEvent) private readonly repo: Repository<TelemetryEvent>,
  ) {}

  async saveBatch(events: TelemetryEvent[]): Promise<void> {
    await this.repo.save(events);
  }

  async getDauCount(startTime: Date, endTime: Date): Promise<number> {
    const result = await this.repo
      .createQueryBuilder('e')
      .select("COUNT(DISTINCT COALESCE(e.user_id::text, e.install_id))", 'dauCount')
      .where("e.event_name = 'app_session_start'")
      .andWhere('e.event_time >= :startTime AND e.event_time < :endTime', {
        startTime,
        endTime,
      })
      .getRawOne();

    return parseInt(result?.dauCount || '0', 10);
  }

  async getDauByPlatform(startTime: Date, endTime: Date): Promise<DauBreakdown[]> {
    const rows = await this.repo
      .createQueryBuilder('e')
      .select("e.properties->>'platform'", 'key')
      .addSelect("COUNT(DISTINCT COALESCE(e.user_id::text, e.install_id))", 'count')
      .where("e.event_name = 'app_session_start'")
      .andWhere('e.event_time >= :startTime AND e.event_time < :endTime', {
        startTime,
        endTime,
      })
      .groupBy("e.properties->>'platform'")
      .getRawMany();

    return rows
      .filter((r) => r.key)
      .map((r) => ({ key: r.key, count: parseInt(r.count, 10) }));
  }

  async getDauByRegion(startTime: Date, endTime: Date): Promise<DauBreakdown[]> {
    const rows = await this.repo
      .createQueryBuilder('e')
      .select("e.properties->>'region'", 'key')
      .addSelect("COUNT(DISTINCT COALESCE(e.user_id::text, e.install_id))", 'count')
      .where("e.event_name = 'app_session_start'")
      .andWhere('e.event_time >= :startTime AND e.event_time < :endTime', {
        startTime,
        endTime,
      })
      .andWhere("e.properties->>'region' IS NOT NULL")
      .groupBy("e.properties->>'region'")
      .getRawMany();

    return rows
      .filter((r) => r.key)
      .map((r) => ({ key: r.key, count: parseInt(r.count, 10) }));
  }

  async findPaginated(filters: EventListFilters): Promise<[TelemetryEvent[], number]> {
    const qb = this.repo.createQueryBuilder('e');
    if (filters.eventName) qb.andWhere('e.event_name = :eventName', { eventName: filters.eventName });
    if (filters.userId) qb.andWhere('e.user_id = :userId', { userId: filters.userId });

    qb.orderBy('e.event_time', 'DESC')
      .skip((filters.page - 1) * filters.limit)
      .take(filters.limit);

    return qb.getManyAndCount();
  }

  async countSince(since: Date): Promise<number> {
    const result = await this.repo
      .createQueryBuilder('e')
      .select('COUNT(*)', 'count')
      .where('e.event_time >= :since', { since })
      .getRawOne();

    return parseInt(result?.count || '0', 10);
  }
}
