import { Repository, FindOptionsWhere } from 'typeorm';
import {
  Channel,
  NotificationTemplate,
  NotificationPreference,
  Notification,
  NotificationBatch,
  InAppNotification,
} from '../entities';

export class NotificationsService {
  constructor(
    private readonly channelRepository: Repository<Channel>,
    private readonly templateRepository: Repository<NotificationTemplate>,
    private readonly preferenceRepository: Repository<NotificationPreference>,
    private readonly notificationRepository: Repository<Notification>,
    private readonly batchRepository: Repository<NotificationBatch>,
    private readonly inAppRepository: Repository<InAppNotification>
  ) {}

  // ============================================
  // CHANNELS
  // ============================================

  async findAllChannels(): Promise<Channel[]> {
    return this.channelRepository.find({
      where: { isActive: true },
      order: { name: 'ASC' },
    });
  }

  async findChannelByCode(code: string): Promise<Channel | null> {
    return this.channelRepository.findOne({ where: { code } });
  }

  // ============================================
  // TEMPLATES
  // ============================================

  async findAllTemplates(tenantId: string): Promise<NotificationTemplate[]> {
    return this.templateRepository.find({
      where: [{ tenantId }, { tenantId: undefined, isActive: true }],
      relations: ['translations'],
      order: { name: 'ASC' },
    });
  }

  async findTemplateByCode(
    code: string,
    channelType: string,
    tenantId?: string
  ): Promise<NotificationTemplate | null> {
    const channel = channelType as any;
    const where: FindOptionsWhere<NotificationTemplate>[] = tenantId
      ? [{ code, channelType: channel, tenantId }, { code, channelType: channel, tenantId: undefined as any }]
      : [{ code, channelType: channel }];

    return this.templateRepository.findOne({
      where,
      relations: ['translations'],
      order: { tenantId: 'DESC' },
    });
  }

  async createTemplate(
    tenantId: string,
    data: Partial<NotificationTemplate>,
    createdBy?: string
  ): Promise<NotificationTemplate> {
    const template = this.templateRepository.create({
      ...data,
      tenantId,
      createdBy,
    });
    return this.templateRepository.save(template);
  }

  // ============================================
  // PREFERENCES
  // ============================================

  async getPreferences(userId: string, tenantId: string): Promise<NotificationPreference | null> {
    return this.preferenceRepository.findOne({
      where: { userId, tenantId },
    });
  }

  async updatePreferences(
    userId: string,
    tenantId: string,
    data: Partial<NotificationPreference>
  ): Promise<NotificationPreference> {
    let preferences = await this.getPreferences(userId, tenantId);

    if (!preferences) {
      preferences = this.preferenceRepository.create({
        userId,
        tenantId,
        ...data,
      });
    } else {
      Object.assign(preferences, data);
    }

    return this.preferenceRepository.save(preferences);
  }

  // ============================================
  // NOTIFICATIONS
  // ============================================

  async createNotification(
    tenantId: string,
    data: Partial<Notification>
  ): Promise<Notification> {
    const notification = this.notificationRepository.create({
      ...data,
      tenantId,
      status: 'pending',
    });
    return this.notificationRepository.save(notification);
  }

  async findPendingNotifications(limit: number = 100): Promise<Notification[]> {
    return this.notificationRepository.find({
      where: { status: 'pending' },
      order: { createdAt: 'ASC' },
      take: limit,
    });
  }

  async updateNotificationStatus(
    id: string,
    status: string,
    errorMessage?: string
  ): Promise<Notification | null> {
    const notification = await this.notificationRepository.findOne({ where: { id } });
    if (!notification) return null;

    notification.status = status as any;
    if (errorMessage) notification.errorMessage = errorMessage;
    if (status === 'sent') notification.sentAt = new Date();
    if (status === 'delivered') notification.deliveredAt = new Date();
    if (status === 'failed') notification.failedAt = new Date();

    return this.notificationRepository.save(notification);
  }

  // ============================================
  // IN-APP NOTIFICATIONS
  // ============================================

  async findInAppNotifications(
    userId: string,
    tenantId: string,
    includeRead: boolean = false
  ): Promise<InAppNotification[]> {
    const where: FindOptionsWhere<InAppNotification> = {
      userId,
      tenantId,
      isArchived: false,
    };

    if (!includeRead) {
      where.isRead = false;
    }

    return this.inAppRepository.find({
      where,
      order: { createdAt: 'DESC' },
      take: 50,
    });
  }

  async getUnreadCount(userId: string, tenantId: string): Promise<number> {
    return this.inAppRepository.count({
      where: {
        userId,
        tenantId,
        isRead: false,
        isArchived: false,
      },
    });
  }

  async markAsRead(id: string): Promise<boolean> {
    const notification = await this.inAppRepository.findOne({ where: { id } });
    if (!notification || notification.isRead) return false;

    notification.isRead = true;
    notification.readAt = new Date();
    await this.inAppRepository.save(notification);
    return true;
  }

  async markAllAsRead(userId: string, tenantId: string): Promise<number> {
    const result = await this.inAppRepository.update(
      { userId, tenantId, isRead: false },
      { isRead: true, readAt: new Date() }
    );
    return result.affected || 0;
  }

  async createInAppNotification(
    tenantId: string,
    userId: string,
    data: Partial<InAppNotification>
  ): Promise<InAppNotification> {
    const notification = this.inAppRepository.create({
      ...data,
      tenantId,
      userId,
    });
    return this.inAppRepository.save(notification);
  }
}
