import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import { DeviceToken, PushChannel } from '../../domain/entities/device-token.entity';
import { IDeviceTokenRepository } from '../../domain/repositories/device-token.repository.interface';

@Injectable()
export class DeviceTokenRepositoryImpl implements IDeviceTokenRepository {
  constructor(
    @InjectRepository(DeviceToken)
    private readonly repo: Repository<DeviceToken>,
  ) {}

  async findActiveByUserId(userId: string): Promise<DeviceToken[]> {
    return this.repo.find({
      where: { userId, isActive: true },
      order: { updatedAt: 'DESC' },
    });
  }

  async findActiveByUserIds(userIds: string[]): Promise<DeviceToken[]> {
    if (!userIds.length) return [];

    // Process in batches of 500 to avoid oversized IN clauses
    const results: DeviceToken[] = [];
    for (let i = 0; i < userIds.length; i += 500) {
      const batch = userIds.slice(i, i + 500);
      const tokens = await this.repo.find({
        where: { userId: In(batch), isActive: true },
      });
      results.push(...tokens);
    }
    return results;
  }

  async upsertToken(data: Partial<DeviceToken>): Promise<DeviceToken> {
    // Find by token value (unique)
    const existing = await this.repo.findOne({ where: { token: data.token! } });

    if (existing) {
      // Update existing — may be re-registered by same or different user
      existing.userId = data.userId ?? existing.userId;
      existing.platform = data.platform ?? existing.platform;
      existing.channel = data.channel ?? existing.channel;
      existing.deviceId = data.deviceId ?? existing.deviceId;
      existing.deviceModel = data.deviceModel ?? existing.deviceModel;
      existing.appVersion = data.appVersion ?? existing.appVersion;
      existing.isActive = true;
      existing.lastUsedAt = new Date();
      return this.repo.save(existing);
    }

    // Create new
    const token = this.repo.create({
      ...data,
      isActive: true,
      lastUsedAt: new Date(),
    });
    return this.repo.save(token);
  }

  async deactivateToken(token: string): Promise<void> {
    await this.repo.update({ token }, { isActive: false });
  }

  async deactivateAllForUser(userId: string): Promise<void> {
    await this.repo.update({ userId, isActive: true }, { isActive: false });
  }

  async countByChannel(): Promise<Array<{ channel: PushChannel; count: number }>> {
    const result = await this.repo
      .createQueryBuilder('dt')
      .select('dt.channel', 'channel')
      .addSelect('COUNT(*)', 'count')
      .where('dt.is_active = true')
      .groupBy('dt.channel')
      .getRawMany();

    return result.map((r) => ({
      channel: r.channel as PushChannel,
      count: parseInt(r.count, 10),
    }));
  }
}
