<?php
namespace SPAATG\Model;

if ( ! defined( 'ABSPATH' ) ) {
 exit; // Exit if accessed directly.
}

use SPAATG\ShortPixelLogger\ShortPixelLogger as Log;
use SPAATG\Notices\NoticeController as Notice;

use SPAATG\Controller\AdminNoticesController as AdminNoticesController;
use SPAATG\Controller\QuotaController as QuotaController;

class ApiKeyModel extends \SPAATG\Model
{

  // variables
  protected $apiKey;
  protected $apiKeyTried;  // stop retrying the same key over and over if not valid.
  protected $verifiedKey;
  protected $validationErrorMessage = '';

  // states
  // key is verified is set by checkKey *after* checks and validation
  protected $key_is_verified = false; // this state doesn't have to be the same as the verifiedKey field in DB.
  protected $key_is_empty = false;
  protected $key_is_constant = false;
  protected $key_is_hidden = false;

  protected static $notified = array();


  protected $legacy_model = array(
       'apiKey' => array('s' => 'string',
                          'key' => 'wp' . '-spaatg-apiKey',
                        ),
       'apiKeyTried' => array('s' => 'string',
                           'key' => 'wp' . '-spaatg-apiKeyTried'
       ),
       'verifiedKey' => array('s' => 'boolean',
                          'key' => 'wp' . '-spaatg-verifiedKey',
                       ),

  );

	protected $model = array(
       'apiKey' => array('s' => 'string',
       ),
       'apiKeyTried' => array('s' => 'string',
       ),
       'verifiedKey' => array('s' => 'boolean',
       ),
       'validationErrorMessage' => array('s' => 'string',
       ),
  );

	private $option_name =  'spaatg_key';

  /** Constructor. Check for constants, load the key */
  public function __construct()
  {
    $this->key_is_constant = (defined("SPAATG_API_KEY")) ? true : false;
    $this->key_is_hidden = (defined("SPAATG_HIDE_API_KEY")) ? (bool) SPAATG_HIDE_API_KEY : false;

  }

  /** Load the key from storage. This can be a constant, or the database. Check if key is valid.
  *
  */
  public function loadKey()
  {
 		$apikeySettings = get_option($this->option_name, null);

		if (is_null($apikeySettings))
		{
			$this->apiKey = get_option($this->legacy_model['apiKey']['key'], false);
	    $this->verifiedKey = get_option($this->legacy_model['verifiedKey']['key'], false);
	    $this->apiKeyTried = get_option($this->legacy_model['apiKeyTried']['key'], false);

				$apikeySettings = [
						'apiKey' => $this->apiKey,
						'verifiedKey' => $this->verifiedKey,
						'apiKeyTried' => $this->apiKeyTried,
				];
			 delete_option($this->legacy_model['apiKey']['key']);
			 delete_option($this->legacy_model['verifiedKey']['key']);
			 delete_option($this->legacy_model['apiKeyTried']['key']);

			 $this->update();
		}

		$this->apiKey = isset($apikeySettings['apiKey']) ? $apikeySettings['apiKey'] : '';
    $this->verifiedKey = isset($apikeySettings['verifiedKey']) ? $apikeySettings['verifiedKey'] : false; 
		$this->apiKeyTried = $apikeySettings['apiKeyTried'];
    $this->validationErrorMessage = isset($apikeySettings['validationErrorMessage']) ? $apikeySettings['validationErrorMessage'] : '';


    if ($this->key_is_constant)
    {
        $key = SPAATG_API_KEY;
        if (isset($apikeySettings['apiKey']))
        {
            $this->apiKey = '';
            $this->update();
        }
        $this->apiKey = $key;
    }


    $valid = $this->checkKey($this->apiKey);

    return $valid;
  }

  protected function update()
  {
			$apikeySettings = [
					'apiKey' => trim($this->apiKey),
					'verifiedKey' => $this->verifiedKey,
					'apiKeyTried' => $this->apiKeyTried,
          'validationErrorMessage' => $this->validationErrorMessage,
			];


			$res = update_option($this->option_name, $apikeySettings, true);
			return $res;
  }

  /** Resets the last APIkey that was attempted with validation
  *
  *  The last apikey tried is saved to prevent server and notice spamming when using a constant key, or a wrong key in the database without updating.
  */
  public function resetTried()
  {
    if (is_null($this->apiKeyTried))
    {
      return; // if already null, no need for additional activity
    }
    $this->apiKeyTried = null;
    $this->validationErrorMessage = '';
    $this->update();
    Log::addDebug('Reset Tried', $this->apiKeyTried);
  }

  /** Checks the API key to see if we have a validated situation
  *  @param $key String The 20-character ShortPixel API Key or empty string
  *  @return boolean Returns a boolean indicating valid key or not
  *
  * An Api key can be removed from the system by passing an empty string when the key is not hidden.
  * If the key has changed from stored key, the function will pass a validation request to the server
  * Failing to key a 20char string, or passing an empty key will result in notices.
  */
  public function checkKey($key)
  {
			$valid = false;
      if (is_null($key) || strlen($key) == 0)
      {
        $this->validationErrorMessage = '';
        // first-timers, redirect to nokey screen
        $this->checkRedirect(); // this should be a one-time thing.
        if($this->key_is_hidden) // hidden empty keys shouldn't be checked
        {
           $this->key_is_verified = $this->verifiedKey;
           return $this->key_is_verified;
        }
        elseif ($key != $this->apiKey)
        {
          Notice::addWarning(__('Your API Key has been removed', 'shortpixel-ai-alt-text-generator'));
          $this->clearApiKey(); // notice and remove;
          return false;
        }
        $valid = false;

      }
      elseif (strlen($key) <> 20 && $key != $this->apiKeyTried)
      {
        $this->NoticeApiKeyLength($key);
        Log::addDebug('Key Wrong Length: ' . $key);
        $this->key_is_verified = false;
        $valid = false;
      }
      elseif( ($key != $this->apiKey || ! $this->verifiedKey) && $key != $this->apiKeyTried)
      {
        Log::addDebug('Validate Key' . $key);
        $valid = $this->validateKey($key);
      }
      elseif($key == $this->apiKey) // all is fine!
      {
        $valid = $this->verifiedKey;
      }

      // if key is not valid on load, means not valid at all
      if (false === $valid)
      {
        if (false === $this->key_is_constant && ! is_null($key) && strlen($key) > 0)
        {
          $this->apiKey = trim($key);
        }
        $this->verifiedKey = false;
        $this->key_is_verified = false;
        $this->apiKeyTried = $key;
        $this->update();
      }
      else {
        $this->validationErrorMessage = '';
        $this->key_is_verified = true;
      }

      return $this->key_is_verified; // first time this is set! *after* this function
  }

  public function is_verified()
  {
      return $this->key_is_verified;
  }

  public function is_constant()
  {
      return $this->key_is_constant;
  }

  public function is_hidden()
  {
      return $this->key_is_hidden;
  }

  public function getKey()
  {
      return $this->apiKey;
  }

  public function getValidationErrorMessage()
  {
      return $this->validationErrorMessage;
  }

	public function uninstall()
	{
		 $this->clearApiKey();
	}

  protected function clearApiKey()
  {
    $this->key_is_empty = true;
    $this->apiKey = '';
    $this->verifiedKey = false;
    $this->apiKeyTried = '';
    $this->key_is_verified = false;
    $this->validationErrorMessage = '';

    AdminNoticesController::resetAPINotices();
    AdminNoticesController::resetQuotaNotices();
    AdminNoticesController::resetIntegrationNotices();

		// Remove them all
		delete_option($this->legacy_model['apiKey']['key']);
		delete_option($this->legacy_model['verifiedKey']['key']);
		delete_option($this->legacy_model['apiKeyTried']['key']);

    delete_option($this->option_name);

  }

  protected function validateKey($key)
  {
    Log::addDebug('Validating Key ' . $key);
    // first, save Auth to satisfy getquotainformation

    $quotaData = $this->remoteValidate($key);

    $checked_key = (isset($quotaData['APIKeyValid']) && $quotaData['APIKeyValid']) ? true : false;
    $message = isset($quotaData['Message']) ? sanitize_text_field($quotaData['Message']) : __('Unknown error during API key verification.', 'shortpixel-ai-alt-text-generator');
    $normalized_message = strtolower($message);

     if (! $checked_key)
     {
			  Log::addError('Key is not validated', $message);
        $this->verifiedKey = false;
        $this->key_is_verified = false;
        if (strpos($normalized_message, 'wrong api key') !== false)
        {
          $this->validationErrorMessage = __('Wrong API Key', 'shortpixel-ai-alt-text-generator');
        }
        else
        {
          /* translators: %s: API validation error message. */
          $this->validationErrorMessage = sprintf(__('Error during verifying API key: %s','shortpixel-ai-alt-text-generator'), $message);
        }
     }
     elseif ($checked_key) {
        if (false === $this->is_constant())
        {
          $this->apiKey = $key;
        }
        $this->verifiedKey = $checked_key;
        $this->validationErrorMessage = '';
        $this->processNewKey($quotaData);
        $this->update();
     }    
      return $checked_key;
  }

  /** Process some things when key has been added. This is from original wp-short-pixel.php */
  protected function processNewKey($quotaData)
  {

    //display notification
    $urlParts = explode("/", get_site_url());
    if( $quotaData['DomainCheck'] == 'NOT Accessible'){
        $notice = array("status" => "warn", "msg" => __("API Key is valid but your site is not accessible from our servers. Please make sure that your server is accessible from the Internet before using the API; otherwise we won't be able to generate image SEO data for your images.",'shortpixel-ai-alt-text-generator'));
        Notice::addWarning($notice);
    } else {
        if ( function_exists("is_multisite") && is_multisite() && !defined("SPAATG_API_KEY"))
        {
            $notice = __("Great, your API Key is valid! <br>You seem to be running a multisite, please note that API Key can also be configured in wp-config.php like this:",'shortpixel-ai-alt-text-generator')
                . "<BR> <b>define('SPAATG_API_KEY', '". $this->apiKey ."');</b>";
            Notice::addSuccess($notice);
        }
    }

    // Test that uploads are writable and the plugin storage folder can be created.
    if ( \wpSPAATG()->filesystem()->checkStorageFolder() === false)
    {
        /* translators: %s: plugin storage folder path. */
	        $notice = sprintf(__("There is something preventing us from creating the plugin storage folder.<BR>Please make sure that folder <b>%s</b> has the necessary write and read rights.",'shortpixel-ai-alt-text-generator'), SPAATG_STORAGE_FOLDER);
       Notice::addError($notice);
    }

    AdminNoticesController::resetAPINotices();
  }

  protected function NoticeApiKeyLength($key)
  {
      // repress double warning.
    if (isset(self::$notified['apilength']) && self::$notified['apilength'])
      return;

    $KeyLength = strlen($key);
    /* translators: %s: API key character count. */
    $notice =  sprintf(__("The API Key you provided has %s characters, but it should contain exactly 20 characters, using only letters and numbers.",'shortpixel-ai-alt-text-generator'), $KeyLength)
               . "<BR> <b>"
               . __('Please check that the API Key is the same as the one you received in your sign-up email.','shortpixel-ai-alt-text-generator')
               . "</b><BR> "
               . __('If this problem persists, please contact us at ','shortpixel-ai-alt-text-generator')
               . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
               . __(' or ','shortpixel-ai-alt-text-generator')
               . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-ai-alt-text-generator') . "</a>.";
    self::$notified['apilength'] = true;
    $this->validationErrorMessage = $notice;
  }

  // Does remote Validation of key. In due time should be replaced with something more lean.
  private function remoteValidate($key)
  {
   $qControl = QuotaController::getInstance();
   $quotaData = $qControl->remoteValidateKey($key);

   return $quotaData;

  }

  protected function checkRedirect()
  {
    if ($this->isPluginActivationRequest())
    {
      return false;
    }

    $redirectedSettings =  \wpSPAATG()->settings()->redirectedSettings;
    if(! \wpSPAATG()->env()->is_ajaxcall && !$redirectedSettings && !$this->verifiedKey && (!function_exists("is_multisite") || ! is_multisite())) {
      
      \wpSPAATG()->settings()->redirectedSettings = 1;

      // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin page query arg prevents redirect loops.
      if (isset($_GET['page']) && 'spaatg-settings' === sanitize_key(wp_unslash($_GET['page'])))
      {
         Log::addError('Panic! RedirectSettings failed setting!');
         return false; 
      }

  //    $this->update();
      if (wp_safe_redirect(admin_url('options-general.php?page=spaatg-settings'))) {
        exit;
      }

      Log::addError('RedirectSettings failed: wp_safe_redirect returned false.');
      return false;
    }

}

  private function isPluginActivationRequest()
  {
    if ( ! is_admin() )
    {
      return false;
    }

    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only activation context guard; WordPress verifies the activation nonce.
    $action = isset($_GET['action']) ? sanitize_key(wp_unslash($_GET['action'])) : '';

    return in_array($action, array('activate', 'activate-selected'), true);
  }


}
