// =============================================================================
// Notification Service
// =============================================================================

import pool from '../config/database';
import logger from '../utils/logger';
import { Notification, CreateNotificationData } from '../models/notification.model';

function rowToNotification(row: any): Notification {
  return {
    id:          row.id,
    user_id:     row.user_id,
    typ:         row.typ,
    titel:       row.titel,
    nachricht:   row.nachricht,
    schwere:     row.schwere,
    gelesen:     row.gelesen,
    gelesen_am:  row.gelesen_am ? new Date(row.gelesen_am) : null,
    link:        row.link ?? null,
    quell_id:    row.quell_id ?? null,
    quell_typ:   row.quell_typ ?? null,
    erstellt_am: new Date(row.erstellt_am),
  };
}

class NotificationService {
  /** Returns all notifications for a user (newest first, max 100). */
  async getByUser(userId: string): Promise<Notification[]> {
    try {
      const result = await pool.query(
        `SELECT * FROM notifications WHERE user_id = $1 ORDER BY erstellt_am DESC LIMIT 100`,
        [userId]
      );
      return result.rows.map(rowToNotification);
    } catch (error) {
      logger.error('NotificationService.getByUser failed', { error, userId });
      throw new Error('Notifications konnten nicht geladen werden');
    }
  }

  /** Returns the count of unread notifications for a user. */
  async getUnreadCount(userId: string): Promise<number> {
    try {
      const result = await pool.query(
        `SELECT COUNT(*) AS cnt FROM notifications WHERE user_id = $1 AND gelesen = FALSE`,
        [userId]
      );
      return parseInt(result.rows[0].cnt, 10);
    } catch (error) {
      logger.error('NotificationService.getUnreadCount failed', { error, userId });
      throw new Error('Ungelesene Notifications konnten nicht gezählt werden');
    }
  }

  /** Marks a single notification as read. Returns false if not found or not owned by user. */
  async markAsRead(id: string, userId: string): Promise<boolean> {
    try {
      const result = await pool.query(
        `UPDATE notifications
         SET gelesen = TRUE, gelesen_am = NOW()
         WHERE id = $1 AND user_id = $2 AND gelesen = FALSE
         RETURNING id`,
        [id, userId]
      );
      return (result.rowCount ?? 0) > 0;
    } catch (error) {
      logger.error('NotificationService.markAsRead failed', { error, id, userId });
      throw new Error('Notification konnte nicht als gelesen markiert werden');
    }
  }

  /** Marks all notifications as read for a user. */
  async markAllRead(userId: string): Promise<void> {
    try {
      await pool.query(
        `UPDATE notifications SET gelesen = TRUE, gelesen_am = NOW()
         WHERE user_id = $1 AND gelesen = FALSE`,
        [userId]
      );
    } catch (error) {
      logger.error('NotificationService.markAllRead failed', { error, userId });
      throw new Error('Notifications konnten nicht als gelesen markiert werden');
    }
  }

  /**
   * Creates a notification.  If quell_typ + quell_id are provided the insert is
   * silently ignored when an identical unread notification already exists
   * (dedup via unique index).
   */
  async createNotification(data: CreateNotificationData): Promise<void> {
    try {
      await pool.query(
        `INSERT INTO notifications
           (user_id, typ, titel, nachricht, schwere, link, quell_id, quell_typ)
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
         ON CONFLICT (user_id, quell_typ, quell_id) WHERE NOT gelesen AND quell_typ IS NOT NULL AND quell_id IS NOT NULL
         DO NOTHING`,
        [
          data.user_id,
          data.typ,
          data.titel,
          data.nachricht,
          data.schwere ?? 'info',
          data.link    ?? null,
          data.quell_id  ?? null,
          data.quell_typ ?? null,
        ]
      );
    } catch (error) {
      logger.error('NotificationService.createNotification failed', { error });
      // Non-fatal — don't propagate to callers
    }
  }

  /** Marks all unread notifications of a given quell_typ as read for a user. */
  async dismissByType(userId: string, quellTyp: string): Promise<void> {
    try {
      await pool.query(
        `UPDATE notifications SET gelesen = TRUE, gelesen_am = NOW()
         WHERE user_id = $1 AND quell_typ = $2 AND gelesen = FALSE`,
        [userId, quellTyp]
      );
    } catch (error) {
      logger.error('NotificationService.dismissByType failed', { error, userId, quellTyp });
      throw new Error('Notifications konnten nicht als gelesen markiert werden');
    }
  }

  /** Deletes all read notifications for a user. */
  async deleteAllRead(userId: string): Promise<void> {
    try {
      await pool.query(
        `DELETE FROM notifications WHERE user_id = $1 AND gelesen = TRUE`,
        [userId]
      );
    } catch (error) {
      logger.error('NotificationService.deleteAllRead failed', { error, userId });
      throw new Error('Gelesene Notifications konnten nicht gelöscht werden');
    }
  }

  /** Deletes read notifications older than 90 days for all users. */
  async deleteOldRead(): Promise<void> {
    try {
      const result = await pool.query(
        `DELETE FROM notifications WHERE gelesen = TRUE AND gelesen_am < NOW() - INTERVAL '90 days'`
      );
      if ((result.rowCount ?? 0) > 0) {
        logger.info(`NotificationService.deleteOldRead: removed ${result.rowCount} old notifications`);
      }
    } catch (error) {
      logger.error('NotificationService.deleteOldRead failed', { error });
    }
  }
}

export default new NotificationService();
