<?php

namespace Drupal\miniorange_2fa\Services;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\miniorange_2fa\Miniorange2FASupport;

/**
 * License Expiry Scheduler Service for handling license expiry notifications.
 * 
 */
class LicenseExpiryScheduler {

  const NOTIFICATION_DAYS = [30, 15, 7, 1, 0];
  const TRANSACTION_ALERT_THRESHOLDS = [50, 30, 5];
  const USER_LIMIT_ALERT_THRESHOLDS = [50, 30, 5];
  const STATE_EXPIRY_NOTIFICATIONS = 'miniorange_2fa.license_expiry_notifications_sent';
  const STATE_TRANSACTION_ALERTS = 'miniorange_2fa.transaction_alerts_sent';
  const STATE_USER_LIMIT_ALERTS = 'miniorange_2fa.user_limit_alerts_sent';

  /**
   * The config factory.
   */
  protected ConfigFactoryInterface $configFactory;

  /**
   * The logger factory.
   */
  protected LoggerChannelFactoryInterface $loggerFactory;

  /**
   * The state service.
   */
  protected StateInterface $state;

  /**
   * The license manager service.
   */
  protected $licenseManager;

  /**
   * Constructor.
   */
  public function __construct(ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory, StateInterface $state = NULL, LicenseManager $license_manager = NULL) {
    $this->configFactory = $config_factory;
    $this->loggerFactory = $logger_factory;
    $this->state = $state ?: \Drupal::state();
    $this->licenseManager = $license_manager ?: \Drupal::service('miniorange_2fa.license_manager');
  }

  /**
   * Check if license expiry notification should be sent.
   */
  public function shouldSendExpiryNotification(int $days_remaining): bool {
    return in_array($days_remaining, self::NOTIFICATION_DAYS);
  } 

  /**
   * Generate email content for license expiry notification.
   */
  public function getExpiryEmailContent(int $days_remaining): array {
    $config = $this->configFactory->get('miniorange_2fa.settings');
    $license_type = $config->get('mo_auth_2fa_license_type') ?? 'DRUPAL_2FA';
    $license_plan = $config->get('mo_auth_2fa_license_plan') ?? 'DRUPAL_2FA';
    $module_name = 'miniOrange 2FA';
    
    $customer_email = $config->get('mo_auth_customer_admin_email') ?? 'N/A';
    $customer_id = $config->get('mo_auth_customer_id') ?? 'N/A';
    $license_expiry = $config->get('mo_auth_2fa_license_expiry') ?? 'N/A';
    $support_expiry = $config->get('mo_auth_2fa_support_expiry') ?? 'N/A';
    $no_of_users = $config->get('mo_auth_2fa_license_no_of_users') ?? 'N/A';

    if ($days_remaining == 0) {
      return [
        'subject' => "Your miniOrange 2FA License Has Expired - {$module_name}",
        'content' => $this->buildEmailContent(
          "Your {$license_type} license for {$module_name} ({$license_plan} plan) has expired.",
          $module_name,
          $license_type,
          $license_plan,
          "Please renew your license immediately to continue using 2FA functionality.",
          $customer_email,
          $customer_id,
          $license_expiry,
          $support_expiry,
          $no_of_users
        ),
      ];
    }

    return [
      'subject' => "License Expiry Warning: {$days_remaining} days remaining - {$module_name}",
      'content' => $this->buildEmailContent(
        "Your miniOrange 2FA license will expire in {$days_remaining} days.",
        $module_name,
        $license_type,
        $license_plan,
        "Please renew your license before it expires to avoid service interruption.",
        $customer_email,
        $customer_id,
        $license_expiry,
        $support_expiry,
        $no_of_users
      ),
    ];
  }

  /**
   * Build standardized email content.
   */
  private function buildEmailContent(string $message, string $module_name, string $license_type, string $license_plan, string $action_message, string $customer_email = 'N/A', string $customer_id = 'N/A', string $license_expiry = 'N/A', string $support_expiry = 'N/A', string $no_of_users = 'N/A'): string {
    return "{$message}\n\n" .
           "Registered Email: {$customer_email}\n" .
           "Customer ID: {$customer_id}\n\n" .
           "Module: {$module_name}\n" .
           "License Type: {$license_type}\n" .
           "License Plan: {$license_plan}\n" .
           "Number of Users: {$no_of_users}\n" .
           "License Expiry: {$license_expiry}\n" .
           "Support Expiry: {$support_expiry}\n\n" .
           "{$action_message}\n\n" .
           "Contact: drupalsupport@xecurify.com";
  }

  /**
   * Send license expiry notification.
   */
  public function sendExpiryNotification(int $days_remaining): bool {
    $notification_key = "expiry_{$days_remaining}";
    
    if ($this->hasNotificationBeenSent($notification_key, self::STATE_EXPIRY_NOTIFICATIONS)) {
      $this->loggerFactory->get('miniorange_2fa')->info('Notification already sent for @days days remaining.', ['@days' => $days_remaining]);
      return TRUE;
    }

    $admin_email = $this->getAdminEmail();
    if (!$admin_email) {
      $this->loggerFactory->get('miniorange_2fa')->warning('No admin email configured for license expiry notification.');
      return FALSE;
    }

    $email_content = $this->getExpiryEmailContent($days_remaining);
    
    if ($this->sendEmail($admin_email, $email_content['subject'], $email_content['content'], 'license_expiry')) {
      $this->markNotificationAsSent($notification_key, self::STATE_EXPIRY_NOTIFICATIONS);
      $this->loggerFactory->get('miniorange_2fa')->info('License expiry notification sent for @days days remaining.', ['@days' => $days_remaining]);
      return TRUE;
    }

    $this->loggerFactory->get('miniorange_2fa')->error('Failed to send license expiry notification for @days days remaining.', ['@days' => $days_remaining]);
    return FALSE;
  }

  /**
   * Process license expiry notifications based on remaining days.
   */
  public function processExpiryNotifications($days_remaining): bool {
    $success = TRUE;
    
    if ($this->shouldSendExpiryNotification($days_remaining)) {
      $success = $this->sendExpiryNotification($days_remaining) && $success;
    }
    
    $success = $this->processTransactionNotifications() && $success;
    $success = $this->processUserLimitNotifications() && $success;
    
    return $success;
  }

  /**
   * Process transaction level notifications.
   */
  public function processTransactionNotifications(): bool {
    $config = $this->configFactory->get('miniorange_2fa.settings');
    $license_type = $config->get('mo_auth_2fa_license_type') ?? 'DRUPAL_2FA';
    
    if ($license_type === 'DRUPAL_2FA') {
      return TRUE;
    }

    $transactions = [
      'IVR' => $config->get('mo_auth_2fa_ivr_remaining') ?? '-',
      'SMS' => $config->get('mo_auth_2fa_sms_remaining') ?? '-',
      'Email' => $config->get('mo_auth_2fa_email_remaining') ?? '-',
    ];

    foreach ($transactions as $type => $remaining) {
      if ($remaining !== '-' && is_numeric($remaining)) {
        $this->checkAndSendTransactionAlert($type, (int) $remaining);
      }
    }

    return TRUE;
  }

  /**
   * Check transaction levels and send alerts if needed.
   */
  protected function checkAndSendTransactionAlert(string $type, int $remaining): void {
    $admin_email = $this->getAdminEmail();
    if (!$admin_email) {
      return;
    }
    
    $sent_alerts = $this->state->get(self::STATE_TRANSACTION_ALERTS, []);
    
    foreach (self::TRANSACTION_ALERT_THRESHOLDS as $threshold) {
      $notification_key = "transaction_{$type}_{$threshold}";
      
      if ($remaining == $threshold && !in_array($notification_key, $sent_alerts)) {
        $this->sendTransactionAlert($type, $remaining, $threshold, $admin_email);
        $sent_alerts[] = $notification_key;
        $this->state->set(self::STATE_TRANSACTION_ALERTS, $sent_alerts);
        break;
      } 
      elseif ($remaining < $threshold) {
        $sent_alerts = array_diff($sent_alerts, [$notification_key]);
        $this->state->set(self::STATE_TRANSACTION_ALERTS, $sent_alerts);
      }
    }
  }

  /**
   * Send transaction alert email.
   */
   protected function sendTransactionAlert(string $type, int $remaining, int $threshold, string $admin_email): void {
    $config = $this->configFactory->get('miniorange_2fa.settings');
    $license_type = $config->get('mo_auth_2fa_license_type') ?? 'DRUPAL_2FA';
    $license_plan = $config->get('mo_auth_2fa_license_plan') ?? 'DRUPAL_2FA';
    $customer_email = $config->get('mo_auth_customer_admin_email') ?? 'N/A';
    $customer_id = $config->get('mo_auth_customer_id') ?? 'N/A';
    $license_expiry = $config->get('mo_auth_2fa_license_expiry') ?? 'N/A';
    $support_expiry = $config->get('mo_auth_2fa_support_expiry') ?? 'N/A';
    $no_of_users = $config->get('mo_auth_2fa_license_no_of_users') ?? 'N/A';
    
    $subject = "miniOrange 2FA - Transaction Alert: Low {$type} Balance.";
    $content = "Your miniOrange 2FA {$type} transactions are running low.\n\n" .
               "Registered Email: {$customer_email}\n" .
               "Customer ID: {$customer_id}\n\n" .
               "Transaction Type: {$type}\n" .
               "Remaining Transactions: {$remaining}\n\n" .
               "License Type: {$license_type}\n" .
               "License Plan: {$license_plan}\n" .
               "Number of Users: {$no_of_users}\n" .
               "License Expiry: {$license_expiry}\n" .
               "Support Expiry: {$support_expiry}\n\n" .
               "Please purchase additional transactions to continue using {$type} authentication.\n\n" .
               "Contact: drupalsupport@xecurify.com";
               
    
    if ($this->sendEmail($admin_email, $subject, $content, 'license_expiry')) {
      $this->loggerFactory->get('miniorange_2fa')->info('Transaction alert sent for @type: @remaining remaining.', [
        '@type' => $type,
        '@remaining' => $remaining
      ]);
    }
  }

  /**
   * Process user limit notifications.
   */
  public function processUserLimitNotifications(): bool {
    $config = $this->configFactory->get('miniorange_2fa.settings');
    $total_users_allowed = $config->get('mo_auth_2fa_license_no_of_users');
    
    if (!$total_users_allowed || !is_numeric($total_users_allowed)) {
      return TRUE;
    }

    $current_user_count = $this->getCurrentUserCount();
    $remaining_user_slots = $total_users_allowed - $current_user_count;

    foreach (self::USER_LIMIT_ALERT_THRESHOLDS as $threshold) {
      if ($remaining_user_slots == $threshold) {
        $this->checkAndSendUserLimitAlert($remaining_user_slots, $current_user_count, $total_users_allowed);
        break;
      }
    }

    return TRUE;
  }

  /**
   * Get current count of users from UserAuthenticationType table.
   */
  protected function getCurrentUserCount(): int {
    try {
      $database = \Drupal::database();
      $query = $database->select('UserAuthenticationType', 'u')
        ->fields('u', ['uid']);
      
      return (int) $query->countQuery()->execute()->fetchField();
    }
    catch (\Exception $e) {
      $this->loggerFactory->get('miniorange_2fa')->error('Error counting users: @message', [
        '@message' => $e->getMessage()
      ]);
      return 0;
    }
  }

  /**
   * Check user limit and send alert if needed.
   */
  protected function checkAndSendUserLimitAlert(int $remaining_slots, int $current_count, int $total_allowed): void {
    $admin_email = $this->getAdminEmail();
    if (!$admin_email) {
      return;
    }

    $notification_key = "user_limit_{$remaining_slots}";
    $sent_alerts = $this->state->get(self::STATE_USER_LIMIT_ALERTS, []);
    
    if (!in_array($notification_key, $sent_alerts)) {
      $this->sendUserLimitAlert($remaining_slots, $current_count, $total_allowed, $admin_email);
      $sent_alerts[] = $notification_key;
      $this->state->set(self::STATE_USER_LIMIT_ALERTS, $sent_alerts);
    }
    
    foreach (self::USER_LIMIT_ALERT_THRESHOLDS as $threshold) {
      if ($remaining_slots < $threshold) {
        $key = "user_limit_{$threshold}";
        $sent_alerts = array_diff($sent_alerts, [$key]);
      }
    }
    $this->state->set(self::STATE_USER_LIMIT_ALERTS, $sent_alerts);
  }

  /**
   * Send user limit alert email.
   */
  protected function sendUserLimitAlert(int $remaining_slots, int $current_count, int $total_allowed, string $admin_email): void {
    $config = $this->configFactory->get('miniorange_2fa.settings');
    $customer_email = $config->get('mo_auth_customer_admin_email') ?? 'N/A';
    $customer_id = $config->get('mo_auth_customer_id') ?? 'N/A';
    $subject = "User Limit Alert: Only {$remaining_slots} user slots remaining - miniOrange 2FA";
    $content = "Your miniOrange 2FA license is approaching its user limit.\n\n" .
               "Current Users: {$current_count}\n" .
               "Total Allowed: {$total_allowed}\n" .
               "Remaining Slots: {$remaining_slots}\n\n" .
               "Registered Email: {$customer_email}\n" .
               "Customer ID: {$customer_id}\n\n" .
               "Please upgrade your license to accommodate more users.\n" .
               "Contact: drupalsupport@xecurify.com";
    
    if ($this->sendEmail($admin_email, $subject, $content, 'license_expiry')) {
      $this->loggerFactory->get('miniorange_2fa')->info('User limit alert sent: @remaining slots remaining (@current/@total).', [
        '@remaining' => $remaining_slots,
        '@current' => $current_count,
        '@total' => $total_allowed
      ]);
    }
  }

  /**
   * Generic method to send email notifications.
   */
  private function sendEmail(string $admin_email, string $subject, string $content, string $query_type): bool {
    try {
      $support = new Miniorange2FASupport($admin_email, '', $query_type);
      return (bool) $support->sendSupportQuery($subject, $content);
    }
    catch (\Exception $e) {
      $this->loggerFactory->get('miniorange_2fa')->error('Exception while sending email: @message', [
        '@message' => $e->getMessage()
      ]);
      return FALSE;
    }
  }

  /**
   * Get admin email from configuration.
   */
  private function getAdminEmail(): ?string {
    return $this->configFactory->get('miniorange_2fa.settings')->get('mo_auth_customer_admin_email');
  }

  /**
   * Generic method to check if notification has been sent today.
   */
  protected function hasNotificationBeenSent(string $notification_key, string $state_key): bool {
    $sent_notifications = $this->state->get($state_key, []);
    $today_key = $notification_key . '_' . date('Y-m-d');
    return in_array($today_key, $sent_notifications);
  }

  /**
   * Generic method to mark notification as sent.
   */
  protected function markNotificationAsSent(string $notification_key, string $state_key): void {
    $sent_notifications = $this->state->get($state_key, []);
    $today_key = $notification_key . '_' . date('Y-m-d');
    $sent_notifications[] = $today_key;
    
    $days_to_keep = $state_key === self::STATE_EXPIRY_NOTIFICATIONS ? 60 : 30;
    $cutoff_date = date('Y-m-d', strtotime("-{$days_to_keep} days"));
    
    $sent_notifications = array_filter($sent_notifications, function($notification) use ($cutoff_date) {
      $date = substr($notification, strrpos($notification, '_') + 1);
      return $date >= $cutoff_date;
    });
    
    $this->state->set($state_key, array_values($sent_notifications));
  }


}
