import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { CreateNoticeDto } from './dto/create-notice.dto';
import { Notice, NoticeStatus, NoticeType } from './notice.entity';
import { NoticeGateway } from './notice.gateway';

@Injectable()
export class NoticeMemoryService {
  private readonly logger = new Logger(NoticeMemoryService.name);
  private readonly notices: Notice[] = [];
  private nextId = 1;

  constructor(private readonly noticeGateway: NoticeGateway) {}

  async create(createNoticeDto: CreateNoticeDto): Promise<Notice> {
    const now = new Date();
    const notice = Object.assign(new Notice(), {
      id: this.nextId++,
      title: createNoticeDto.title,
      content: createNoticeDto.content,
      type: createNoticeDto.type || NoticeType.SYSTEM,
      status: NoticeStatus.PENDING,
      userId: createNoticeDto.userId ?? null,
      senderId: createNoticeDto.senderId ?? null,
      scheduledAt: createNoticeDto.scheduledAt ? new Date(createNoticeDto.scheduledAt) : null,
      sentAt: null,
      readAt: null,
      metadata: createNoticeDto.metadata || null,
      createdAt: now,
      updatedAt: now,
    });

    this.notices.push(notice);

    if (!notice.scheduledAt) {
      await this.sendNotice(notice);
    }

    return notice;
  }

  async findAll(userId?: number): Promise<Notice[]> {
    const notices = userId
      ? this.notices.filter(notice => notice.userId === userId || notice.userId === null)
      : this.notices;

    return [...notices].sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
  }

  async findById(id: number): Promise<Notice> {
    const notice = this.notices.find(item => item.id === id);
    if (!notice) {
      throw new NotFoundException(`Notice with ID ${id} not found`);
    }
    return notice;
  }

  async markAsRead(id: number, userId?: number): Promise<Notice> {
    const notice = await this.findById(id);

    if (notice.userId && userId && notice.userId !== userId) {
      throw new NotFoundException(`Notice with ID ${id} not found`);
    }

    notice.status = NoticeStatus.READ;
    notice.readAt = new Date();
    notice.updatedAt = new Date();
    return notice;
  }

  async getUserUnreadCount(userId: number): Promise<number> {
    return this.notices.filter(notice => (
      (notice.userId === userId || notice.userId === null) &&
      notice.status === NoticeStatus.SENT
    )).length;
  }

  @Cron(CronExpression.EVERY_MINUTE)
  async handleScheduledNotices(): Promise<void> {
    const now = new Date();
    const pendingNotices = this.notices.filter(notice => (
      notice.status === NoticeStatus.PENDING &&
      notice.scheduledAt &&
      notice.scheduledAt <= now
    ));

    for (const notice of pendingNotices) {
      await this.sendNotice(notice);
    }

    if (pendingNotices.length > 0) {
      this.logger.log(`Processed ${pendingNotices.length} scheduled notices`);
    }
  }

  async sendSystemNotice(title: string, content: string, userId?: number): Promise<Notice> {
    return this.create({
      title,
      content,
      type: NoticeType.SYSTEM,
      userId,
    });
  }

  async sendBroadcast(title: string, content: string): Promise<Notice> {
    return this.create({
      title,
      content,
      type: NoticeType.BROADCAST,
    });
  }

  private async sendNotice(notice: Notice): Promise<void> {
    try {
      if (notice.userId) {
        this.noticeGateway.sendToUser(notice.userId, {
          type: 'notice',
          data: notice,
        });
      } else {
        this.noticeGateway.broadcast({
          type: 'notice',
          data: notice,
        });
      }

      notice.status = NoticeStatus.SENT;
      notice.sentAt = new Date();
      notice.updatedAt = new Date();
    } catch (error) {
      this.logger.error(`Failed to send notice ${notice.id}:`, error);
      notice.status = NoticeStatus.FAILED;
      notice.updatedAt = new Date();
    }
  }
}
