<?php

declare(strict_types=1);

/*
 * This file is part of vonRotenberg Shopware API Bundle.
 *
 * (c) vonRotenberg
 *
 * @license proprietary
 */

namespace vonRotenberg\ShopwareApiBundle\API;

use Contao\System;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

class Shopware
{
    protected $serializer;
    protected $httpClient;
    protected $clientId;
    protected $clientSecret;
    protected $apiEndpoint;

    private $token;
    private $tokenType;

    public function __construct(SerializerInterface $serializer, HttpClientInterface $httpClient)
    {
        $this->serializer = $serializer;
        $this->httpClient = $httpClient;

        $this->clientId = System::getContainer()->getParameter('vonrotenberg_shopware_api.credentials.client_id');
        $this->clientSecret = System::getContainer()->getParameter('vonrotenberg_shopware_api.credentials.client_secret');
        $this->apiEndpoint = System::getContainer()->getParameter('vonrotenberg_shopware_api.credentials.api_endpoint');
    }

    protected function getClientId()
    {
        return $this->clientId;
    }

    protected function getClientSecret()
    {
        return $this->clientSecret;
    }

    protected function getApiEndpoint()
    {
        return rtrim($this->apiEndpoint,'/');
    }

    protected function sendRequest(string $relEndpoint, array $options, string $method = 'GET',bool $blnFQDNEndpoint=false)
    {
        if ($blnFQDNEndpoint)
        {
            return $this->httpClient->request($method,$relEndpoint,$options);
        }

        $relEndpoint = '/' . ltrim($relEndpoint,'/');

        $request = $this->httpClient->request($method,$this->getApiEndpoint().$relEndpoint,$options);

        if ($request->getStatusCode() == 401)
        {
            throw new \RuntimeException('Unauthorized');
        }

        return $request;
    }

    protected function getAccessToken()
    {
        $options = [
            'body' => json_encode([
                'grant_type' => 'client_credentials',
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret
            ]),
            'headers' => ['Content-Type: application/json', 'Accept: application/json'],
        ];

        $response = $this->sendRequest('oauth/token',$options,'POST');

        if ($response->getStatusCode() == 200)
        {
            $content = $response->getContent();

            return json_decode($content);
        }

        throw new \RuntimeException('Can\'t retrieve access token. Check credentials.');
    }

    protected function getAuthentication()
    {
        $Token = $this->getAccessToken();
        return 'Authorization: ' . $Token->token_type . ' ' . $Token->access_token;
    }

    public function isShopwareRunning(): bool
    {
        $options = [
            'headers' => [
                $this->getAuthentication(),
                'Content-Type: application/json',
                'Accept: application/json'
            ],
        ];

        $response = $this->sendRequest('_info/health-check',$options,'GET');

        if ($response->getStatusCode() == 200)
        {
            return true;
        }

        return false;
    }

    public function getProductsForSku(string $strSku, bool $blnWildcardSearch=false, bool $blnShort=false): ResponseInterface
    {
        $options = [
            'headers' => [
                $this->getAuthentication(),
                'Content-Type: application/json',
                'Accept: application/json'
            ],
            'body' => json_encode([
                'filter' => [
                    [
                        'type' => !$blnWildcardSearch ? 'equals' : 'contains',
                        'field' => 'productNumber',
                        'value' => $strSku
                    ]
                ],
                'sort' => [
                    [
                        'field' => 'productNumber',
                        'order' => 'ASC',
                        'naturalSorting' => true
                    ]
                ]
            ])
        ];

        if ($blnShort)
        {
            $options['body'] = json_encode(
                array_merge(
                    json_decode($options['body'],true),
                    [
                        'fields' => [
                            'name'
                        ]
                    ]
                )
            );
        }

        return $this->sendRequest('search/product',$options,'POST');
    }

    public function queryAPI(string $strUrlFragment, ?string $strBody, string $strMethod = 'GET', bool $blnAuthenticate=true): ResponseInterface
    {
        $options = [
            'headers' => [
                'Content-Type: application/json',
                'Accept: application/json'
            ]
        ];

        if ($blnAuthenticate)
        {
            $options['headers'][] = $this->getAuthentication();
        }

        if ($strBody !== null)
        {
            $options['body'] = $strBody;
        }

        return $this->sendRequest($strUrlFragment,$options,$strMethod);
    }

    protected function checkBySkuIfExists(string $strSku): bool
    {
        $Result = $this->getProductsForSku($strSku,false,true);

        if ($Result->getStatusCode() == 200)
        {
            $Content = json_decode($Result->getContent());

            if ($Content->total)
            {
                return true;
            }
        }
        return false;
    }

    public function addOrUpdateProductBySku(string $strSku, array $arrData)/*: bool*/
    {
        $blnInsert = true;
        // Update or insert check
        if (($searchResponse = $this->getProductsForSku($strSku))->getStatusCode() == 200)
        {
            $Product = json_decode($searchResponse->getContent());

            if ($Product->total)
            {
                $blnInsert = false;
            }
        }

        // Prepare product data
        $arrData = [
            'taxId' => '018e65c0485071508949c072f8dc18bd',
            'id' => $blnInsert ? $this->createUuid() : $Product->data[0]->id,
            'price' => [
                [
                    'currencyId' => 'b7d2554b0ce847cd82f3ac9bd1c0dfca',
                    'gross' => 9.9,
                    'net' => 9.9/119*100,
                    'linked' => true
                ]
            ],
            'productNumber' => $strSku,
            'stock' => 9999999,
            'name' => 'Superheldenumhang',
            'customFields' => [
                'custom_wine_attributes_jahrgang' => "2020"
            ]
        ];

        $options = [
            'headers' => [
                $this->getAuthentication(),
                'Content-Type: application/json',
                'Accept: application/json'
            ],
            'body' => json_encode($arrData)
        ];

        if ($blnInsert)
        {
            $response = $this->sendRequest('product',$options,'POST');
        } else {
            $response = $this->sendRequest('product/'.$Product->data[0]->id,$options,'PATCH');
        }

        return in_array($response->getStatusCode(),[200,201,202,203,204]);
    }

    protected function createUuid($data = null) {
        // Generate 16 bytes (128 bits) of random data or use the data passed into the function.
        $data = $data ?? random_bytes(16);
        assert(strlen($data) == 16);

        // Set version to 0100
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
        // Set bits 6-7 to 10
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80);

        // Output the 36 character UUID.
        return bin2hex($data);
    }
}