import { query, queryOne } from '../../config/database.js';
import { NotFoundError } from '../../shared/errors/index.js';

export interface Notification {
  id: string;
  tenant_id: string;
  user_id: string;
  title: string;
  message: string;
  url?: string;
  model?: string;
  record_id?: string;
  status: 'pending' | 'sent' | 'read' | 'failed';
  read_at?: Date;
  created_at: Date;
  sent_at?: Date;
}

export interface CreateNotificationDto {
  user_id: string;
  title: string;
  message: string;
  url?: string;
  model?: string;
  record_id?: string;
}

export interface NotificationFilters {
  user_id?: string;
  status?: string;
  unread_only?: boolean;
  model?: string;
  search?: string;
  page?: number;
  limit?: number;
}

class NotificationsService {
  async findAll(tenantId: string, filters: NotificationFilters = {}): Promise<{ data: Notification[]; total: number }> {
    const { user_id, status, unread_only, model, search, page = 1, limit = 50 } = filters;
    const offset = (page - 1) * limit;

    let whereClause = 'WHERE n.tenant_id = $1';
    const params: any[] = [tenantId];
    let paramIndex = 2;

    if (user_id) {
      whereClause += ` AND n.user_id = $${paramIndex++}`;
      params.push(user_id);
    }

    if (status) {
      whereClause += ` AND n.status = $${paramIndex++}`;
      params.push(status);
    }

    if (unread_only) {
      whereClause += ` AND n.read_at IS NULL`;
    }

    if (model) {
      whereClause += ` AND n.model = $${paramIndex++}`;
      params.push(model);
    }

    if (search) {
      whereClause += ` AND (n.title ILIKE $${paramIndex} OR n.message ILIKE $${paramIndex})`;
      params.push(`%${search}%`);
      paramIndex++;
    }

    const countResult = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count FROM system.notifications n ${whereClause}`,
      params
    );

    params.push(limit, offset);
    const data = await query<Notification>(
      `SELECT n.*
       FROM system.notifications n
       ${whereClause}
       ORDER BY n.created_at DESC
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

    return {
      data,
      total: parseInt(countResult?.count || '0', 10),
    };
  }

  async findByUser(userId: string, tenantId: string, unreadOnly: boolean = false): Promise<Notification[]> {
    let whereClause = 'WHERE n.user_id = $1 AND n.tenant_id = $2';
    if (unreadOnly) {
      whereClause += ' AND n.read_at IS NULL';
    }

    const notifications = await query<Notification>(
      `SELECT n.*
       FROM system.notifications n
       ${whereClause}
       ORDER BY n.created_at DESC
       LIMIT 100`,
      [userId, tenantId]
    );

    return notifications;
  }

  async getUnreadCount(userId: string, tenantId: string): Promise<number> {
    const result = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count
       FROM system.notifications
       WHERE user_id = $1 AND tenant_id = $2 AND read_at IS NULL`,
      [userId, tenantId]
    );

    return parseInt(result?.count || '0', 10);
  }

  async findById(id: string, tenantId: string): Promise<Notification> {
    const notification = await queryOne<Notification>(
      `SELECT n.*
       FROM system.notifications n
       WHERE n.id = $1 AND n.tenant_id = $2`,
      [id, tenantId]
    );

    if (!notification) {
      throw new NotFoundError('Notificación no encontrada');
    }

    return notification;
  }

  async create(dto: CreateNotificationDto, tenantId: string): Promise<Notification> {
    const notification = await queryOne<Notification>(
      `INSERT INTO system.notifications (
        tenant_id, user_id, title, message, url, model, record_id, status, sent_at
      )
       VALUES ($1, $2, $3, $4, $5, $6, $7, 'sent', CURRENT_TIMESTAMP)
       RETURNING *`,
      [tenantId, dto.user_id, dto.title, dto.message, dto.url, dto.model, dto.record_id]
    );

    return notification!;
  }

  async createBulk(notifications: CreateNotificationDto[], tenantId: string): Promise<number> {
    if (notifications.length === 0) return 0;

    const values = notifications.map((n, i) => {
      const base = i * 7;
      return `($${base + 1}, $${base + 2}, $${base + 3}, $${base + 4}, $${base + 5}, $${base + 6}, $${base + 7}, 'sent', CURRENT_TIMESTAMP)`;
    }).join(', ');

    const params = notifications.flatMap(n => [
      tenantId, n.user_id, n.title, n.message, n.url, n.model, n.record_id
    ]);

    const result = await query(
      `INSERT INTO system.notifications (
        tenant_id, user_id, title, message, url, model, record_id, status, sent_at
      )
       VALUES ${values}`,
      params
    );

    return notifications.length;
  }

  async markAsRead(id: string, tenantId: string): Promise<Notification> {
    await this.findById(id, tenantId);

    const notification = await queryOne<Notification>(
      `UPDATE system.notifications SET
         status = 'read',
         read_at = CURRENT_TIMESTAMP
       WHERE id = $1 AND tenant_id = $2
       RETURNING *`,
      [id, tenantId]
    );

    return notification!;
  }

  async markAllAsRead(userId: string, tenantId: string): Promise<number> {
    const result = await query(
      `UPDATE system.notifications SET
         status = 'read',
         read_at = CURRENT_TIMESTAMP
       WHERE user_id = $1 AND tenant_id = $2 AND read_at IS NULL`,
      [userId, tenantId]
    );

    return result.length;
  }

  async delete(id: string, tenantId: string): Promise<void> {
    await this.findById(id, tenantId);

    await query(
      `DELETE FROM system.notifications WHERE id = $1 AND tenant_id = $2`,
      [id, tenantId]
    );
  }

  async deleteOld(daysToKeep: number = 30, tenantId?: string): Promise<number> {
    let whereClause = `WHERE read_at IS NOT NULL AND created_at < CURRENT_TIMESTAMP - INTERVAL '${daysToKeep} days'`;
    const params: any[] = [];

    if (tenantId) {
      whereClause += ' AND tenant_id = $1';
      params.push(tenantId);
    }

    const result = await query(
      `DELETE FROM system.notifications ${whereClause}`,
      params
    );

    return result.length;
  }
}

export const notificationsService = new NotificationsService();
