<?php
/**
 * For the full copyright and license information, please view the
 * docs/licenses/LICENSE.txt file that was distributed with this source code.
 */

namespace PrestaShop\PrestaShop\Core\Shop;

use Configuration;
use Context;
use ImageManager;
use PrestaShop\PrestaShop\Core\Domain\Shop\DTO\ShopLogoSettings;
use PrestaShop\PrestaShop\Core\Image\ImageFormatConfigurationInterface;
use PrestaShopException;
use Shop;
use Tools;

/**
 * Class LogoUploader used to manage upload of Shop logos and favicon.
 */
class LogoUploader
{
    /**
     * @var array
     */
    private $errors = [];

    public function __construct(
        private Shop $shop,
        private ImageFormatConfigurationInterface $imageFormatConfiguration,
        private string $imageDirection
    ) {
    }

    public function updateHeader()
    {
        if ($this->update('PS_LOGO', 'logo')) {
            [$width, $height] = getimagesize($this->imageDirection . Configuration::get('PS_LOGO'));
            Configuration::updateValue('SHOP_LOGO_HEIGHT', (int) round($height));
            Configuration::updateValue('SHOP_LOGO_WIDTH', (int) round($width));
        }
    }

    public function updateMail()
    {
        $this->update('PS_LOGO_MAIL', 'logo_mail');
    }

    public function updateInvoice()
    {
        $this->update('PS_LOGO_INVOICE', 'logo_invoice');
    }

    public function updateFavicon()
    {
        $shopId = (int) $this->shop->id;
        if ($shopId == Configuration::get('PS_SHOP_DEFAULT')) {
            $this->uploadIco('PS_FAVICON', $this->imageDirection . 'favicon.ico');
        }
        if ($this->uploadIco('PS_FAVICON', $this->imageDirection . 'favicon-' . $shopId . '.ico')) {
            Configuration::updateValue('PS_FAVICON', 'favicon-' . $shopId . '.ico');
        }

        Configuration::updateGlobalValue('PS_FAVICON', 'favicon.ico');
    }

    /**
     * Generic function which allows logo upload.
     *
     * @param string $fieldName
     * @param string $logoPrefix
     * @param array<string,array<string,string>> $files[] the array of files to avoid use $_POST
     *
     * @return bool
     *
     * @throws PrestaShopException in case of upload failure
     */
    public function update($fieldName, $logoPrefix, array $files = [])
    {
        $files = empty($files) ? $_FILES : $files;

        if (isset($files[$fieldName]['tmp_name'], $files[$fieldName]['tmp_name'], $files[$fieldName]['size'])) {
            $availableExtensions = (in_array($fieldName, ['PS_LOGO_MAIL', 'PS_LOGO_INVOICE'])) ? ShopLogoSettings::AVAILABLE_MAIL_AND_INVOICE_LOGO_IMAGE_EXTENSIONS : ShopLogoSettings::AVAILABLE_LOGO_IMAGE_EXTENSIONS;
            if ($error = ImageManager::validateUpload($files[$fieldName], Tools::getMaxUploadSize(), $availableExtensions)) {
                throw new PrestaShopException($error);
            }
            $tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS');

            if (!$tmpName || !move_uploaded_file($files[$fieldName]['tmp_name'], $tmpName)) {
                throw new PrestaShopException(sprintf('Upload of temporary file to %s has failed.', $tmpName));
            }

            if (ImageManager::isSvgMimeType($files[$fieldName]['type'])) {
                $fileExtension = '.svg';
            } else {
                $fileExtension = ($fieldName == 'PS_STORES_ICON') ? '.gif' : '.jpg';
            }
            $logoName = $this->getLogoName($logoPrefix, $fileExtension);

            if ($fieldName == 'PS_STORES_ICON') {
                if (!@ImageManager::resize($tmpName, $this->imageDirection . $logoName, null, null, 'gif', true)) {
                    throw new PrestaShopException(sprintf('An error occurred while attempting to copy shop icon %s.', $logoName));
                }
            } else {
                if (ImageManager::isSvgMimeType($files[$fieldName]['type'])) {
                    if (!copy($tmpName, $this->imageDirection . $logoName)) {
                        throw new PrestaShopException(sprintf('An error occurred while attempting to copy shop logo %s.', $logoName));
                    }
                } else {
                    /*
                    * Let's resolve which formats we will use for image generation.
                    *
                    * In case of .jpg images, the actual format inside is decided by ImageManager.
                    */
                    $configuredImageFormats = $this->imageFormatConfiguration->getGenerationFormats();
                    foreach ($configuredImageFormats as $imageFormat) {
                        if (!ImageManager::resize(
                            $tmpName,
                            $this->imageDirection . $this->getLogoName($logoPrefix, '.' . $imageFormat),
                            null,
                            null,
                            $imageFormat
                        )) {
                            throw new PrestaShopException(sprintf('An error occurred while attempting to copy shop logo %s.', $logoName));
                        }
                    }
                }
            }

            $idShop = $this->shop->id;
            $idShopGroup = null;

            // on updating PS_LOGO if the new file is an svg, copy old logo for mail and invoice
            if ($fieldName == 'PS_LOGO' && ImageManager::isSvgMimeType($files[$fieldName]['type'])) {
                if (empty(Configuration::get('PS_LOGO_MAIL'))) {
                    $newLogoMail = $this->getLogoName('logo_mail', '.' . pathinfo($this->imageDirection . Configuration::get($fieldName), \PATHINFO_EXTENSION));
                    // copy old logo file for mail
                    if (@copy($this->imageDirection . Configuration::get($fieldName), $this->imageDirection . $newLogoMail)) {
                        Configuration::updateValue('PS_LOGO_MAIL', $newLogoMail);
                    }
                }
                if (empty(Configuration::get('PS_LOGO_INVOICE'))) {
                    $newLogoInvoice = $this->getLogoName('logo_invoice', '.' . pathinfo(Configuration::get($fieldName), \PATHINFO_EXTENSION));
                    // copy old logo file for invoice
                    if (@copy($this->imageDirection . Configuration::get($fieldName), $this->imageDirection . $newLogoInvoice)) {
                        Configuration::updateValue('PS_LOGO_INVOICE', $newLogoInvoice);
                    }
                }
            }

            // manage deleting old logo
            if (!count($this->errors) && @filemtime($this->imageDirection . Configuration::get($fieldName))) {
                if (Shop::isFeatureActive()) {
                    $this->updateInMultiShopContext($idShop, $idShopGroup, $fieldName);
                } else {
                    @unlink($this->imageDirection . Configuration::get($fieldName));
                }
            }

            Configuration::updateValue($fieldName, $logoName, false, $idShopGroup, $idShop);
            unlink($tmpName);

            return true;
        }

        return false;
    }

    private function updateInMultiShopContext(&$idShop, &$idShopGroup, $fieldName)
    {
        if (Shop::getContext() == Shop::CONTEXT_SHOP) {
            $idShop = Shop::getContextShopID();
            $idShopGroup = Shop::getContextShopGroupID();
            Shop::setContext(Shop::CONTEXT_ALL);
            $logoAll = Configuration::get($fieldName);
            Shop::setContext(Shop::CONTEXT_GROUP);
            $logoGroup = Configuration::get($fieldName);
            Shop::setContext(Shop::CONTEXT_SHOP);
            $logoShop = Configuration::get($fieldName);
            if ($logoAll != $logoShop && $logoGroup != $logoShop && $logoShop != false) {
                @unlink($this->imageDirection . Configuration::get($fieldName));
            }
        } elseif (Shop::getContext() == Shop::CONTEXT_GROUP) {
            $idShopGroup = Shop::getContextShopGroupID();
            Shop::setContext(Shop::CONTEXT_ALL);
            $logoAll = Configuration::get($fieldName);
            Shop::setContext(Shop::CONTEXT_GROUP);
            if ($logoAll != Configuration::get($fieldName)) {
                @unlink($this->imageDirection . Configuration::get($fieldName));
            }
        }
    }

    public function uploadIco($name, $destination, $files = [])
    {
        $files = empty($files) ? $_FILES : $files;

        if (isset($files[$name]['tmp_name']) && !empty($files[$name]['tmp_name'])) {
            if ($error = ImageManager::validateIconUpload($files[$name])) {
                throw new PrestaShopException($error);
            } elseif (!copy($_FILES[$name]['tmp_name'], $destination)) {
                throw new PrestaShopException(Context::getContext()->getTranslator()->trans('An error occurred while uploading the favicon: cannot copy file "%s" to folder "%s".', [$files[$name]['tmp_name'], $destination], 'Admin.Design.Notification'));
            }
        }

        return !count($this->errors);
    }

    /**
     * @param string $logoPrefix
     * @param string $fileExtension
     *
     * @return string
     */
    private function getLogoName($logoPrefix, $fileExtension)
    {
        $shopId = $this->shop->id;

        $logoName = $logoPrefix
            . '-'
            . (int) Configuration::get('PS_IMG_UPDATE_TIME')
            . (int) $shopId . $fileExtension;

        if ($this->shop->getContext() == Shop::CONTEXT_ALL
            || $shopId == 0
            || Shop::isFeatureActive() == false
        ) {
            $logoName = $logoPrefix . '-' . (int) Configuration::get('PS_IMG_UPDATE_TIME') . $fileExtension;
        }

        return $logoName;
    }
}
