<?php
namespace SPAATG\Model\Queue;

if (!defined('ABSPATH')) {
   exit; // Exit if accessed directly.
}
// Attempt to standardize what goes around in the queue and keep some overview.

use SPAATG\ShortPixelLogger\ShortPixelLogger as Log;
use SPAATG\Model\Image\ImageModel as ImageModel;

use SPAATG\Controller\Api\RequestManager as RequestManager;

use SPAATG\Controller\Optimizer\OptimizeAiController as OptimizeAiController;
use SPAATG\Model\AiDataModel;
use stdClass;

class QueueItem
{

   protected $imageModel; // ImageModel 
   protected $item_id; // Item Id 
   protected $queueItem; // Object coming from WPQ

   protected $result; // Result object stores a viable customer response.

   protected $data; // something savable to dbase, for now object. This is the only thing persistent!

   protected $item_count; // counted images for the table.

   protected $debug_active = false; // prevent operations when it's debug view in edit media

   public function __construct($args = [])
   {

      if (isset($args['imageModel']) && !is_null($args['imageModel']) && is_object($args['imageModel'])) {
         $this->setModel($args['imageModel']);
      } elseif (isset($args['item_id']) && is_numeric($args['item_id'])) {
         $this->item_id = intval($args['item_id']);
      }

      // Init defaults
      $this->data = new QueueItemData(); // init
   }

   /** Sets required ImageModel to the QueueItem
    * 
    * @param ImageModel $imageModel 
    * @return void 
    */
   public function setModel(ImageModel $imageModel)
   {
      $this->imageModel = $imageModel;
      $this->item_id = $imageModel->get('id');
   }

   /** Sets the data in QueueItem from data coming from the Shortpixel Queue 
    * 
    * @param mixed $data 
    * @return void 
    */
   public function setFromQueueData($data)
   {
      foreach($data as $name => $value)
      {
          $this->setData($name, $value);
      }
   }

   /** Sets information to the (persistent) data object, which is saved in ShortPixel Queue
    * 
    * @param string $name 
    * @param mixed $value 
    * @return void 
    */
   public function setData($name, $value)
   {
      $this->data->$name = $value;
   }

   /** Without parameters returns the current block status of this item, otherwise applied required block ( true / false )
    * 
    * @param boolean $block 
    * @return boolean|void     
    */
   public function block($block = null)
   {
      if (is_null($block)) {
            return $this->data->block;
      } else {
         $this->setData('block', (bool) $block);
      }
   }

   /** Returns QueueItemData object for functions requiring this information 
    * 
    * @return QueueItemData 
    */
   public function data()
   {
      return $this->data;
   }

   /** Returns result object which can be interpreted by UI . Creates it if null 
    * 
    * @return stdClass 
    */
   public function result()
   {

      if (is_null($this->result))
      {
         $this->result = new \stdClass;
      }

      // check / set defaults.
      if (false === property_exists($this->result, 'item_id')) {
         $this->result->item_id = $this->item_id;
      }

      if (false === property_exists($this->result, 'is_error')) {
         $this->result->is_error = false;
      }

      if (false === property_exists($this->result, 'is_done')) {
         $this->result->is_done = false;
      }

      $result = $this->result;

      $result->_debug_is_qItemResult = true;

      return $result;
   }

   /** Sets value of a property. 
    * 
    * @param string $name 
    * @param mixed $value 
    * @return void 
    */
   public function set($name, $value)
   {
      if (property_exists($this, $name)) {
         $this->$name = $value;
      } else {
         // @todo Throw here some php error
      }
   }

   /** Return Data that need's storing in Queue Table
    * 
    *
    * @return Object|boolean 
    */
   public function getQueueItem()
   {
      if (is_null($this->queueItem))
      {
          return false; 
      }

      $qItem = $this->queueItem;
      $qItem->value = $this->data->toObject();
      return $qItem;
   }

   /** Gets property value by name, null if does not exists. 
    * 
    * @param mixed $name 
    * @return mixed 
    */
   public function __get($name)
   {
      if (property_exists($this, $name)) {
         return $this->$name;
      }


      return null;
   }

   /** Return Array representation of this object, which is used to enqueue the Item.
    * 
    * @return array{id: int, value: object, item_count: mixed, order: mixed} 
    */
   public function returnEnqueue()
   {
      $value = $this->data->toObject();

      $item_id = $this->item_id; 

      // ImageModel could not be set i.e. migrate or other special actions.
      if (is_object($this->imageModel) && $this->imageModel->getParent() !== false) {
         $media_id = $this->imageModel->getParent();
      }

      $enqueue = ['id' => $item_id, 'value' => $value, 'item_count' => $this->item_count];
      
      if (! is_null($this->data->queue_list_order))
      {
         $enqueue['order'] = $this->data->queue_list_order;
      }

      return $enqueue; 
      
   }

   /** Set debug flag, used in edit-media debug info.
    * 
    * @return void 
    */
   public function setDebug()
   {
      $this->debug_active = true;
   }

   public function getAltDataAction()
   {
       $this->newAction(); 
       $this->data->action = 'getAltData'; 
       
       $this->item_count = 0; 
   }

   public function addResult($data = [])
   {
      // Should list every possible item, arrayfilter out.
      $validation = [
         'apiStatus', 
         'message',
         'is_error',
         'is_done',
         'file',  // should probably be merged these two.
         'files',
         'fileStatus',
         'filename', // @todo figure out why this is here.
         'error',  // might in time better be called error_code or so
         'new_attach_id', // new attach id for background remove.
         'success', // new
         'improvements',
         'original',
         'optimized',
         'redirect', // Redirection for background remove etc 
         'queueType',
         'kblink',
         'data', // Is returnDataList returned by apiController. (array)
    //     'retrievedText', // Ai text returning from AIController  //  @todo Can probably be removed on release. 
         'apiName', // NAme of the handling api, for JS / Response to show different results.
         'remote_id', 
         'aiData',   // Returning AI Data

      ];

      if (is_null($this->result)) {
         $this->result = new \stdClass;
      }

      foreach ($data as $name => $value) {
         if (false === in_array($name, $validation)) {
            Log::addWarn("Result $name not in validation");
         }

         $this->result->$name = $value;
      }
   }


   /** Clean several aspects of this object ( result, other things ) before triggering a new action. 
    * 
    * Since QItem is mostly passed by reference 
    * @return void 
    */
   protected function newAction()
   {
       $this->result = new \stdClass; // new action, new results 

       if ($this->data()->hasNextAction()) // Keep this at all times / not optimal still
       {
          $nextActions = $this->data()->next_actions; 
       } 

       $keepDataArgs = $this->data()->getKeepDataArgs();
       $next_keepdata = $this->data()->next_keepdata; 


       $this->data = new QueueItemData(); // new action, new data(?)

       if (isset($nextActions))
       {
         $this->data()->next_actions = $nextActions;
       }

      // Always pass
      if (count($keepDataArgs) > 0)
      {
         $this->data()->next_keepdata = $next_keepdata;
         foreach($keepDataArgs as $name => $value)
         {
               $this->data()->$name = $value;
         }

      }

   }

   public function requestAltAction($args = [])
   {   
      $this->newAction(); 
      $this->data->urls = [$this->imageModel->getUrl()];
      $this->data->tries = 0;
      $this->item_count = 1;

      $item_id = $this->imageModel->get('id');

      $paramlist = []; 

      $preview_only = false; 
      if (isset($args['preview_only']) && true == $args['preview_only'])
      {
         $paramlist['preview_only'] = true;
         $preview_only = true; 
      } 

      $aiDataModel = new AiDataModel($item_id);
      
      $data = $aiDataModel->getOptimizeData($args);

      if (isset($data['paramlist']))
      {
         $this->data()->paramlist = $data['paramlist'];
      }
      if (isset($data['returndatalist']))
      {
         $this->data()->returndatalist = $data['returndatalist'];
         $this->data()->addKeepDataArgs('returndatalist');
      }

      $this->data->addCount(['aiCount' => 1]); // @todo Check if this is really a one credito operation.

      $this->data->action = 'requestAlt'; // For Queue

      if ($this->data()->hasNextAction())
      {
          $next_actions = array_merge(['retrieveAlt'], $this->data()->next_actions);
      }
      else
      {
         $next_actions = ['retrieveAlt'];
      }
      
      if (false === $preview_only)
      {
         $this->data->next_actions = $next_actions;
      }
   }

   public function retrieveAltAction($args)
   {
      $this->newAction();

      $remote_id = $args['remote_id'];
      
      if (isset($args['returndatalist']))
      {
         $this->data()->returndatalist = $args['returndatalist'];
      }

      $this->data->remote_id = $remote_id;
      $this->data->tries = 0;
      $this->item_count = 1;
      $this->data->action = 'retrieveAlt';

   }

   /**
    * Get the ApiController associated to the action performed
    * 
    * In future probably should not take data()->action since newActions wipes all of this ( double ? )
    * @return OptimizeBase  optimizer or higher.
    */
   public function getAPIController($action = null) // @todo Move to QueueItem, or QUeueItems ?
   {
      $api = null;
      if (is_null($action))
      {
         $action = $this->data()->action;         
      }

      switch ($action) {
         case 'requestAlt': // @todo Check if this is correct action name,
         case 'retrieveAlt':
         case 'getAltData': 
         case 'undoAI': 
         case 'redoAI': 
            $api = OptimizeAiController::getInstance();
            break;
      }

      return $api;
   }
   
   /**
    * Add a timestamp to the URL for cache-prevention.
    *
    * @param array $urls  URL's to timestamp 
    * @param int $id  Item_id to get post time for this.
    * @return array
    */
   protected function timestampURLS($urls, $id)
   {
      // https://developer.wordpress.org/reference/functions/get_post_modified_time/
      $time = get_post_modified_time('U', false, $id);

      foreach ($urls as $index => $url) {
         $urls[$index] = add_query_arg('ver', $time, $url); //has url
      }

      return $urls;
   }

   
   public function checkImageModelExists()
   {
      if (is_null($this->imageModel) || false === is_object($this->imageModel)) {
         return false;
      }
      return true;
   }

} // class
