<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonDHL\Classes\DHLPortalConfigScraper;

use Exception;
use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\Exception\ClientException;
use Shopware\Plugins\ViisonCommon\Classes\Util\Util as ViisonCommonUtil;

/**
 * DHL Config Scraper
 *
 * Communicates with DHL Geschäftskundenportal to check login credentials and scrape configuration for webservice.
 */
class DhlConfigScraper
{
    const DHL_BCP_URL = 'https://geschaeftskunden.dhl.de/customeradministration/api/v1/';

    /**
     * @var HttpClient
     */
    private $client;

    /**
     * @var string
     */
    private $userName;

    /**
     * @var string
     */
    private $password;

    /**
     * @var string
     */
    private $accessToken;

    /**
     * @param string $userName
     * @param string $password
     * @throws DhlConfigScraperInvalidCredentialsException
     */
    public function __construct($userName, $password)
    {
        $this->userName = $userName;
        $this->password = $password;

        if (!$this->userName || !$this->password) {
            throw DhlConfigScraperInvalidCredentialsException::userNameOrPasswordMissing();
        }

        $this->client = new HttpClient();
    }

    /**
     * Try to access the "DHL Geschäftskundenportal" using the provided credentials.
     *
     * @throws AbstractDhlConfigScraperException|\GuzzleHttp\Exception\GuzzleException
     */
    public function checkCredentials()
    {
        try {
            $this->login();

            return true;
        } catch (DhlConfigScraperInvalidCredentialsException $e) {
            return false;
        } catch (AbstractDhlConfigScraperException $e) {
            throw $e;
        } catch (Exception $e) {
            // Wrap any error, because that probably means the website changed and made this component incompatible
            throw DhlConfigScraperCommunicationException::unexpectedError($e);
        }
    }

    /**
     * Access the "DHL Geschäftskundenportal" using the provided credentials, then read the customer number and the
     * billing numbers from the contractData.
     *
     * @throws AbstractDhlConfigScraperException|\GuzzleHttp\Exception\GuzzleException
     */
    public function scrape()
    {
        try {
            $this->login();

            return $this->getContractData();
        } catch (AbstractDhlConfigScraperException $e) {
            throw $e;
        } catch (Exception $e) {
            // Wrap any error, because that probably means the website changed and made this component incompatible
            throw DhlConfigScraperCommunicationException::unexpectedError($e);
        }
    }

    /**
     * Login using the login form.
     *
     * @throws DhlConfigScraperInvalidCredentialsException|\GuzzleHttp\Exception\GuzzleException
     */
    private function login()
    {
        $loginURI = 'https://sso.geschaeftskunden.dhl.de/auth/realms/GkpExternal/protocol/openid-connect/token';

        if (ViisonCommonUtil::assertMinimumShopwareVersion('5.7.0')) {
            $requestOptions = [
                'form_params' => [
                    'grant_type' => 'password',
                    'username' => $this->userName,
                    'password' => $this->password,
                    'client_id' => 'external-frame',
                ],
            ];
        } else {
            $requestOptions = [
                'body' => [
                    'grant_type' => 'password',
                    'username' => $this->userName,
                    'password' => $this->password,
                    'client_id' => 'external-frame',
                ],
            ];
        }

        try {
            $response = $this->client->post($loginURI, $requestOptions);
        } catch (ClientException $e) {
            if ($e->getResponse()->getStatusCode() === 401) {
                throw DhlConfigScraperInvalidCredentialsException::loginFailed();
            } else {
                throw $e;
            }
        }

        $responseArray = json_decode($response->getBody());

        $this->accessToken = $responseArray->access_token;
    }

    /**
     * Retrieve the customerNumber from the user endpoint
     *
     * @return string customerNumber
     */
    private function getCustomerNumber()
    {
        $response = $this->client->get(self::DHL_BCP_URL . 'user', [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->accessToken,
            ],
        ]);

        $responseArray = json_decode($response->getBody());

        return $responseArray[0]->ekp;
    }

    /**
     * Retrieve the contractData from the customerData endpoint
     *
     * @return DhlContractData
     */
    private function getContractData()
    {
        $customerNumber = $this->getCustomerNumber();

        $response = $this->client->get(self::DHL_BCP_URL . 'customerdata/' . $customerNumber, [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->accessToken,
            ],
        ]);

        $responseArray = json_decode($response->getBody());
        $contracts = $responseArray->contracts;

        // Format response to only contain needed information
        $data = [];
        foreach ($contracts as $contract) {
            $accountNumber = $contract->contractBillingNumber;
            $data[$contract->bookingText] = [$accountNumber];
        }

        return new DhlContractData(
            $customerNumber,
            $data
        );
    }
}
