import { Injectable } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import {
  DataSource,
  IsNull,
  LessThanOrEqual,
  MoreThan,
  Not,
} from 'typeorm';
import { Timeline } from '@/entities/timeline.entity';
import { NotificationService } from '@/notification/notification.service';
import { NotificationHelper } from '@/helpers/notification.helper';
import { RefreshToken } from '@/entities/refresh-token.entity';
import moment from 'moment-timezone';
import { EmailVerificationCode } from '@/entities/email-verification-code.entity';

@Injectable()
export class CronService {
  constructor(
    private dataSource: DataSource,
    private notificationService: NotificationService,
  ) {}

  //   @Cron('*/10 * * * * *') // mỗi 10 giây
  @Cron('* * * * *')
  async handleTimelineNotification() {
    const vnNow = moment.tz('Asia/Ho_Chi_Minh');

    const currentMinuteStart = vnNow.clone().startOf('minute');
    const previousMinuteStart = currentMinuteStart
      .clone()
      .subtract(1, 'minute');

    const timelineRepo = this.dataSource.getRepository(Timeline);

    const timelines = await timelineRepo
      .createQueryBuilder('t')
      .leftJoinAndSelect('t.trip', 'trip')
      .leftJoinAndSelect('trip.group', 'group')
      .leftJoinAndSelect('group.members', 'members')
      .leftJoinAndSelect('members.user', 'user')
      .where('t.notify = true')
      .andWhere('t.isNotified = false')
      .andWhere('t.scheduledAt >= :start', {
        start: previousMinuteStart.toDate(),
      })
      .andWhere('t.scheduledAt < :end', {
        end: currentMinuteStart.toDate(),
      })
      .getMany();

    for (const timeline of timelines) {
      const updated = await timelineRepo.update(
        { id: timeline.id, isNotified: false },
        { isNotified: true },
      );

      if (updated.affected === 0) continue;

      try {
        const members = timeline.trip?.group?.members || [];

        const payload = members.map((m) =>
          NotificationHelper.notificationTimeline({
            timeline,
            group: timeline.trip.group,
            user: m.user,
            createdBy: m.user.id,
          }),
        );

        await this.notificationService.createManyAndPush(
          timeline.trip.group.id,
          payload,
        );
      } catch (error) {
        console.error(`Error timeline ${timeline.id}:`, error);

        // rollback nếu fail
        await timelineRepo.update(timeline.id, { isNotified: false });
      }
    }
  }

  /**
   * Cron job chạy mỗi giờ để xoá các refresh token đã hết hạn
   * Chạy lúc: 0 phút mỗi giờ (00:00, 01:00, 02:00, ...)
   */
  @Cron('0 * * * *') // Chạy vào đầu mỗi giờ
  async cleanupExpiredRefreshTokens() {
    const refreshTokenRepo = this.dataSource.getRepository(RefreshToken);
    const now = new Date();

    // Cách 1: Xoá tất cả token có expiresAt <= thời gian hiện tại
    const expiredTokens = await refreshTokenRepo.find({
      where: {
        expiresAt: LessThanOrEqual(now),
      },
    });

    if (expiredTokens.length === 0) {
      console.log(
        `[Cron] No expired refresh tokens found at ${now.toISOString()}`,
      );
      return;
    }

    // Xoá các token đã hết hạn
    const deleteResult = await refreshTokenRepo.delete({
      expiresAt: LessThanOrEqual(now),
    });

    console.log(
      `[Cron] Deleted ${deleteResult.affected} expired refresh tokens at ${now.toISOString()}`,
    );
  }

  /**
   * Cron job chạy mỗi ngày lúc 2h sáng để dọn dẹp token cũ
   * Giữ lại token còn hạn trong 7 ngày gần nhất, xoá các token không cần thiết
   */
  @Cron('0 2 * * *') // Chạy lúc 2:00 AM mỗi ngày
  async deepCleanupRefreshTokens() {
    const refreshTokenRepo = this.dataSource.getRepository(RefreshToken);
    const now = new Date();

    // Xoá tất cả token đã hết hạn
    const expiredResult = await refreshTokenRepo.delete({
      expiresAt: LessThanOrEqual(now),
    });

    // Tuỳ chọn: Xoá các token còn hạn nhưng đã được tạo quá 30 ngày
    // (trường hợp token không bao giờ hết hạn nhưng vẫn muốn dọn dẹp)
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    const oldTokensResult = await refreshTokenRepo.delete({
      createdAt: LessThanOrEqual(thirtyDaysAgo),
      expiresAt: MoreThan(now),
    });

    console.log(
      `[Cron] Deep cleanup completed at ${now.toISOString()}: ` +
        `Deleted ${expiredResult.affected} expired tokens, ` +
        `deleted ${oldTokensResult.affected} old but still valid tokens`,
    );
  }

  @Cron('* * * * *')
  async cleanupEmailVerificationCodes() {
    const repository = this.dataSource.getRepository(EmailVerificationCode);
    await repository.delete([
      { expiresAt: LessThanOrEqual(new Date()) },
      { usedAt: Not(IsNull()) },
    ]);
  }
}
