<?php

namespace MailPoet\Mailer;

if (!defined('ABSPATH')) exit;


use MailPoet\Settings\SettingsController;

class MailerLog {
  const SETTING_NAME = 'mta_log';
  const STATUS_PAUSED = 'paused';
  const RETRY_ATTEMPTS_LIMIT = 3;
  const RETRY_INTERVAL = 120; // seconds

  public static function getMailerLog($mailerLog = false) {
    if ($mailerLog) return $mailerLog;
    $settings = SettingsController::getInstance();
    $mailerLog = $settings->get(self::SETTING_NAME);
    if (!$mailerLog) {
      $mailerLog = self::createMailerLog();
    }
    return $mailerLog;
  }

  public static function createMailerLog() {
    $mailerLog = [
      'sent' => null,
      'started' => time(),
      'status' => null,
      'retry_attempt' => null,
      'retry_at' => null,
      'error' => null,
    ];
    $settings = SettingsController::getInstance();
    $settings->set(self::SETTING_NAME, $mailerLog);
    return $mailerLog;
  }

  public static function resetMailerLog() {
    return self::createMailerLog();
  }

  public static function updateMailerLog($mailerLog) {
    $settings = SettingsController::getInstance();
    $settings->set(self::SETTING_NAME, $mailerLog);
    return $mailerLog;
  }

  public static function enforceExecutionRequirements($mailerLog = false) {
    $mailerLog = self::getMailerLog($mailerLog);
    if ($mailerLog['retry_attempt'] === self::RETRY_ATTEMPTS_LIMIT) {
      $mailerLog = self::pauseSending($mailerLog);
    }
    if (self::isSendingPaused($mailerLog)) {
      throw new \Exception(__('Sending has been paused.', 'mailpoet'));
    }
    if (!is_null($mailerLog['retry_at'])) {
      if (time() <= $mailerLog['retry_at']) {
        throw new \Exception(__('Sending is waiting to be retried.', 'mailpoet'));
      } else {
        $mailerLog['retry_at'] = null;
        self::updateMailerLog($mailerLog);
      }
    }
    // ensure that sending frequency has not been reached
    if (self::isSendingLimitReached($mailerLog)) {
      throw new \Exception(__('Sending frequency limit has been reached.', 'mailpoet'));
    }
  }

  public static function pauseSending($mailerLog) {
    $mailerLog['status'] = self::STATUS_PAUSED;
    $mailerLog['retry_attempt'] = null;
    $mailerLog['retry_at'] = null;
    return self::updateMailerLog($mailerLog);
  }

  public static function resumeSending() {
    return self::resetMailerLog();
  }

  /**
   * Process error, doesn't increase retry_attempt so it will not block sending
   *
   * @param string $operation
   * @param string $errorMessage
   * @param int $retryInterval
   *
   * @throws \Exception
   */
  public static function processNonBlockingError($operation, $errorMessage, $retryInterval = self::RETRY_INTERVAL) {
    $mailerLog = self::getMailerLog();
    $mailerLog['retry_at'] = time() + $retryInterval;
    $mailerLog = self::setError($mailerLog, $operation, $errorMessage);
    self::updateMailerLog($mailerLog);
    self::enforceExecutionRequirements();
  }

  /**
   * Process error, increase retry_attempt and block sending if it goes above RETRY_INTERVAL
   *
   * @param string $operation
   * @param string $errorMessage
   * @param string $errorCode
   * @param bool $pauseSending
   *
   * @throws \Exception
   */
  public static function processError($operation, $errorMessage, $errorCode = null, $pauseSending = false, $throttledBatchSize = null) {
    $mailerLog = self::getMailerLog();
    if (!isset($throttledBatchSize) || $throttledBatchSize === 1) {
      $mailerLog['retry_attempt']++;
    }
    $mailerLog['retry_at'] = time() + self::RETRY_INTERVAL;
    $mailerLog = self::setError($mailerLog, $operation, $errorMessage, $errorCode);
    self::updateMailerLog($mailerLog);
    if ($pauseSending) {
      self::pauseSending($mailerLog);
    }
    self::enforceExecutionRequirements();
  }

  public static function setError($mailerLog, $operation, $errorMessage, $errorCode = null) {
    $mailerLog['error'] = [
      'operation' => $operation,
      'error_message' => $errorMessage,
    ];
    if ($errorCode) {
      $mailerLog['error']['error_code'] = $errorCode;
    }
    return $mailerLog;
  }

  public static function getError($mailerLog = false) {
    $mailerLog = self::getMailerLog($mailerLog);
    return isset($mailerLog['error']) ? $mailerLog['error'] : null;
  }

  public static function incrementSentCount() {
    $mailerLog = self::getMailerLog();
    // do not increment count if sending limit is reached
    if (self::isSendingLimitReached($mailerLog)) return;
    // clear previous retry count, errors, etc.
    if ($mailerLog['error']) {
      $mailerLog = self::clearSendingErrorLog($mailerLog);
    }
    (int)$mailerLog['sent']++;
    return self::updateMailerLog($mailerLog);
  }

  public static function clearSendingErrorLog($mailerLog) {
    $mailerLog['retry_attempt'] = null;
    $mailerLog['retry_at'] = null;
    $mailerLog['error'] = null;
    return self::updateMailerLog($mailerLog);
  }

  public static function isSendingLimitReached($mailerLog = false) {
    $settings = SettingsController::getInstance();
    $mailerConfig = $settings->get(Mailer::MAILER_CONFIG_SETTING_NAME);
    // do not enforce sending limit for MailPoet's sending method
    if ($mailerConfig['method'] === Mailer::METHOD_MAILPOET) return false;
    $mailerLog = self::getMailerLog($mailerLog);
    $elapsedTime = time() - (int)$mailerLog['started'];

    if (empty($mailer['frequency'])) {
      $defaultSettings = $settings->getAllDefaults();
      $mailer['frequency'] = $defaultSettings['mta']['frequency'];
    }
    $frequencyInterval = (int)$mailerConfig['frequency']['interval'] * Mailer::SENDING_LIMIT_INTERVAL_MULTIPLIER;
    $frequencyLimit = (int)$mailerConfig['frequency']['emails'];

    if ($mailerLog['sent'] >= $frequencyLimit) {
      if ($elapsedTime <= $frequencyInterval) return true;
      // reset mailer log as enough time has passed since the limit was reached
      self::resetMailerLog();
    }
    return false;
  }

  public static function isSendingPaused($mailerLog = false) {
    $mailerLog = self::getMailerLog($mailerLog);
    return $mailerLog['status'] === self::STATUS_PAUSED;
  }
}
