import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DeepPartial, Repository } from 'typeorm';
import { Notification } from '../entities/notification.entity';
import { NotificationGateway } from './notification.gateway';
import { CreateNotificationDto } from './dto/create-notification.dto';
import { NotificationHelper } from '@/helpers/notification.helper';
import { PushService } from '@/push/push.service';

@Injectable()
export class NotificationService {
  constructor(
    @InjectRepository(Notification)
    private readonly repo: Repository<Notification>,
    private readonly gateway: NotificationGateway,
    private pushService: PushService,
  ) {}

  async createAndPush(data: Partial<Notification>) {
    if (!data.user?.id) {
      throw new Error('User ID is required');
    }

    const noti = await this.repo.save(this.repo.create(data));

    this.gateway.sendToUser(data.user?.id, noti);

    await this.pushService.sendToUser(data.user.id, {
      title: data.title,
      body: data.content,
      type: noti.type || '',
      groupId: noti.groupKey || '',
      tripId: data.metadata?.tripId || '',
      notificationId: noti.id || '',
    });

    return noti;
  }

  async createManyAndPush(groupId: string, data: DeepPartial<Notification>[]) {
    const notis = await this.repo.save(this.repo.create(data));

    for (const n of notis) {
      this.gateway.sendToUser(n.user?.id, n);

      await this.pushService.sendToUser(n.user.id, {
        title: n.title,
        body: n.content,
        type: n.type || '',
        groupId: n.groupKey || '',
        tripId: n.metadata?.tripId || '',
        notificationId: n.id || '',
      });
    }

    return notis;
  }

  async findAll(userId: string, query: any) {
    const { page = 1, limit = 10, isRead } = query;

    const qb = this.repo
      .createQueryBuilder('n')
      .leftJoinAndSelect('n.createdBy', 'createdBy')
      .where('n.userId = :userId', { userId })
      .orderBy('n.createdAt', 'DESC');

    if (isRead !== undefined) {
      qb.andWhere('n.isRead = :isRead', { isRead });
    }

    qb.skip((page - 1) * limit).take(limit);

    const [data, total] = await qb.getManyAndCount();

    const unreadTotal = await this.repo
      .createQueryBuilder('n')
      .where('n.userId = :userId', { userId })
      .andWhere('n.isRead = false')
      .getCount();

    return {
      data,
      total,
      unreadTotal,
      page,
      limit,
    };
  }

  async markAsRead(id: string, userId: string) {
    const result = await this.repo.update(
      { id, user: { id: userId } },
      {
        isRead: true,
        readAt: new Date(),
      },
    );

    if (!result.affected) throw new NotFoundException();

    return this.repo.findOne({
      where: { id },
      relations: ['user'],
    });
  }

  async markAllAsRead(userId: string) {
    const result = await this.repo.update(
      { user: { id: userId }, isRead: false },
      {
        isRead: true,
        readAt: new Date(),
      },
    );

    return {
      success: true,
      affected: result.affected ?? 0,
    };
  }

  async countUnread(userId: string) {
    return this.repo.count({
      where: { user: { id: userId }, isRead: false },
    });
  }

  async delete(id: string, userId: string) {
    const result = await this.repo.delete({ id, user: { id: userId } });
    if (!result.affected) {
      throw new NotFoundException();
    }
    return { success: true };
  }

  async add(userId: string, dto: CreateNotificationDto) {
    if (!dto.userIds.length) {
      throw new BadRequestException('Người nhận thông báo không được rỗng');
    }

    const payload: any[] = [];

    dto.userIds.forEach((id) => {
      const notification = NotificationHelper.leaderAdd({
        groupId: dto.groupId,
        user: id,
        title: dto.title,
        content: dto.content,
        createdBy: userId,
      });
      payload.push(notification);
    });

    await this.createManyAndPush(dto.groupId, payload);

    return { success: true };
  }
}
