import * as nodeSchedule from 'node-schedule';
import { EventSubscribeService } from '../services/EventSubscribeService';
import type { EventDetail, Player } from '../types';
import { redis } from '../utils/server';
import { diffArrays } from '../utils/common';
import { sendNotification } from '../utils/firebase-admin';
import { prisma } from '../prisma/db';
import type { Schedule } from './schedule';
import dayjs from 'dayjs';
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";

dayjs.extend(utc);
dayjs.extend(timezone);

export class EventWatchSchedule implements Schedule {

  #job?: nodeSchedule.Job;

  constructor() {}

  async watchEvents() {
    console.debug('nextStartTime: %s', dayjs.tz(this.#job?.nextInvocation(), 'Asia/Tokyo').format('YYYY-MM-DD HH:mm:ss'));
    const events = await EventSubscribeService.getAllEvents();
    events.forEach((e) => this.diffEvent(e));
  }

  async diffEvent(event: EventDetail) {
    if (event.players.length === 0) {
      console.debug('名单为空，可能请求过于频繁，跳过处理...', event.title, event.eventId);
      return;
    }
    const key = `my-kaiqiuwang:watch:${event.eventId}`;
    const oldPlayers = await redis.get(key).then(s => s ? JSON.parse(s) as Player[] : []);
    await redis.set(key, JSON.stringify(event.players));
    if (oldPlayers.length === 0) {
      console.debug('event first time watch, skip compare. event: %s - %s', event.eventId, event.title);
      return;
    }
    const playerMap = new Map<string, Player>(oldPlayers.map(e => [e.uid, e]));
    event.players.forEach(e => playerMap.set(e.uid, e));
    const oldSet = new Set(oldPlayers.map(e => e.uid));
    const newSet = new Set(event.players.map(e => e.uid));
    const diff = diffArrays(oldSet, newSet);
    const removedPlayers = diff.deletedList.map(e => playerMap.get(e) as Player);
    const addedPlayers = diff.newMemberList.map(e => playerMap.get(e) as Player);
    if (!removedPlayers.length && !addedPlayers.length) {
      console.debug('No change detected in event: %s - %s`,', event.eventId, event.title);
      return;
    }
    const title = `${event.title} 人员变动`;
    const msgs: string[] = [];
    if (removedPlayers.length) {
      msgs.push(`${removedPlayers.map(e => e.name).join(',')}共${removedPlayers.length}人退赛`);
    }
    if (addedPlayers.length) {
      msgs.push(`新参赛${addedPlayers.length}人：${addedPlayers.map(e => e.name).join(',')}`);
    }
    const url = `https://tt.ksr.la/event/${event.eventId}`;
    this.notificateEventState(event.eventId, title, msgs.join('\n'), url);
  }
  async notificateEventState(eventId: string, title: string, body: string, url: string) {
    console.debug('Notifying event state change for event:', eventId, title, body);
    const uids = await prisma.eventSubs
      .findMany({ where: { event_id: eventId }, select: { logto_uid: true } })
      .then(e => e.map(e => e.logto_uid));
    const tokens = await prisma.notificationToken
      .findMany({ where: { logto_uid: { in: uids }}, select: { token: true } })
      .then(v => v.map(e => e.token));
    await Promise.all(tokens.map(token => sendNotification(token, { title, body, url })));
  }
  start() {
    const job = nodeSchedule.scheduleJob('event-watch', {
      minute: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55],
      hour: [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22],
    }, () => this.watchEvents());
    this.#job = job;
    console.debug('Start schedule: %s, next run at %s', job.name, dayjs.tz(job.nextInvocation(), 'Asia/Tokyo').format('YYYY-MM-DD HH:mm:ss'));
  }
}