<?php

namespace ShortPixel\Model\Converter;

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

use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;

use ShortPixel\Helper\UtilHelper as UtilHelper;
use ShortPixel\Model\Image\ImageModel as ImageModel;
use ShortPixel\Model\Queue\QueueItem as QueueItem;

/**
 * Converter that delegates format conversion to the ShortPixel API.
 *
 * Handles HEIC, TIFF, TIF, and BMP images by sending them to the remote API,
 * placing a local placeholder while the API processes the file, and then
 * writing the returned JPG back to disk on completion.
 *
 * @package ShortPixel\Model\Converter
 */
class ApiConverter extends MediaLibraryConverter
{

	/** @var array File extensions that this converter can handle. */
	const CONVERTABLE_EXTENSIONS = array('heic', 'tiff', 'tif', 'bmp');

	/** @var bool Whether to request thumbnails from the API alongside the main image. */
	protected $requestAPIthumbnails = true;

	/**
	 * Determines whether the current image can be converted by this converter.
	 *
	 * Returns true for supported extensions, or when a placeholder has been set
	 * (indicating an in-progress API conversion).
	 *
	 * @return bool True if convertable, false otherwise.
	 */
	public function isConvertable()
	{
		$fs = \wpSPIO()->filesystem();
		$extension = $this->imageModel->getExtension();

		// If extension is in list of allowed Api Converts.
		if (in_array($extension, static::CONVERTABLE_EXTENSIONS) && $extension !== 'png') {
			return true;
		}

		// If file has not been converted in terms of file, but has a placeholder - process ongoing, so continue;
		if (false === $this->imageModel->getMeta()->convertMeta()->isConverted() && true === $this->imageModel->getMeta()->convertMeta()->hasPlaceHolder()) {
			return true;
		}

		// File has been converted, not converting again.
		if (true === $this->imageModel->getMeta()->convertMeta()->isConverted()) {
			return false;
		}
	}

	/**
	 * Modifies the queue item before it is submitted to the API.
	 *
	 * Strips any local 'convertto' parameters, inserts a 'convert_api' action,
	 * forces lossless compression to avoid double-compression on thumbnails,
	 * resets image credit counts, and calls prepareQueue() to create the placeholder.
	 *
	 * @param QueueItem $qItem The queue item to modify.
	 * @param array     $args  Additional arguments; 'debug_active' suppresses prepareQueue.
	 * @return void
	 */
	public function filterQueue(QueueItem $qItem, $args = [])
	{
		foreach ($qItem->data()->paramlist as $index => $data) {
			if (isset($qItem->data()->paramlist[$index]['convertto'])) {
				$paramlist = $qItem->data()->paramlist;
				unset($paramlist[$index]['convertto']);
				$qItem->data()->paramlist = $paramlist;
//				$item->data()->paramlist[$index]['convertto'] = 'jpg';
			}
		}

		$prev_action = $qItem->data()->action;
		$qItem->data()->action = 'convert_api';
		$qItem->data()->addNextAction(($prev_action));
		/*
		if (false === is_null($qItem->data()->next_actions))
		{
			$qItem->data()->next_actions = array_merge($qItem->data()->next_actions, [$prev_action]);
		}
		else
		{
			$qItem->data()->next_actions = [$prev_action];
		} */

		// Run converter to create backup and make placeholder to block similar heics from overwriting.
		$converter_args = array('runReplacer' => false);
		if (false === $args['debug_active']) {
			$this->prepareQueue($converter_args);
		}

		//Lossless because thumbnails will otherwise be derived of compressed image, leaving to double compression.
		if (property_exists($qItem->data(), 'compressionType')) {
			$qItem->setData('compressionTypeRequested', $qItem->compressionType);
		}
		// Process Heic as Lossless so we don't have double opts.
		$qItem->data()->compressionType = ImageModel::COMPRESSION_LOSSLESS;

		// Reset counts
		$counts = new \stdClass;
		$counts->baseCount = 1; // count the base images.
		$counts->avifCount = 0;
		$counts->webpCount = 0;
		$counts->creditCount = 1;

		$qItem->setData('counts', $counts);
	}

	/**
	 * Prepare to use the converter on the imageModel. Called in queue.php
	 * @param  array  $args      Parameters for the function (runreplacer now - will replace urls)
	 * @return bool       success or not.
	 */
	protected function prepareQueue($args = array())
	{
		// Turning off replacer, since it's always called off in Api?
		$defaults = array(
			'runReplacer' => true, // The replacer doesn't need running when the file is just uploaded and doing in handle upload hook.
		);

		$args = wp_parse_args($args, $defaults);

		$this->setupReplacer();

		$fs = \wpSPIO()->filesystem();

		$extension = $this->imageModel->getExtension();

		if ('heic' === $extension) {
			$placeholderFile = $fs->getFile(\wpSPIO()->plugin_path('res/img/fileformat-heic-placeholder.jpg'));
		} elseif ('tiff' === $extension || 'tif' === $extension) {
			$placeholderFile = $fs->getFile(\wpSPIO()->plugin_path('res/img/fileformat-tiff-placeholder.jpg'));
		} elseif ('bmp' === $extension) {
			$placeholderFile = $fs->getFile(\wpSPIO()->plugin_path('res/img/fileformat-bmp-placeholder.jpg'));
		} else { // wrong file better than no file.
			$placeholderFile = $fs->getFile(\wpSPIO()->plugin_path('res/img/fileformat-heic-placeholder.jpg'));
		}

		// Convert runs when putting imageModel to queue format in the Queue classs. This could run without optimization (before) taking place and when accidentally running it more than once results in duplicate files / backups (img-1, img-2 etc). Check placeholder and baseName to prevent this. Assume already done when it has it .
		if ($this->imageModel->getMeta()->convertMeta()->hasPlaceHolder() && $this->imageModel->getMeta()->convertMeta()->getReplacementImageBase() !== false) {
			return true;
		}

		$replacementPath = $this->getReplacementPath();

		if (false === $replacementPath) {
			Log::addWarn('ApiConverter replacement path failed');
			$this->imageModel->getMeta()->convertMeta()->setError(self::ERROR_PATHFAIL);

			return false; // @todo Add ResponseController something here.
		}

		$replaceFile = $fs->getFile($replacementPath);
		// If filebase (filename without extension) is not the same, this indicates that a double is there and it's enumerated. Move backup accordingly.


		$destinationFile = $fs->getFile($replacementPath);

		// Create placeholder here.
		$copyok = $placeholderFile->copy($destinationFile);

		if ($copyok) {
			$this->imageModel->getMeta()->convertMeta()->setFileFormat($extension);
			$this->imageModel->getMeta()->convertMeta()->setPlaceHolder(true);
			$this->imageModel->getMeta()->convertMeta()->setReplacementImageBase($destinationFile->getFileBase());
			$this->imageModel->saveMeta();

			// @todo Wip . Moved from handleConverted.
			// Backup basically. Do this first.
			$conversion_args = array(
				'replacementPath' => $replacementPath,
				'backup_thumbnails' => false, // no need for this. either they should be optimized, or generated after the run
			);
			$prepared = $this->imageModel->conversionPrepare($conversion_args);
			if (false === $prepared) {
				return false;
			}

			// Don't offload until the API file has been returned properly.
			do_action('shortpixel/converter/prevent-offload', $this->imageModel->get('id'));

			// Turning off replacer, since it's always called off in Api?
			//	$this->setTarget($destinationFile);

			//	$params = array('success' => true, 'generate_metadata' => false);
			//	$this->updateMetaData($params);

			$fs->flushImage($this->imageModel);

			// Turning off all replacer, since it's always called off in Api?
			/*
           if (true === $args['runReplacer'])

					{
						$result = $this->replacer->replace();
					} */
		} else {
			Log::addError('Failed to copy placeholder');
			return false;
		}

		return true;
	}

	/** Currently not in use */
	public function convert($args = array())
	{
		return;
	}

	// Restore from original file. Search and replace everything else to death.
	public function restore()
	{
		/*$params = array('restore' => true);
			$fs = \wpSPIO()->filesystem();

			$this->setupReplacer();

			$newExtension =  $this->imageModel->getMeta()->convertMeta()->getFileFormat();

			$oldFileName = $this->imageModel->getFileName(); // Old File Name, Still .jpg
			$newFileName =  $this->imageModel->getFileBase() . '.' . $newExtension;

			if ($this->imageModel->isScaled())
			{
				 $oldFileName = $this->imageModel->getOriginalFile()->getFileName();
				 $newFileName = $this->imageModel->getOriginalFile()->getFileBase() . '.' . $newExtension;
			}

			$fsNewFile = $fs->getFile($this->imageModel->getFileDir() . $newFileName);

			$this->newFile = $fsNewFile;
			$this->setTarget($fsNewFile);

			$this->updateMetaData($params);
	//		$result = $this->replacer->replace();

			$fs->flushImageCache(); */
	}

	/**
	 * Returns a fixed checksum indicating whether a conversion has been attempted.
	 *
	 * @return int Always 1.
	 */
	public function getCheckSum()
	{
		return 1; // done or not.
	}

	/**
	 * Handles the optimized file returned by the API after a successful conversion.
	 *
	 * Removes the placeholder, copies the API-returned JPG to the replacement path,
	 * updates WordPress metadata, runs the URL replacer, and marks the conversion as complete.
	 *
	 * @param array $optimizeData Data returned by the optimizer, containing 'files' and 'data' keys.
	 * @return bool True on success, false on any failure.
	 */
	public function handleConverted($optimizeData)
	{
		$this->setupReplacer();
		$fs = \wpSPIO()->filesystem();

		$extension = $this->imageModel->getExtension();
		$replacementBase = $this->imageModel->getMeta()->convertMeta()->getReplacementImageBase();
		if (false === $replacementBase) {
			$replacementPath = $this->getReplacementPath();
			$replacementFile = $fs->getFile($replacementPath);
		} else {
			$replacementPath = $replacementBase . '.jpg';
			$replacementFile = $fs->getFile($this->imageModel->getFileDir() . $replacementPath);
		}

		// If -sigh- file has a placeholder, then do something with that.
		if (true === $this->imageModel->getMeta()->convertMeta()->hasPlaceHolder()) {
			$this->imageModel->getMeta()->convertMeta()->setPlaceHolder(false);

			// ReplacementFile as source should not point to the placeholder file
			$this->source_url = $fs->pathToUrl($replacementFile);
			$this->replacer->setSource($this->source_url);

			$replacementFile->delete();
		}

		if (isset($optimizeData['files']) && isset($optimizeData['data'])) {
			$files = $optimizeData['files'];
			$data = $optimizeData['data'];
		} else {
			Log::addError('Something went wrong with handleOptimized', $optimizeData);
			return false;
		}

		$mainImageKey = $this->imageModel->get('mainImageKey');
		$mainFile = (isset($files) && isset($files[$mainImageKey])) ? $files[$mainImageKey] : false;

		if (false === $mainFile) {
			Log::addError('MainFile not set during success Api Conversion');
			return false;
		}

		if (! isset($mainFile['image']) || ! isset($mainFile['image']['file'])) {
			Log::addError('Optimizer didn\'t return file', $mainFile);
			return false;
		}

		$tempFile = $fs->getFile($mainFile['image']['file']);
		$res = $tempFile->copy($replacementFile);

		if (true === $res) {
			$this->newFile = $replacementFile;
			$tempFile->delete();

			$generate_metadata = true;

			$params = array(
				'success' => true,
				'generate_metadata' => $generate_metadata,
			);
			$this->updateMetaData($params);

			$result = true;

			$result = $this->replacer->replace();

			// Conversion done, but backup results.
			$this->imageModel->conversionSuccess(array('omit_backup' => false));
			return true;
		} else {
			return false;
		}
	}
} // class
