<?php
/**
 * NOTICE OF LICENSE.
 *
 * Copyright 2022 Kosmonaft.dev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *  @author    Alexandre DEBUSSCHÈRE <alexandre@kosmonaft.dev>
 *  @copyright 2022 - 2023 Kosmonaft.dev
 *  @license   MIT
 */

if (!defined('_PS_VERSION_')) {
    exit;
}

/**
 * Class MRMaxMTOMResponseReader
 */
class MRMaxMTOMResponseReader
{

    const CONTENT_TYPE = 'Content-Type: application/json';
    const UUID = '/--uuid:/';
    const CONTENT = 'Content-';

    /** @var array */
    public $attachments = [];

    /** @var array */
    public $soapResponse = [];

    /** @var null|string */
    public $uuid = null;

    /**
     * @param string $response
     * @throws Exception
     */
    public function __construct($response)
    {
        if (strpos($response, self::CONTENT_TYPE) === false) {
            throw new InvalidArgumentException('The response is not of type: application/xop+xml');
        }

        $this->parseResponse($response);
    }

    /**
     * @param bool $assoc
     * @param int $depth
     * @param int $options
     * @return mixed If found returns the decoded json, null otherwise.
     */
    public function getJson($assoc = false, $depth = 512, $options = 0)
    {
        return array_reduce($this->attachments, function ($acc, $cur) use ($assoc, $depth, $options) {
            if ($cur['header']['Content-ID'] == '<jsonInfos>') {
                $acc = MRMaxTools::jsonDecode($cur['data'], $assoc, $depth, $options);
            }

            return $acc;
        }, null);
    }

    /**
     * @return bool
     */
    public function hasJson()
    {
        return array_reduce($this->attachments, function ($acc, $cur) {
            if ($cur['header']['Content-ID'] == '<jsonInfos>' && Tools::strlen($cur['data'])) {
                $acc = true;
            }

            return $acc;
        }, false);
    }

    /**
     * @return string Binary file representing the PDF label
     */
    public function getLabel()
    {
        return array_reduce($this->attachments, function ($acc, $cur) {
            if ($cur['header']['Content-ID'] == '<label>') {
                $acc = $cur['data'];
            }

            return $acc;
        }, null);
    }

    /**
     * @return bool
     */
    public function hasLabel()
    {
        return array_reduce($this->attachments, function ($acc, $cur) {
            if ($cur['header']['Content-ID'] == '<label>' && Tools::strlen($cur['data'])) {
                $acc = true;
            }

            return $acc;
        }, false);
    }

    /**
     * @return string Binary file representing the PDF CN23
     */
    public function getCN23()
    {
        return array_reduce($this->attachments, function ($acc, $cur) {
            if ($cur['header']['Content-ID'] == '<cn23>') {
                $acc = $cur['data'];
            }

            return $acc;
        }, null);
    }

    /**
     * @return bool
     */
    public function hasCN23()
    {
        return array_reduce($this->attachments, function ($acc, $cur) {
            if ($cur['header']['Content-ID'] == '<cn23>' && Tools::strlen($cur['data'])) {
                $acc = true;
            }

            return $acc;
        }, false);
    }

    /**
     * @return string Binary file representing the PDF delivery paper
     */
    public function getDeliveryPaper()
    {
        return array_reduce($this->attachments, function ($acc, $cur) {
            if ($cur['header']['Content-ID'] == '<deliveryPaper>') {
                $acc = $cur['data'];
            }

            return $acc;
        }, null);
    }

    /**
     * @return bool
     */
    public function hasDeliveryPaper()
    {
        return array_reduce($this->attachments, function ($acc, $cur) {
            if ($cur['header']['Content-ID'] == '<deliveryPaper>' && Tools::strlen($cur['data'])) {
                $acc = true;
            }

            return $acc;
        }, false);
    }

    /**
     * @param string $response
     * @return void
     */
    private function parseResponse($response)
    {
        $content = [];
        $matches = [];

        preg_match_all(self::UUID, $response, $matches, PREG_OFFSET_CAPTURE);

        for ($i = 0; $i < count($matches[0]) - 1; $i++) {
            if ($i + 1 < count($matches[0])) {
                $content[$i] = Tools::substr(
                    $response,
                    $matches[0][$i][1],
                    $matches[0][$i + 1][1] - $matches[0][$i][1],
                    'ISO-8859-1'
                );
            } else {
                $content[$i] = Tools::substr(
                    $response,
                    $matches[0][$i][1],
                    Tools::strlen($response, 'ISO-8859-1'),
                    'ISO-8859-1'
                );
            }
        }

        foreach ($content as $part) {
            if ($this->uuid == null) {
                $uuidStart = strpos($part, self::UUID) + 7;
                $uuidEnd = strpos($part, "\r\n", $uuidStart);

                $this->uuid = Tools::substr(
                    $part,
                    $uuidStart,
                    $uuidEnd - $uuidStart,
                    'ISO-8859-1'
                );
            }

            $header = $this->extractHeader($part);
            if (count($header) > 0) {
                if (strpos($header['Content-Type'], 'type="text/xml"') !== false) {
                    $this->soapResponse['header'] = $header;
                    $this->soapResponse['data'] = trim(Tools::substr(
                        $part,
                        $header['offsetEnd'],
                        false,
                        'ISO-8859-1'
                    ));
                } else {
                    $attachment = [];
                    $attachment['header'] = $header;
                    $attachment['data'] = trim(Tools::substr(
                        $part,
                        $header['offsetEnd'],
                        Tools::strlen($part, 'ISO-8859-1'),
                        'ISO-8859-1'
                    ));

                    $this->attachments[] = $attachment;
                }
            }
        }
    }

    /**
     * Exclude the header from the Web Service response.
     *
     * @param string $part
     * @return array $header
     */
    private function extractHeader($part)
    {
        $header = array();
        $headerLineStart = strpos($part, self::CONTENT);
        $endLine = 0;

        while ($headerLineStart !== false) {
            $endLine = strpos($part, "\r\n", $headerLineStart);
            $headerLine = explode(
                ':',
                Tools::substr($part, $headerLineStart, $endLine - $headerLineStart, 'ISO-8859-1')
            );
            $header[trim($headerLine[0])] = trim($headerLine[1]);

            $headerLineStart = strpos($part, self::CONTENT, $endLine);
        }

        $header['offsetEnd'] = $endLine;

        return $header;
    }
}
