/**
 * TypeORM Notification Repository Implementation
 */

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, LessThan } from 'typeorm';
import {
  NotificationRepository,
  NotificationFilters,
} from '@domain/ports/out/notification.repository';
import { Notification } from '@domain/entities/notification.entity';
import { NotificationOrmEntity } from '../entities/notification.orm-entity';

@Injectable()
export class TypeOrmNotificationRepository implements NotificationRepository {
  constructor(
    @InjectRepository(NotificationOrmEntity)
    private readonly ormRepository: Repository<NotificationOrmEntity>
  ) {}

  async save(notification: Notification): Promise<void> {
    const ormEntity = this.toOrm(notification);
    await this.ormRepository.save(ormEntity);
  }

  async findById(id: string): Promise<Notification | null> {
    const ormEntity = await this.ormRepository.findOne({ where: { id } });
    return ormEntity ? this.toDomain(ormEntity) : null;
  }

  async findByFilters(filters: NotificationFilters): Promise<Notification[]> {
    const query = this.ormRepository.createQueryBuilder('notification');

    if (filters.userId) {
      query.andWhere('notification.user_id = :userId', { userId: filters.userId });
    }

    if (filters.organizationId) {
      query.andWhere('notification.organization_id = :organizationId', {
        organizationId: filters.organizationId,
      });
    }

    if (filters.type && filters.type.length > 0) {
      query.andWhere('notification.type IN (:...types)', { types: filters.type });
    }

    if (filters.read !== undefined) {
      query.andWhere('notification.read = :read', { read: filters.read });
    }

    if (filters.priority && filters.priority.length > 0) {
      query.andWhere('notification.priority IN (:...priorities)', {
        priorities: filters.priority,
      });
    }

    if (filters.startDate) {
      query.andWhere('notification.created_at >= :startDate', {
        startDate: filters.startDate,
      });
    }

    if (filters.endDate) {
      query.andWhere('notification.created_at <= :endDate', {
        endDate: filters.endDate,
      });
    }

    query.orderBy('notification.created_at', 'DESC');

    if (filters.offset) {
      query.skip(filters.offset);
    }

    if (filters.limit) {
      query.take(filters.limit);
    }

    const ormEntities = await query.getMany();
    return ormEntities.map(e => this.toDomain(e));
  }

  async count(filters: NotificationFilters): Promise<number> {
    const query = this.ormRepository.createQueryBuilder('notification');

    if (filters.userId) {
      query.andWhere('notification.user_id = :userId', { userId: filters.userId });
    }

    if (filters.organizationId) {
      query.andWhere('notification.organization_id = :organizationId', {
        organizationId: filters.organizationId,
      });
    }

    if (filters.type && filters.type.length > 0) {
      query.andWhere('notification.type IN (:...types)', { types: filters.type });
    }

    if (filters.read !== undefined) {
      query.andWhere('notification.read = :read', { read: filters.read });
    }

    if (filters.priority && filters.priority.length > 0) {
      query.andWhere('notification.priority IN (:...priorities)', {
        priorities: filters.priority,
      });
    }

    if (filters.startDate) {
      query.andWhere('notification.created_at >= :startDate', {
        startDate: filters.startDate,
      });
    }

    if (filters.endDate) {
      query.andWhere('notification.created_at <= :endDate', {
        endDate: filters.endDate,
      });
    }

    return query.getCount();
  }

  async findUnreadByUser(userId: string, limit: number = 50): Promise<Notification[]> {
    const ormEntities = await this.ormRepository.find({
      where: { user_id: userId, read: false },
      order: { created_at: 'DESC' },
      take: limit,
    });

    return ormEntities.map(e => this.toDomain(e));
  }

  async countUnreadByUser(userId: string): Promise<number> {
    return this.ormRepository.count({
      where: { user_id: userId, read: false },
    });
  }

  async findRecentByUser(userId: string, limit: number = 50): Promise<Notification[]> {
    const ormEntities = await this.ormRepository.find({
      where: { user_id: userId },
      order: { created_at: 'DESC' },
      take: limit,
    });

    return ormEntities.map(e => this.toDomain(e));
  }

  async markAsRead(id: string): Promise<void> {
    await this.ormRepository.update(id, {
      read: true,
      read_at: new Date(),
    });
  }

  async markAllAsReadForUser(userId: string): Promise<void> {
    await this.ormRepository.update(
      { user_id: userId, read: false },
      {
        read: true,
        read_at: new Date(),
      }
    );
  }

  async delete(id: string): Promise<void> {
    await this.ormRepository.delete(id);
  }

  async deleteOldReadNotifications(olderThanDays: number): Promise<number> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - olderThanDays);

    const result = await this.ormRepository.delete({
      read: true,
      read_at: LessThan(cutoffDate),
    });

    return result.affected || 0;
  }

  private toDomain(ormEntity: NotificationOrmEntity): Notification {
    return Notification.fromPersistence({
      id: ormEntity.id,
      userId: ormEntity.user_id,
      organizationId: ormEntity.organization_id,
      type: ormEntity.type as any,
      priority: ormEntity.priority as any,
      title: ormEntity.title,
      message: ormEntity.message,
      metadata: ormEntity.metadata,
      read: ormEntity.read,
      readAt: ormEntity.read_at,
      actionUrl: ormEntity.action_url,
      createdAt: ormEntity.created_at,
    });
  }

  private toOrm(notification: Notification): NotificationOrmEntity {
    const ormEntity = new NotificationOrmEntity();
    ormEntity.id = notification.id;
    ormEntity.user_id = notification.userId;
    ormEntity.organization_id = notification.organizationId;
    ormEntity.type = notification.type;
    ormEntity.priority = notification.priority;
    ormEntity.title = notification.title;
    ormEntity.message = notification.message;
    ormEntity.metadata = notification.metadata;
    ormEntity.read = notification.read;
    ormEntity.read_at = notification.readAt;
    ormEntity.action_url = notification.actionUrl;
    ormEntity.created_at = notification.createdAt;
    return ormEntity;
  }
}
