<?php

/**
 * Image uploader class
 */

namespace Extendify\Shared\Services\Import;

defined('ABSPATH') || die('No direct access.');

/**
 * This class responsible for uploading the image.
 */

class ImageUploader
{
    /**
     * The mime types we support !!
     *
     * @var string[]
     */
    protected $mimes = [
        'image/gif' => '.gif',
        'image/jpeg' => '.jpg',
        'image/png' => '.png',
        'image/x-png' => '.png',
        'image/jp2' => '.jp2',
        'image/jpx' => '.jp2',
        'image/webp' => '.wbmp',
        'image/avif' => '.avif',
    ];

    /**
     * The images domains we use
     *
     * @var string[] the domain names.
     */
    public static $imagesDomains = [
        'unsplash.com',
        'extendify.com',
    ];

    /**
     * Upload the image and return the attachment information
     * If the attachment is already there, then return the
     * attachment information only.
     *
     * @param string      $image  the image url to upload.
     * @param string|null $author the WordPress post author.
     * @return array|\WP_Error
     */
    public function uploadImage($image, $author = null) // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
    {
        require_once ABSPATH . 'wp-admin/includes/image.php';

        $image = preg_replace('/(\?.*?)\?/', '$1&', $image);
        $image = str_replace('%2C', ',', $image);
        // If the attachment has been already uploaded, just return it.
        $attachment = $this->getAttachmentIfExists($image);

        if ($attachment instanceof \WP_Post) {
            return [
                'attachment_id' => $attachment->ID,
                'url' => $attachment->guid,
            ];
        }

        $imageHeadersInformation = wp_remote_retrieve_headers(wp_safe_remote_head($image));

        if (!empty($imageHeadersInformation)) {
            $headers = $imageHeadersInformation->getAll();
            $fileMimeType = $headers['content-type'];
        } else {
            $fileMimeType = wp_get_image_mime($image);
        }

        if (!array_key_exists($fileMimeType, $this->mimes)) {
            return new \WP_Error(2002, 'File type is not allowed.');
        }

        if (!preg_match('(' . implode('|', array_map('preg_quote', self::$imagesDomains)) . ')i', $image)) {
            $imageUrl = esc_url_raw($image);
        } else {
            $parsedUrl = wp_parse_url($image);
            parse_str($parsedUrl['query'], $params);

            if (!isset($params['w'])) {
                $params['w'] = 1280;
            }

            if (isset($params['orientation'])) {
                if ($params['orientation'] === 'portrait') {
                    $params['h'] = 1440;
                    unset($params['w']);
                } elseif (in_array($params['orientation'], ['landscape', 'square'], true) && isset($params['w'])) {
                    $params['w'] = 1440;
                }

                unset($params['orientation']);
            }

            $params['auto'] = 'auto,compress';
            $params['q'] = 70;

            $imageUrl = $parsedUrl['scheme'] . '://'
            . $parsedUrl['host']
            . $parsedUrl['path']
            . '?'
            . http_build_query($params);
        }//end if

        $imageSha = sha1($image);
        $upload = $this->handleImageUpload($imageUrl, $imageSha, $fileMimeType);
        if (is_wp_error($upload)) {
            return $upload;
        }

        $attachmentId = $this->createAttachment($upload, $image, $author);
        if (is_wp_error($attachmentId)) {
            return $attachmentId;
        }

        $upload['attachment_id'] = $attachmentId;
        return $upload;
    }

    /**
     * Handle the image upload process.
     *
     * @param string $imageUrl     The image URL.
     * @param string $imageSha     The image SHA.
     * @param string $fileMimeType The file mime type.
     * @return array|\WP_Error
     */
    protected function handleImageUpload($imageUrl, $imageSha, $fileMimeType)
    {
        $upload = $this->upload($imageUrl, $imageSha, $fileMimeType);

        if ($upload['error']) {
            return new \WP_Error(2003, $upload['error']);
        }

        if (!wp_getimagesize($upload['file'])) {
			      // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
            @unlink($upload['file']);
            $imageUrl = str_replace('avif', 'jpg', $imageUrl);
            $upload = $this->upload($imageUrl, $imageSha, 'image/jpeg');
        }

		    // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
        if (!@filesize($upload['file']) || !wp_getimagesize($upload['file'])) {
			      // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
            @unlink($upload['file']);
            return new \WP_Error(2001, 'File is not a valid image.');
        }

        return $upload;
    }

    /**
     * Create the attachment in WordPress.
     *
     * @param array       $upload The upload information.
     * @param string      $image  The original image URL.
     * @param string|null $author The post author.
     * @return int|\WP_Error
     */
    protected function createAttachment($upload, $image, $author)
    {
        $attachment = [
            'guid' => $upload['url'],
            'post_mime_type' => $upload['type'],
            'post_title' => sha1($image),
            'post_content' => '',
            'post_status' => 'inherit',
            'post_author' => $author,
        ];

        $attachmentId = wp_insert_attachment($attachment, $upload['file']);

        if (is_wp_error($attachmentId) || !$attachmentId) {
            return new \WP_Error(2004, 'There was an error while adding the attachment record in the database.');
        }

        $metadata = wp_generate_attachment_metadata($attachmentId, $upload['file']);
        if (!empty($metadata)) {
            wp_update_attachment_metadata($attachmentId, $metadata);
        }

        return $attachmentId;
    }

    /**
     * Upload the image and return the uploaded file information.
     *
     * @param string $imageUrl     The image url to upload.
     * @param string $imageSha     The image sha.
     * @param string $fileMimeType The file mime type.
     * @return array {
     *      Information about the newly-uploaded file.
     *
     *      @type string $file Filename of the newly-uploaded file.
     *      @type string $url URL of the uploaded file.
     *      @type string $type File type.
     *      @type string|false $error Error message, if there has been an error.
     * }
     */
    protected function upload($imageUrl, $imageSha, $fileMimeType)
    {
        $response = wp_remote_get($imageUrl);
        $body = trim(wp_remote_retrieve_body($response));
        return wp_upload_bits($imageSha . $this->mimes[$fileMimeType], null, $body);
    }

    /**
     * Check if the image has been already uploaded or not, if yes
     * then return the attachment information.
     *
     * @param string $image the image url we need to check for.
     * @return array|\WP_Post|null
     */
    protected function getAttachmentIfExists($image)
    {
        $postId = attachment_url_to_postid($image);
        if ($postId) {
            return get_post($postId);
        }

        $attachment = get_posts([
            'post_type' => 'attachment',
            'numberposts' => 1,
            's' => sha1($image),
            'post_status' => 'inherit',
            'post_mime_type' => implode(',', array_keys($this->mimes)),
        ]);

        return $attachment ? $attachment[0] : [];
    }
}
