<?php

declare(strict_types=1);

namespace FiloBlu\Storelocator\Model\Google;

use Exception;
use FiloBlu\Storelocator\Model\Stores;
use FiloBlu\Storelocator\Model\WorkingTimes;
use Google\Client;
use Google\Service\MyBusinessAccountManagement;
use Google\Service\MyBusinessBusinessInformation;
use Google\Service\MyBusinessBusinessInformation\BusinessHours;
use Google\Service\MyBusinessBusinessInformation\Categories;
use Google\Service\MyBusinessBusinessInformation\Category;
use Google\Service\MyBusinessBusinessInformation\LatLng;
use Google\Service\MyBusinessBusinessInformation\Location;
use Google\Service\MyBusinessBusinessInformation\PhoneNumbers;
use Magento\Backend\Helper\Data;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Session\SessionManagerInterface;
use Magento\Store\Model\StoreManagerInterface;

use function count;

/**
 *
 */
class Api
{
    /**
     * @var string
     */
    const CONFIG_FILE_DIR_PREFIX = 'filoblu/storelocator/';
    /**
     * @var \Magento\Backend\Helper\Data
     */
    protected $helper;
    /**
     * @var \Magento\Framework\Session\SessionManagerInterface
     */
    protected $session;
    /**
     * @var \Magento\Framework\App\RequestInterface
     */
    protected $request;
    /**
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $config;
    /**
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $storeManager;
    /**
     * @var \Magento\Framework\App\Filesystem\DirectoryList
     */
    protected $directoryList;
    /**
     * @var \FiloBlu\Storelocator\Model\WorkingTimes
     */
    protected $workingTimes;
    /**
     * @var \Google\Client
     */
    private $client;

    /**
     * @param Data $helper
     * @param SessionManagerInterface $session
     * @param RequestInterface $request
     * @param ScopeConfigInterface $config
     * @param StoreManagerInterface $storeManager
     * @param DirectoryList $directory_list
     * @param WorkingTimes $workingTimes
     */
    public function __construct(
        Data $helper,
        SessionManagerInterface $session,
        RequestInterface $request,
        ScopeConfigInterface $config,
        StoreManagerInterface $storeManager,
        DirectoryList $directory_list,
        WorkingTimes $workingTimes
    ) {
        $this->helper = $helper;
        $this->session = $session;
        $this->request = $request;
        $this->config = $config;
        $this->storeManager = $storeManager;
        $this->directoryList = $directory_list;
        $this->workingTimes = $workingTimes;
    }

    /**
     * @param string $regionCode
     * @param string $languageCode
     * @return array
     */
    public function getCategoryList($regionCode = 'IT', $languageCode = 'it-IT')
    {
        try {
            $api = new MyBusinessBusinessInformation($this->getClient());
            $result = $api->categories->listCategories(
                [
                    'regionCode'   => $regionCode,
                    'languageCode' => $languageCode
                ]
            );

            $array = [];
            foreach ($result->getCategories() as $categoryObj) {
                $array[$categoryObj->getName()] = $categoryObj->getName();
            }

            asort($array);

            return $array;
        } catch (Exception $ex) {
            return [];
        }
    }

    /**
     * Get Client
     *
     * @return \Google\Client
     * @throws Exception
     */
    public function getClient()
    {
        if (!$this->client) {
            $app_name = $this->config->getValue('filoblu_storelocator_section/google_my_business/gmb_api_app_name');

            $this->client = new  Client();
            $this->client->setApplicationName($app_name);

            $this->client->setClientId(
                $this->config->getValue('filoblu_storelocator_section/google_my_business/gmb_api_client_id')
            );
            $this->client->setClientSecret(
                $this->config->getValue('filoblu_storelocator_section/google_my_business/gmb_api_client_secret')
            );
            $this->client->setAccessType('offline');
            $this->client->setApprovalPrompt('force');

            $token = $this->getAuthData();

            if ($token) {
                $this->client->setAccessToken($token);

                $token = json_decode($token, true);
                if ($this->client->isAccessTokenExpired()) {
                    //refresh access token
                    $this->client->refreshToken($token['refresh_token']);
                    $updated_token = $this->client->getAccessToken();
                    //keep the original refresh_token for later reuse
                    $updated_token['refresh_token'] = $token['refresh_token'];
                    $this->storeAuthData(json_encode($updated_token));
                }
            }

            $this->client->setState(
                $this->helper->getUrl('adminhtml/system_config/edit', ['section' => 'filoblu_storelocator_section'])
            );

            $scopes = ['https://www.googleapis.com/auth/plus.business.manage'];

            $this->client->setScopes($scopes);

            $this->client->setRedirectUri(
                $this->config->getValue('filoblu_storelocator_section/google_my_business/gmb_redirect_uri')
            );

            if (isset($_GET['code'])) {
                $this->getClient()->authenticate($_GET['code']);

                $updated_token = json_decode($this->client->getAccessToken(), true);

                //keep the original refresh_token for later reuse
                //$updated_token['refresh_token'] = $updated_token['refresh_token'];

                $this->storeAuthData(json_encode($updated_token));

                header(
                    'Location: ' . $this->helper->getUrl(
                        'adminhtml/system_config/edit', ['section' => 'filoblu_storelocator_section']
                    )
                );
                // TODO: ????????
                exit();
            }
        }

        return $this->client;
    }

    /**
     * @return false|mixed
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    private function getAuthData()
    {
        $filename = $this->directoryList->getPath(
                'media'
            ) . DIRECTORY_SEPARATOR . self::CONFIG_FILE_DIR_PREFIX . 'oauth_token.json';

        if (file_exists($filename)) {
            return json_decode(file_get_contents($filename), true);
        }

        return false;
    }

    /**
     * @param $token
     * @return true
     * @throws \Magento\Framework\Exception\FileSystemException
     * @throws \Exception
     */
    private function storeAuthData($token)
    {
        if (file_put_contents(
                $this->directoryList->getPath(
                    'media'
                ) . DIRECTORY_SEPARATOR . self::CONFIG_FILE_DIR_PREFIX . 'oauth_token.json',
                json_encode($token)
            ) > 0) {
            return true;
        }

        throw new Exception('Could not write the OAuth token to disk');
    }

    /**
     * @param \FiloBlu\Storelocator\Model\Stores $store
     * @return \Google\Service\MyBusinessBusinessInformation\Location|string
     */
    public function createLocation(Stores $store)
    {
        try {
            $api = new MyBusinessBusinessInformation($this->getClient());
            $location = $this->getLocationObj($store);
            return $api->accounts_locations->create(
                $this->config->getValue('filoblu_storelocator_section/google_my_business/gmb_account_name'), $location,
                ['requestId' => uniqid()]
            );
        } catch (Exception $ex) {
            return $ex->getMessage();
        }
    }

    /**
     * @param $store
     * @return \Google\Service\MyBusinessBusinessInformation\Location
     */
    public function getLocationObj($store)
    {
        $location = new Location();
//        $location->setName($store->getTitle());


        $languageCode = 'en';

        if ($store->getGmbLanguageCode()) {
            $languageCode = $store->getGmbLanguageCode();
        }

        $location->setLanguageCode($languageCode);
        $location->setStoreCode($store->getId());
        $location->setName($store->getTitle());

        $phoneNumbers = new PhoneNumbers();
        $phoneNumbers->setPrimaryPhone($store->getPhone1());
        $phoneNumbers->setAdditionalPhones([$store->getPhone2()]);
        $location->setPhoneNumbers($phoneNumbers);
        // Address

//        $address = new Google_Service_MyBusiness_Address();
//        $address->setAddressLines($store->getStreet());
//        $address->setLocality($store->getCity());
//        $address->setAdministrativeArea($store->getCity());
//        $address->setCountry($store->getCountry());
//        $address->setPostalCode($store->getPostcode());
//        $location->setAddress($address);

        // Primary category
        $primaryCategory = new Category();
        $primaryCategory->setName($store->getGmbCat());

        $categories = new Categories();
        $categories->setPrimaryCategory($primaryCategory);
        $location->setCategories($categories);


        $location->setWebsiteUri($store->getWebsiteUrl());

        // Lat LNg
        $latlng = new LatLng();
        $latlng->setLatitude($store->getLatitude());
        $latlng->setLongitude($store->getLongitude());
        $location->setLatlng($latlng);

        // Hours
        $hours = new BusinessHours();
        $hoursArray = [];

        $workingTimesCollection = $this->workingTimes->loadByStoreId($store->getId());
        $workingTimesCollectionArray = $workingTimesCollection->getData();
        $wtIndex = 0;
        $skipNextForContinuedTimetable = false;

        foreach ($workingTimesCollectionArray as $workingTime) {
            if ($skipNextForContinuedTimetable) {
                $skipNextForContinuedTimetable = false;
                $wtIndex++;
                continue;
            }
            $isClosed = $workingTime['closed'];

            if ($isClosed || (!$workingTime['from'] && !$workingTime['to'])) {
                $wtIndex++;
                continue;
            }

            $fromHH = '00';
            $fromMM = '00';
            $fromSS = '00';

            if ($workingTime['from']) {
                $timeFrom = explode(':', explode(' ', $workingTime['from'])[1]);
                $fromHH = $timeFrom[0];
                $fromMM = $timeFrom[1];
                $fromSS = $timeFrom[2];

                // Closed day
                if ($fromHH == '00' && $fromMM == '00' && isset($workingTimesCollectionArray[$wtIndex + 1])) {
                    $wtNext = $workingTimesCollectionArray[$wtIndex + 1];
                    if ($wtNext['day'] == $workingTime['day']) {
                        if ($wtNext['to']) {
                            $timeToNext = explode(':', explode(' ', $wtNext['to'])[1]);
                            $toHHNext = $timeToNext[0];
                            $toMMNext = $timeToNext[1];
                            $toSSNext = $timeToNext[2];
                            if ($toHHNext == '00' && $toMMNext == '00') {
                                $wtIndex++;
                                continue;
                            }
                        }
                    }
                }
            }

            $toHH = '00';
            $toMM = '00';
            $toSS = '00';

            if ($workingTime['to']) {
                $timeTo = explode(':', explode(' ', $workingTime['to'])[1]);
                $toHH = $timeTo[0];
                $toMM = $timeTo[1];
                $toSS = $timeTo[2];

                // Closed day
                if ($toHH == '00' && $toMM == '00' && isset($workingTimesCollectionArray[$wtIndex - 1])) {
                    $wtPrev = $workingTimesCollectionArray[$wtIndex - 1];
                    if ($wtPrev['day'] == $workingTime['day']) {
                        if ($wtPrev['from']) {
                            $timeFromPrev = explode(':', explode(' ', $wtPrev['from'])[1]);
                            $fromHHPrev = $timeFromPrev[0];
                            $fromMMPrev = $timeFromPrev[1];
                            $fromSSPrev = $timeFromPrev[2];
                            if ($fromHHPrev == '00' && $fromMMPrev == '00') {
                                $wtIndex++;
                                continue;
                            }
                        }
                    }
                }

                // Continued timetable
                if ($toHH == '00' && $toMM == '00' && isset($workingTimesCollectionArray[$wtIndex + 1])) {
                    $wtNext = $workingTimesCollectionArray[$wtIndex + 1];
                    if ($wtNext['day'] == $workingTime['day']) {
                        if ($wtNext['to']) {
                            $timeToNext = explode(':', explode(' ', $wtNext['to'])[1]);
                            $toHHNext = $timeToNext[0];
                            $toMMNext = $timeToNext[1];
                            $toSSNext = $timeToNext[2];
                        } else {
                            $toHHNext = '00';
                            $toMMNext = '00';
                            $toSSNext = '00';
                        }

                        if (!$wtNext['from']) {
                            $toHH = $toHHNext;
                            $toMM = $toMMNext;
                            $toSS = $toSSNext;

                            $skipNextForContinuedTimetable = true;
                        }

                        if ($wtNext['from']) {
                            $timeFromNext = explode(':', explode(' ', $wtNext['from'])[1]);
                            $fromHHNext = $timeFromNext[0];
                            $fromMMNext = $timeFromNext[1];
                            $fromSSNext = $timeFromNext[2];
                            if ($fromHHNext == '00' && $fromMMNext == '00') {
                                $skipNextForContinuedTimetable = true;
                                $toHH = $toHHNext;
                                $toMM = $toMMNext;
                                $toSS = $toSSNext;
                            }
                        }
                    }
                }
            }

            $dayStr = '';

            switch ($workingTime['day']) {
                case 1:
                    $dayStr = 'MONDAY';
                    break;
                case 2:
                    $dayStr = 'TUESDAY';
                    break;
                case 3:
                    $dayStr = 'WEDNESDAY';
                    break;
                case 4:
                    $dayStr = 'THURSDAY';
                    break;
                case 5:
                    $dayStr = 'FRIDAY';
                    break;
                case 6:
                    $dayStr = 'SATURDAY';
                    break;
                case 7:
                    $dayStr = 'SUNDAY';
                    break;

                default:
                    $dayStr = 'DAY_OF_WEEK_UNSPECIFIED';
                    break;
            }

            $hoursArray[] = [
                'openDay'   => $dayStr,
                'closeDay'  => $dayStr,
                'openTime'  => $fromHH . ':' . $fromMM,
                'closeTime' => $toHH . ':' . $toMM
            ];

            $wtIndex++;
        }

        if (count($hoursArray)) {
            $hours->setPeriods($hoursArray);
            $location->setRegularHours($hours);
        }

        return $location;
    }

    /**
     * @param $name
     * @param \FiloBlu\Storelocator\Model\Stores $store
     * @return \Google\Service\MyBusinessBusinessInformation\Location|null
     */
    public function updateLocation($name, Stores $store)
    {
        try {
            $api = new MyBusinessBusinessInformation($this->getClient());
            $locationObject = $this->getLocationObj($store);
            return $api->locations->patch($name, $locationObject);
        } catch (Exception $ex) {
            return null;
        }
    }

    /**
     * @return mixed
     */
    public function getAccountName()
    {
        return $this->config->getValue('filoblu_storelocator_section/google_my_business/gmb_account_name');
    }

    /**
     * @param $name
     * @return \Google\Service\MyBusinessBusinessInformation\MybusinessbusinessinformationEmpty|string
     */
    public function deleteLocation($name)
    {
        try {
            $api = new MyBusinessBusinessInformation($this->getClient());
            return $api->locations->delete($name);
        } catch (Exception $ex) {
            return $ex->getMessage();
        }
    }

    /**
     * @param string $name
     * @return \Google\Service\MyBusinessBusinessInformation\Location
     */
    public function getLocation($name)
    {
        try {
            $api = new MyBusinessBusinessInformation($this->getClient());
            return $api->locations->get($name);
        } catch (Exception $ex) {
            return null;
        }
    }

    /**
     * @return array|\Google\Service\MyBusinessAccountManagement\Account[]
     */
    public function getAccountList()
    {
        try {
            $api = new MyBusinessAccountManagement($this->getClient());
            return $api->accounts->listAccounts()->getAccounts();
        } catch (Exception $ex) {
            return [];
        }
    }
}
