import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { OnEvent } from '@nestjs/event-emitter';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { cert, getApps, initializeApp } from 'firebase-admin/app';
import { getMessaging } from 'firebase-admin/messaging';
import {
  NotificationType,
  TripStatus,
  UserRole,
} from '../common/constants/enums';
import { APP_EVENTS } from '../common/constants/events';
import { User, UserDocument } from '../users/schemas/user.schema';
import {
  Notification,
  NotificationDocument,
} from './schemas/notification.schema';

@Injectable()
export class NotificationsService {
  private readonly logger = new Logger(NotificationsService.name);
  private fcmInitialized = false;

  constructor(
    @InjectModel(Notification.name)
    private readonly notificationModel: Model<NotificationDocument>,
    @InjectModel(User.name) private readonly userModel: Model<UserDocument>,
    private readonly config: ConfigService,
  ) {
    this.initFcm();
  }

  private initFcm() {
    const projectId = this.config.get<string>('FCM_PROJECT_ID');
    const clientEmail = this.config.get<string>('FCM_CLIENT_EMAIL');
    const privateKey = this.config
      .get<string>('FCM_PRIVATE_KEY')
      ?.replace(/\\n/g, '\n');

    if (!projectId || !clientEmail || !privateKey) {
      this.logger.warn('FCM not configured — push notifications will be logged only');
      return;
    }

    if (!getApps().length) {
      initializeApp({
        credential: cert({ projectId, clientEmail, privateKey }),
      });
    }
    this.fcmInitialized = true;
  }

  async sendToUser(
    userId: string,
    type: NotificationType,
    title: string,
    body: string,
    data?: Record<string, string>,
  ) {
    const user = await this.userModel.findById(userId).exec();
    if (!user) return;

    await this.notificationModel.create({
      userId,
      type,
      title,
      body,
      data,
    });

    if (!user.fcmToken) return;

    if (this.fcmInitialized) {
      try {
        await getMessaging().send({
          token: user.fcmToken,
          notification: { title, body },
          data,
        });
      } catch (e) {
        this.logger.error(`FCM send failed: ${(e as Error).message}`);
      }
    } else {
      this.logger.log(`[FCM stub] ${title}: ${body}`);
    }
  }

  async notifyManagers(
    userId: string,
    type: NotificationType,
    title: string,
    body: string,
  ) {
    const user = await this.userModel.findById(userId).exec();
    if (!user) return;

    const managers = await this.userModel
      .find({
        $or: [
          { role: { $in: [UserRole.SUPER_ADMIN, UserRole.ADMIN, UserRole.HOD] } },
          ...(user.reportingManagerId
            ? [{ _id: user.reportingManagerId }]
            : []),
        ],
      })
      .exec();

    for (const mgr of managers) {
      await this.sendToUser(mgr._id.toString(), type, title, body, {
        employeeUid: user.uid,
      });
    }
  }

  @OnEvent(APP_EVENTS.TRIP_STATUS_CHANGED)
  async onTripStatus(trip: { tripId: string; userId: string; status: TripStatus }) {
    const map: Partial<
      Record<TripStatus, { type: NotificationType; title: string; body: string }>
    > = {
      [TripStatus.STARTED]: {
        type: NotificationType.TRIP_STARTED,
        title: 'Trip Started',
        body: 'Employee has started a trip',
      },
      [TripStatus.ARRIVED]: {
        type: NotificationType.ARRIVAL_COMPLETED,
        title: 'Arrival Completed',
        body: 'Employee has arrived at destination',
      },
      [TripStatus.MEETING_STARTED]: {
        type: NotificationType.MEETING_STARTED,
        title: 'Meeting Started',
        body: 'Employee has started a meeting',
      },
      [TripStatus.MEETING_COMPLETED]: {
        type: NotificationType.MEETING_ENDED,
        title: 'Meeting Ended',
        body: 'Employee has completed a meeting',
      },
    };

    const msg = map[trip.status];
    if (msg) {
      await this.notifyManagers(trip.userId, msg.type, msg.title, msg.body);
    }
  }

  @OnEvent(APP_EVENTS.EMPLOYEE_OFFLINE)
  async onEmployeeOffline(payload: { userId: string }) {
    await this.notifyManagers(
      payload.userId,
      NotificationType.EMPLOYEE_OFFLINE,
      'Employee Offline',
      'Employee has gone offline',
    );
  }
}
