/**
 * Notification Service
 *
 * Central service for managing all admin notifications via Telegram.
 * Handles notification creation, queuing, and sending with proper
 * error handling and database persistence.
 */

import { prisma } from '../../shared/database/prisma.client';
import { logger } from '../../shared/utils/logger';
import { NotificationType } from '@prisma/client';
import { telegramClient } from './telegram/telegram.client';
import { generateNewUserAlert, generateSystemErrorAlert, generateDailySummaryAlert } from './telegram/templates/alert.template';
import { generateModuleCompletionMessage } from './telegram/templates/progress.template';
import { generateTop10EntryMessage } from './telegram/templates/achievement.template';

/**
 * Notification payload interface
 */
export interface NotificationPayload {
  type: NotificationType;
  title: string;
  message: string;
  userId: string;
  priority?: number;
  metadata?: Record<string, any>;
}

/**
 * Create notification result
 */
export interface CreateNotificationResult {
  success: boolean;
  notificationId?: string;
  error?: string;
}

/**
 * Send notification result
 */
export interface SendNotificationResult {
  success: boolean;
  notificationId: string;
  messageId?: number;
  error?: string;
  attempts: number;
}

/**
 * Notification statistics
 */
export interface NotificationStatistics {
  total: number;
  pending: number;
  sent: number;
  failed: number;
  todaySent: number;
  todayFailed: number;
}

/**
 * Notification Service Class
 */
class NotificationService {
  private static instance: NotificationService;

  private constructor() {}

  /**
    * Get singleton instance
    */
  public static getInstance(): NotificationService {
    if (!NotificationService.instance) {
      NotificationService.instance = new NotificationService();
    }
    return NotificationService.instance;
  }

  /**
   * Notify admin about new user registration
   */
  public async notifyNewUser(data: {
    userId?: string;
    anonymousId: string;
    username?: string;
    email?: string;
    registeredAt: string;
    ipAddress?: string;
    userAgent?: string;
  }): Promise<CreateNotificationResult> {
    try {
      const message = generateNewUserAlert(data);

      const notification = await this.createNotification({
        type: 'NEW_USER',
        title: 'Nuevo Usuario Registrado',
        message,
        userId: data.userId || 'system',
        priority: 5,
        metadata: {
          userId: data.userId,
          anonymousId: data.anonymousId,
          username: data.username,
          registeredAt: data.registeredAt,
        },
      });

      if (notification) {
        // Queue for sending
        await this.queueNotification(notification.id);
        return { success: true, notificationId: notification.id };
      }

      return { success: false, error: 'Failed to create notification' };
    } catch (error: any) {
      logger.error({ error, data }, 'Failed to notify new user');
      return { success: false, error: error.message };
    }
  }

  /**
   * Notify admin about module completion
   */
  public async notifyModuleCompleted(data: {
    userId?: string;
    anonymousId: string;
    moduleName: string;
    moduleType: string;
    finalScore: number;
    totalPoints: number;
    exercisesCompleted: number;
    perfectExercises?: number;
    timeSpentMinutes: number;
    startedAt: string;
    completedAt: string;
  }): Promise<CreateNotificationResult> {
    try {
      const message = generateModuleCompletionMessage({
        ...data,
        perfectExercises: data.perfectExercises ?? 0,
      });

      const notification = await this.createNotification({
        type: 'MODULE_COMPLETED',
        title: 'Módulo Completado',
        message,
        userId: data.userId || 'system',
        priority: 3,
        metadata: {
          userId: data.userId,
          anonymousId: data.anonymousId,
          moduleName: data.moduleName,
          finalScore: data.finalScore,
        },
      });

      if (notification) {
        await this.queueNotification(notification.id);
        return { success: true, notificationId: notification.id };
      }

      return { success: false, error: 'Failed to create notification' };
    } catch (error: any) {
      logger.error({ error, data }, 'Failed to notify module completion');
      return { success: false, error: error.message };
    }
  }

  /**
   * Notify admin about top 10 ranking entry
   */
  public async notifyTop10Entry(data: {
    userId?: string;
    anonymousId: string;
    position: number;
    points: number;
    exercisesCompleted: number;
    streak?: number;
    moduleName?: string;
  }): Promise<CreateNotificationResult> {
    try {
      const message = generateTop10EntryMessage({
        ...data,
        newPosition: data.position,
        isTop10: true,
      });

      const notification = await this.createNotification({
        type: 'RANKING_CHANGED',
        title: 'Top 10 Alcanzado',
        message,
        userId: data.userId || 'system',
        priority: 8,
        metadata: {
          userId: data.userId,
          anonymousId: data.anonymousId,
          position: data.position,
          points: data.points,
        },
      });

      if (notification) {
        await this.queueNotification(notification.id);
        return { success: true, notificationId: notification.id };
      }

      return { success: false, error: 'Failed to create notification' };
    } catch (error: any) {
      logger.error({ error, data }, 'Failed to notify top 10 entry');
      return { success: false, error: error.message };
    }
  }

  /**
   * Notify admin about system error
   */
  public async notifySystemError(data: {
    errorType: string;
    errorMessage: string;
    stackTrace?: string;
    path?: string;
    method?: string;
    statusCode?: number;
    userId?: string;
    anonymousId?: string;
    metadata?: Record<string, any>;
  }): Promise<CreateNotificationResult> {
    try {
      const message = generateSystemErrorAlert({
        ...data,
        timestamp: new Date().toISOString(),
      });

      const notification = await this.createNotification({
        type: 'SYSTEM_ERROR',
        title: `Error: ${data.errorType}`,
        message,
        userId: data.userId || 'system',
        priority: 10,
        metadata: {
          errorType: data.errorType,
          path: data.path,
          method: data.method,
          statusCode: data.statusCode,
          userId: data.userId,
          anonymousId: data.anonymousId,
        },
      });

      if (notification) {
        // High priority - send immediately
        await this.sendNotification(notification.id);
        return { success: true, notificationId: notification.id };
      }

      return { success: false, error: 'Failed to create notification' };
    } catch (error: any) {
      logger.error({ error, data }, 'Failed to notify system error');
      return { success: false, error: error.message };
    }
  }

  /**
   * Send system notification (generic)
   */
  public async sendSystemNotification(
    title: string,
    message: string,
    metadata?: Record<string, any>
  ): Promise<CreateNotificationResult> {
    try {
      const notification = await this.createNotification({
        type: 'SYSTEM_ERROR',
        title,
        message,
        userId: 'system',
        priority: 5,
        ...(metadata !== undefined && { metadata }),
      });

      if (notification?.id) {
        return { success: true, notificationId: notification.id };
      }
      return { success: false, error: 'Failed to create notification' };
    } catch (error: any) {
      logger.error({ error, title, message }, 'Failed to send system notification');
      return { success: false, error: error.message };
    }
  }

  /**
   * Generate and send daily summary
   */
  public async sendDailySummary(date: Date = new Date()): Promise<CreateNotificationResult> {
    try {
      const startOfDay = new Date(date);
      startOfDay.setHours(0, 0, 0, 0);

      const endOfDay = new Date(date);
      endOfDay.setHours(23, 59, 59, 999);

      // Gather statistics
      const [
        totalUsers,
        newUsers,
        activeUsers,
        modulesCompleted,
        exercisesCompleted,
        achievementsUnlocked,
        failedNotifications,
      ] = await Promise.all([
        prisma.user.count(),
        prisma.user.count({
          where: { createdAt: { gte: startOfDay, lte: endOfDay } },
        }),
        prisma.user.count({
          where: { lastLoginAt: { gte: startOfDay } },
        }),
        prisma.progress.count({
          where: {
            isCompleted: true,
            completedAt: { gte: startOfDay, lte: endOfDay },
          },
        }),
        prisma.exerciseAttempt.count({
          where: { createdAt: { gte: startOfDay, lte: endOfDay } },
        }),
        prisma.userAchievement.count({
          where: { unlockedAt: { gte: startOfDay, lte: endOfDay } },
        }),
        prisma.notification.count({
          where: {
            status: 'FAILED',
            createdAt: { gte: startOfDay, lte: endOfDay },
          },
        }),
      ]);

      // Get top performers
      const topRankings = await prisma.ranking.findMany({
        where: { moduleId: null },
        orderBy: { points: 'desc' },
        take: 5,
        include: { user: true },
      });

      const topPerformers = topRankings.map(r => ({
        anonymousId: `Usuario#${r.id.substring(0, 6)}`,
        points: r.points,
        exercisesCompleted: r.exercisesCompleted,
      }));

      // Get average session time (from progress)
      const progressRecords = await prisma.progress.findMany({
        where: { lastAccessedAt: { gte: startOfDay } },
      });
      const avgTimeSeconds = progressRecords.length > 0
        ? progressRecords.reduce((sum, p) => sum + p.totalTimeSpent, 0) / progressRecords.length
        : 0;

      const systemHealth: 'healthy' | 'degraded' | 'critical' =
        failedNotifications > 10 ? 'critical' :
        failedNotifications > 5 ? 'degraded' : 'healthy';

      const message = generateDailySummaryAlert({
        date: date.toISOString(),
        totalUsers,
        newUsers,
        activeUsers,
        modulesCompleted,
        exercisesCompleted,
        achievementsUnlocked,
        averageSessionTime: avgTimeSeconds,
        topPerformers,
        errorsCount: failedNotifications,
        systemHealth,
      });

      const notification = await this.createNotification({
        type: 'DAILY_SUMMARY',
        title: 'Resumen Diario',
        message,
        userId: 'system',
        priority: 2,
        metadata: {
          date: date.toISOString(),
          totalUsers,
          activeUsers,
          systemHealth,
        },
      });

      if (notification) {
        await this.queueNotification(notification.id);
        return { success: true, notificationId: notification.id };
      }

      return { success: false, error: 'Failed to create notification' };
    } catch (error: any) {
      logger.error({ error }, 'Failed to send daily summary');
      return { success: false, error: error.message };
    }
  }

  /**
   * Create notification in database
   */
  private async createNotification(
    payload: NotificationPayload
  ): Promise<{ id: string } | null> {
    try {
      const notification = await prisma.notification.create({
        data: {
          type: payload.type,
          title: payload.title,
          message: payload.message,
           user_id: payload.userId,
          priority: payload.priority || 0,
          status: 'PENDING',
          metadata: payload.metadata || {},
        },
      });

      logger.info({
        notificationId: notification.id,
        type: notification.type,
      }, 'Notification created');

      return { id: notification.id };
    } catch (error: any) {
      logger.error({ error, payload }, 'Failed to create notification in database');
      return null;
    }
  }

  /**
   * Queue notification for async sending
   */
  private async queueNotification(notificationId: string): Promise<void> {
    try {
      // Worker disabled temporarily
      // const { queueNotification: queueToWorker } = await import('../../workers/notification-sender.worker');
      // await queueToWorker(notificationId);
      logger.debug({ notificationId }, 'Notification queued (worker disabled)');
    } catch (error: any) {
      logger.error({ error, notificationId }, 'Failed to queue notification');
    }
  }

  /**
   * Send notification immediately
   */
  private async sendNotification(notificationId: string): Promise<SendNotificationResult> {
    try {
      // Get notification from database
      const notification = await prisma.notification.findUnique({
        where: { id: notificationId },
      });

      if (!notification) {
        return {
          success: false,
          notificationId,
          error: 'Notification not found',
          attempts: 0,
        };
      }

      // Update attempt count
      await prisma.notification.update({
        where: { id: notificationId },
        data: {
          attempts: { increment: 1 },
          lastAttemptAt: new Date(),
        },
      });

      // Send via Telegram
      const result = await telegramClient.sendToAdmin(notification.message);

      if (result.success) {
        // Mark as sent
        await prisma.notification.update({
          where: { id: notificationId },
          data: {
            status: 'SENT',
            sentAt: new Date(),
          },
        });

        logger.info({
          notificationId,
          messageId: result.messageId,
          attempts: result.attempts,
        }, 'Notification sent successfully');

        const successResult: SendNotificationResult = {
          success: true,
          notificationId,
          attempts: result.attempts,
          ...(result.messageId !== undefined && { messageId: result.messageId }),
        };

        return successResult;
      }

      // Mark as failed - only include errorMessage if it exists
      const updateData: { status: 'FAILED'; errorMessage?: string } = {
        status: 'FAILED',
      };
      if (result.error !== undefined && result.error !== null) {
        updateData.errorMessage = result.error;
      }

      await prisma.notification.update({
        where: { id: notificationId },
        data: updateData,
      });

      logger.error({
        notificationId,
        error: result.error,
        attempts: result.attempts,
      }, 'Notification failed to send');

      const failureResult: SendNotificationResult = {
        success: false,
        notificationId,
        attempts: result.attempts,
        ...(result.error !== undefined && { error: result.error }),
      };

      return failureResult;
    } catch (error: any) {
      logger.error({ error, notificationId }, 'Failed to send notification');

      const errorMessage = error?.message ?? 'Unknown error';
      const catchResult: SendNotificationResult = {
        success: false,
        notificationId,
        attempts: 0,
        error: errorMessage,
      };

      return catchResult;
    }
  }

  /**
   * Get notification statistics
   */
  public async getStatistics(): Promise<NotificationStatistics> {
    const now = new Date();
    const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());

    const [total, pending, sent, failed, todaySent, todayFailed] = await Promise.all([
      prisma.notification.count(),
      prisma.notification.count({ where: { status: 'PENDING' } }),
      prisma.notification.count({ where: { status: 'SENT' } }),
      prisma.notification.count({ where: { status: 'FAILED' } }),
      prisma.notification.count({
        where: {
          status: 'SENT',
          sentAt: { gte: startOfToday },
        },
      }),
      prisma.notification.count({
        where: {
          status: 'FAILED',
          createdAt: { gte: startOfToday },
        },
      }),
    ]);

    return {
      total,
      pending,
      sent,
      failed,
      todaySent,
      todayFailed,
    };
  }

  /**
   * Retry failed notifications
   */
  public async retryFailedNotifications(limit: number = 10): Promise<void> {
    try {
      // Worker disabled temporarily
      // const { retryFailedNotifications: retryInWorker } = await import('../../workers/notification-sender.worker');
      // const retriedCount = await retryInWorker(limit);
      // logger.info({ retriedCount }, 'Notifications queued for retry');
      logger.info('Worker disabled, skipping retry');
    } catch (error: any) {
      logger.error({ error }, 'Failed to retry notifications via worker');
      // Fallback to direct sending
      const failedNotifications = await prisma.notification.findMany({
        where: {
          status: 'FAILED',
          attempts: { lt: 3 },
        },
        orderBy: [{ priority: 'desc' }, { createdAt: 'asc' }],
        take: limit,
      });

      logger.info({
        count: failedNotifications.length,
      }, 'Retrying failed notifications directly');

      for (const notification of failedNotifications) {
        await this.sendNotification(notification.id);
      }
    }
  }

  /**
   * Clean old sent notifications
   */
  public async cleanupOldNotifications(daysToKeep: number = 30): Promise<number> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);

    const result = await prisma.notification.deleteMany({
      where: {
        status: 'SENT',
        sentAt: { lt: cutoffDate },
      },
    });

    logger.info({
      deletedCount: result.count,
      daysToKeep,
    }, 'Cleaned up old notifications');

    return result.count;
  }

  /**
   * Health check
   */
  public async healthCheck(): Promise<{ healthy: boolean; telegram: boolean; stats: NotificationStatistics }> {
    const telegramHealth = await telegramClient.healthCheck();
    const stats = await this.getStatistics();

    return {
      healthy: telegramHealth.healthy,
      telegram: telegramHealth.healthy,
      stats,
    };
  }
}

/**
 * Export singleton instance
 */
export const notificationService = NotificationService.getInstance();

/**
 * Export convenience functions
 */
export async function notifyNewUser(data: Parameters<NotificationService['notifyNewUser']>[0]): Promise<CreateNotificationResult> {
  return await notificationService.notifyNewUser(data);
}

export async function notifyModuleCompleted(data: Parameters<NotificationService['notifyModuleCompleted']>[0]): Promise<CreateNotificationResult> {
  return await notificationService.notifyModuleCompleted(data);
}

export async function notifyTop10Entry(data: Parameters<NotificationService['notifyTop10Entry']>[0]): Promise<CreateNotificationResult> {
  return await notificationService.notifyTop10Entry(data);
}

export async function notifySystemError(data: Parameters<NotificationService['notifySystemError']>[0]): Promise<CreateNotificationResult> {
  return await notificationService.notifySystemError(data);
}

export async function sendDailySummary(date?: Date): Promise<CreateNotificationResult> {
  return await notificationService.sendDailySummary(date);
}

export default notificationService;
