import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ClsService } from 'nestjs-cls';
import { QueryEntity } from '../../entities/query.entity';
import { BookingEntity } from '../../entities/booking.entity';
import { DriverEntity, DriverStatus } from '../../entities/driver.entity';
import { AuditLogEntity } from '../../entities/audit-log.entity';
import { CLS_TENANT_ID } from '../../common/cls/cls.constants';

@Injectable()
export class DashboardService {
  constructor(
    @InjectRepository(QueryEntity) private readonly queryRepo: Repository<QueryEntity>,
    @InjectRepository(BookingEntity) private readonly bookingRepo: Repository<BookingEntity>,
    @InjectRepository(DriverEntity) private readonly driverRepo: Repository<DriverEntity>,
    @InjectRepository(AuditLogEntity) private readonly auditRepo: Repository<AuditLogEntity>,
    private readonly cls: ClsService,
  ) {}

  private getTenantId(): string {
    return this.cls.get<string>(CLS_TENANT_ID);
  }

  async getStats(startDate?: string, endDate?: string) {
    const tenantId = this.getTenantId();

    const [
      totalQueries,
      activeBookings,
      revenue,
      upcomingData,
      availableDrivers,
      stageCounts,
      recentActivity,
    ] = await Promise.all([
      this.countQueries(tenantId, startDate, endDate),
      this.countActiveBookings(tenantId),
      this.sumRevenue(tenantId, startDate, endDate),
      this.getUpcomingTrips(tenantId),
      this.countAvailableDrivers(tenantId),
      this.getQueryStageCounts(tenantId, startDate, endDate),
      this.getRecentActivity(tenantId),
    ]);

    return {
      total_queries: totalQueries,
      active_bookings: activeBookings,
      revenue,
      upcoming_trips: upcomingData.count,
      available_drivers: availableDrivers,
      query_stage_counts: stageCounts,
      upcoming_bookings: upcomingData.bookings,
      recent_activity: recentActivity,
    };
  }

  private async countQueries(tenantId: string, startDate?: string, endDate?: string): Promise<number> {
    const qb = this.queryRepo.createQueryBuilder('q')
      .where('q.tenant_id = :tenantId', { tenantId })
      .andWhere('q.is_deleted = false');
    if (startDate) qb.andWhere('q.created_at >= :startDate', { startDate });
    if (endDate) qb.andWhere('q.created_at <= :endDate', { endDate: endDate + 'T23:59:59' });
    return qb.getCount();
  }

  private async countActiveBookings(tenantId: string): Promise<number> {
    return this.bookingRepo.createQueryBuilder('b')
      .where('b.tenant_id = :tenantId', { tenantId })
      .andWhere('b.is_deleted = false')
      .andWhere('b.status IN (:...statuses)', { statuses: ['upcoming', 'ongoing'] })
      .getCount();
  }

  private async sumRevenue(tenantId: string, startDate?: string, endDate?: string): Promise<number> {
    const qb = this.bookingRepo.createQueryBuilder('b')
      .select('COALESCE(SUM(b.grand_total), 0)', 'total')
      .where('b.tenant_id = :tenantId', { tenantId })
      .andWhere('b.is_deleted = false');
    if (startDate) qb.andWhere('b.created_at >= :startDate', { startDate });
    if (endDate) qb.andWhere('b.created_at <= :endDate', { endDate: endDate + 'T23:59:59' });
    const result = await qb.getRawOne();
    return Number(result?.total) || 0;
  }

  private async getUpcomingTrips(tenantId: string) {
    const qb = this.bookingRepo.createQueryBuilder('b')
      .leftJoinAndSelect('b.query', 'q')
      .leftJoinAndSelect('q.destination', 'd')
      .where('b.tenant_id = :tenantId', { tenantId })
      .andWhere('b.is_deleted = false')
      .andWhere('b.status = :status', { status: 'upcoming' })
      .orderBy('q.start_date', 'ASC')
      .take(10);

    const [bookings, count] = await qb.getManyAndCount();

    return {
      count,
      bookings: bookings.map((b: any) => ({
        id: b.id,
        booking_number: b.booking_number,
        guest_name: b.query?.guest_name || '',
        destination_name: b.query?.destination?.name || '—',
        start_date: b.query?.start_date,
        end_date: b.query?.end_date,
        status: b.status,
      })),
    };
  }

  private async countAvailableDrivers(tenantId: string): Promise<number> {
    return this.driverRepo.createQueryBuilder('d')
      .where('d.tenant_id = :tenantId', { tenantId })
      .andWhere('d.is_deleted = false')
      .andWhere('d.is_active = true')
      .andWhere('d.status = :status', { status: DriverStatus.AVAILABLE })
      .getCount();
  }

  private async getQueryStageCounts(tenantId: string, startDate?: string, endDate?: string): Promise<Record<string, number>> {
    const qb = this.queryRepo.createQueryBuilder('q')
      .select('q.stage', 'stage')
      .addSelect('COUNT(*)', 'count')
      .where('q.tenant_id = :tenantId', { tenantId })
      .andWhere('q.is_deleted = false')
      .groupBy('q.stage');
    if (startDate) qb.andWhere('q.created_at >= :startDate', { startDate });
    if (endDate) qb.andWhere('q.created_at <= :endDate', { endDate: endDate + 'T23:59:59' });
    const rows = await qb.getRawMany();
    const counts: Record<string, number> = {};
    for (const row of rows) counts[row.stage] = Number(row.count);
    return counts;
  }

  private async getRecentActivity(tenantId: string) {
    // Get latest audit entry per entity (grouped by entity_type + entity_id)
    // Using a subquery to get the max created_at per entity, then join back
    const rawEntries = await this.auditRepo.createQueryBuilder('a')
      .leftJoinAndSelect('a.performer', 'u')
      .where('a.tenant_id = :tenantId', { tenantId })
      .orderBy('a.created_at', 'DESC')
      .take(50)
      .getMany();

    // Group by entity_type + entity_id, keep only the latest per entity
    const seen = new Map<string, typeof rawEntries[0]>();
    for (const entry of rawEntries) {
      const key = `${entry.entity_type}:${entry.entity_id}`;
      if (!seen.has(key)) seen.set(key, entry);
    }
    const grouped = Array.from(seen.values()).slice(0, 10);

    // Enrich with entity-specific data (guest name, number)
    const entityIds = { query: new Set<string>(), booking: new Set<string>() };
    for (const e of grouped) {
      if (e.entity_type === 'query') entityIds.query.add(e.entity_id);
      if (e.entity_type === 'booking') entityIds.booking.add(e.entity_id);
    }

    const queryMap = new Map<string, { query_number: string; guest_name: string }>();
    const bookingMap = new Map<string, { booking_number: string; guest_name: string }>();

    if (entityIds.query.size > 0) {
      const queries = await this.queryRepo.createQueryBuilder('q')
        .select(['q.id', 'q.query_number', 'q.guest_name'])
        .where('q.id IN (:...ids)', { ids: [...entityIds.query] })
        .getMany();
      for (const q of queries) queryMap.set(q.id, { query_number: q.query_number, guest_name: q.guest_name });
    }

    if (entityIds.booking.size > 0) {
      const bookings = await this.bookingRepo.createQueryBuilder('b')
        .leftJoinAndSelect('b.query', 'q')
        .select(['b.id', 'b.booking_number', 'q.guest_name'])
        .where('b.id IN (:...ids)', { ids: [...entityIds.booking] })
        .getMany();
      for (const b of bookings as any[]) bookingMap.set(b.id, { booking_number: b.booking_number, guest_name: b.query?.guest_name || '' });
    }

    return grouped.map(e => {
      const qData = queryMap.get(e.entity_id);
      const bData = bookingMap.get(e.entity_id);
      return {
        id: e.id,
        entity_type: e.entity_type,
        entity_id: e.entity_id,
        action: e.action,
        performer_name: (e as any).performer?.name || null,
        created_at: e.created_at,
        entity_number: qData?.query_number || bData?.booking_number || null,
        guest_name: qData?.guest_name || bData?.guest_name || null,
      };
    });
  }
}
