import * as webpush from 'web-push';
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { PushSubscriptionEntity } from './push-subscription.entity';
import { SubscribeDto, UnsubscribeDto } from './push.dto';

export interface PushPayload {
  title: string;
  body: string;
  icon?: string;
  badge?: string;
  url?: string;
  tag?: string;
}

@Injectable()
export class PushService implements OnModuleInit {
  private readonly logger = new Logger(PushService.name);

  constructor(
    private configService: ConfigService,
    @InjectRepository(PushSubscriptionEntity)
    private subscriptionRepo: Repository<PushSubscriptionEntity>,
  ) {}

  onModuleInit() {
    const publicKey = this.configService.get<string>('VAPID_PUBLIC_KEY');
    const privateKey = this.configService.get<string>('VAPID_PRIVATE_KEY');
    const subject = this.configService.get<string>(
      'VAPID_SUBJECT',
      'mailto:contact@placebo.mk',
    );

    if (!publicKey || !privateKey) {
      this.logger.warn(
        'VAPID keys not configured. Push notifications will not work. Run: npm run generate-vapid',
      );
      return;
    }

    webpush.setVapidDetails(subject, publicKey, privateKey);
    this.logger.log('VAPID keys configured successfully');
  }

  getPublicKey(): string | null {
    return this.configService.get<string>('VAPID_PUBLIC_KEY') ?? null;
  }

  async subscribe(dto: SubscribeDto): Promise<PushSubscriptionEntity> {
    const existing = await this.subscriptionRepo.findOne({
      where: { endpoint: dto.endpoint },
    });

    if (existing) {
      if (dto.userId && existing.userId !== dto.userId) {
        existing.userId = dto.userId;
        return this.subscriptionRepo.save(existing);
      }
      return existing;
    }

    const subscription = this.subscriptionRepo.create({
      endpoint: dto.endpoint,
      p256dh: dto.p256dh,
      auth: dto.auth,
      userId: dto.userId ?? null,
    });

    return this.subscriptionRepo.save(subscription);
  }

  async unsubscribe(dto: UnsubscribeDto): Promise<void> {
    await this.subscriptionRepo.delete({ endpoint: dto.endpoint });
  }

  async sendToAll(
    payload: PushPayload,
  ): Promise<{ sent: number; failed: number }> {
    const subscriptions = await this.subscriptionRepo.find();

    if (subscriptions.length === 0) {
      return { sent: 0, failed: 0 };
    }

    const notification = JSON.stringify({
      title: payload.title,
      body: payload.body,
      icon: payload.icon ?? '/icons/icon-192.png',
      badge: payload.badge ?? '/icons/badge-72.png',
      url: payload.url ?? '/',
      tag: payload.tag,
    });

    let sent = 0;
    let failed = 0;

    const results = await Promise.allSettled(
      subscriptions.map(async (sub) => {
        try {
          await webpush.sendNotification(
            {
              endpoint: sub.endpoint,
              keys: {
                p256dh: sub.p256dh,
                auth: sub.auth,
              },
            },
            notification,
            {
              TTL: 86400,
            },
          );
          return { success: true, id: sub.id };
        } catch (error) {
          if (error instanceof Error) {
            this.logger.warn(`Push failed for ${sub.id}: ${error.message}`);
          }
          if (
            error instanceof webpush.WebPushError &&
            (error.statusCode === 410 || error.statusCode === 404)
          ) {
            await this.subscriptionRepo.delete({ id: sub.id });
            this.logger.log(`Removed invalid subscription: ${sub.id}`);
          }
          return { success: false, id: sub.id };
        }
      }),
    );

    for (const result of results) {
      if (result.status === 'fulfilled' && result.value.success) {
        sent++;
      } else {
        failed++;
      }
    }

    this.logger.log(`Push sent: ${sent}, failed: ${failed}`);
    return { sent, failed };
  }

  async notifyNewArticle(
    articleTitle: string,
    articleSlug: string,
  ): Promise<void> {
    await this.sendToAll({
      title: 'Нови вести! 📰',
      body: articleTitle,
      url: `/article/${articleSlug}`,
      tag: 'new-article',
    });
  }

  async notifyLiveBlogUpdate(
    blogTitle: string,
    blogSlug: string,
    updatePreview: string,
  ): Promise<void> {
    const body =
      updatePreview.length > 100
        ? updatePreview.substring(0, 97) + '...'
        : updatePreview;

    await this.sendToAll({
      title: `${blogTitle} 📡`,
      body,
      url: `/live-blog/${blogSlug}`,
      tag: `live-blog-${blogSlug}`,
    });
  }

  async getStats(): Promise<{ totalSubscribers: number }> {
    const totalSubscribers = await this.subscriptionRepo.count();
    return { totalSubscribers };
  }
}
