import { join } from 'path';
import { config } from '../../utils/config.js';
import { readJSON, atomicWriteJSON, ensureDir } from '../data-store/file-ops.js';
import { createModuleLogger } from '../../utils/logger.js';
import type { Notification } from '../../types/notification.js';

const log = createModuleLogger('notification');

const MAX_STORED = 200;

export class NotificationService {
  private filePath: string;

  constructor(dataDir?: string) {
    const base = dataDir ?? config.dataDir;
    this.filePath = join(base, 'notifications', 'notifications.json');
  }

  async init(): Promise<void> {
    const dir = join(this.filePath, '..');
    await ensureDir(dir);
  }

  private async load(): Promise<Notification[]> {
    const data = await readJSON<Notification[]>(this.filePath);
    return data ?? [];
  }

  private async save(notifications: Notification[]): Promise<void> {
    await atomicWriteJSON(this.filePath, notifications);
  }

  async create(
    input: Omit<Notification, 'id' | 'read' | 'createdAt'>
  ): Promise<Notification> {
    const notifications = await this.load();

    const notification: Notification = {
      ...input,
      id: crypto.randomUUID(),
      read: false,
      createdAt: new Date().toISOString(),
    };

    notifications.push(notification);

    // Trim to MAX_STORED, keeping newest
    const trimmed =
      notifications.length > MAX_STORED
        ? notifications
            .sort(
              (a, b) =>
                new Date(b.createdAt).getTime() -
                new Date(a.createdAt).getTime()
            )
            .slice(0, MAX_STORED)
        : notifications;

    await this.save(trimmed);
    log.info({ id: notification.id, type: notification.type }, 'Notification created');
    return notification;
  }

  async list(
    options?: { limit?: number; unreadOnly?: boolean }
  ): Promise<Notification[]> {
    const limit = options?.limit ?? 50;
    const unreadOnly = options?.unreadOnly ?? false;

    let notifications = await this.load();

    if (unreadOnly) {
      notifications = notifications.filter((n) => !n.read);
    }

    return notifications
      .sort(
        (a, b) =>
          new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
      )
      .slice(0, limit);
  }

  async markAsRead(id: string): Promise<void> {
    const notifications = await this.load();
    const target = notifications.find((n) => n.id === id);
    if (!target) {
      log.warn({ id }, 'Notification not found for markAsRead');
      return;
    }
    target.read = true;
    await this.save(notifications);
  }

  async markAllAsRead(): Promise<void> {
    const notifications = await this.load();
    for (const n of notifications) {
      n.read = true;
    }
    await this.save(notifications);
  }

  async getUnreadCount(): Promise<number> {
    const notifications = await this.load();
    return notifications.filter((n) => !n.read).length;
  }
}
