<?php

namespace App\Services\Shopify;

use App\Models\User;
use Illuminate\Support\Facades\Log;
use RuntimeException;

/**
 * Base service class for services using Shopify API
 */
abstract class BaseService
{
    protected $shop;
    protected $api;
    protected $metaNamespace;

    public function __construct()
    {
        $shopifyStore = config('shopify-app.store');
        $this->shop = User::where('name', $shopifyStore)->first();
        if (!$this->shop) {
            throw new RuntimeException('Shopify store not found! Perhaps not installed yet? - ' . $shopifyStore);
        }
        $this->api = $this->shop->api();
        $this->metaNamespace = config('shopify-app.metafields_namespace');
    }

    /**
     * Publishes a given resource (Collection, Product any publishable resource) on the online store
     *
     * @param string $publishableGid
     * @return bool
     */
    public function publishToOnlineStore(string $publishableGid)
    {
        $onlineStorePublicationGid = config('shopify-app.online_store_publication_gid');
        if (!$onlineStorePublicationGid) {
            Log::warning('Unable to publish to online store - Publication gid not set in env');
            return false;
        }

        $query = '
            mutation publishablePublish($id: ID!, $input: [PublicationInput!]!) {
                publishablePublish(id: $id, input: $input) {
                    userErrors {
                        field
                        message
                    }
                }
            }
        ';

        $response = $this->api->graph($query, [
            'id' => $publishableGid,
            'input' => [
                'publicationId' => $onlineStorePublicationGid
            ]
        ]);

        if (!$this->_checkResponse($response, 'publishablePublish')) {
            return false;
        }

        return true;
    }

    /**
     * Generic function to check Shopify graphQL responses
     *
     * @param array $response
     * @param string $mutation
     * @param string $errorsField
     * @return bool - Success
     */
    protected function _checkResponse(array $response, string $mutation, string $errorsField = 'userErrors')
    {
        if (($response['status'] !== 200 && $response['status'] !== 201) || !empty($response['errors'])) {
            Log::error('Error with mutation ' . $mutation . ': ' .  serialize($response['body']));
            return false;
        }

        if (count($response['body']['data'][$mutation][$errorsField])) {
            Log::error('Problem with mutation ' . $mutation . ': ' .  json_encode($response['body']['data'][$mutation][$errorsField]));
            return false;
        }

        return true;
    }

    /**
     * Creates a publication in shopify (used to create product lists for catalogs)
     *
     * @param boolean $autoPublish - if the publication should have all product, current and future
     * @return array - publication data
     */
    protected function _createPublication(bool $autoPublish = false)
    {
        $query = '
            mutation publicationCreate($input: PublicationCreateInput!) {
                publicationCreate(input: $input) {
                    publication {
                        id
                    }
                    userErrors {
                        field
                        message
                    }
                }
            }
        ';

        $response = $this->api->graph($query, [
            'input' => [
                'autoPublish' => $autoPublish,
                'defaultState' => $autoPublish ? 'ALL_PRODUCTS' : 'EMPTY'
            ]
        ]);

        if (!$this->_checkResponse($response, 'publicationCreate')) {
            return false;
        }

        return $response['body']['data']['publicationCreate']['publication'];
    }

    /**
     * Updates an existing shopify publication, adding or removing products
     *
     * @param string $publicationGid
     * @param array $productsGidsToAdd
     * @param array $productsGidsToRemove
     * @return bool
     */
    protected function _updatePublication(string $publicationGid, array $productsGidsToAdd = [], array $productsGidsToRemove = [])
    {
        $query = '
            mutation publicationUpdate($id: ID!, $input: PublicationUpdateInput!) {
                publicationUpdate(id: $id, input: $input) {
                    userErrors {
                        field
                        message
                    }
                }
            }
        ';

        $response = $this->api->graph($query, [
            'id' => $publicationGid,
            'input' => [
                'publishablesToAdd' => $productsGidsToAdd,
                'publishablesToRemove' => $productsGidsToRemove
            ]
        ]);

        if (!$this->_checkResponse($response, 'publicationUpdate')) {
            return false;
        }
        return true;
    }

    /**
     * Creates a price list in shopify - used to create discounts associated with catalogs
     *
     * @param string $name
     * @param integer $discount
     * @return array - Price List Data
     */
    protected function _createPriceList(string $name, int $discount)
    {
        $query = '
            mutation priceListCreate($input: PriceListCreateInput!) {
                priceListCreate(input: $input) {
                    priceList {
                        id
                    }
                    userErrors {
                        field
                        message
                    }
                }
            }
        ';

        $response = $this->api->graph($query, [
            // TODO: replace currency for logical currency
            'input' => [
                'currency' => 'EUR',
                'name' => $name,
                'parent' => [
                    'adjustment' => [
                        'type' => 'PERCENTAGE_DECREASE',
                        'value' => $discount
                    ]
                ]
            ]
        ]);

        if (!$this->_checkResponse($response, 'priceListCreate')) {
            return false;
        }

        return $response['body']['data']['priceListCreate']['priceList'];
    }

    /**
     * Adds or removes tags (products, orders, customer...)
     *
     * @param string $resourceGid
     * @param array $tags
     * @param boolean $add
     * @return void
     */
    protected function _addOrRemoveTags(string $resourceGid, array $tags, $add = true)
    {
        $mutation = $add ? 'tagsAdd' : 'tagsRemove';
        $query = '
            mutation ' . $mutation . '($id: ID!, $tags: [String!]!) {
                ' . $mutation . '(id: $id, tags: $tags) {
                    userErrors {
                        field
                        message
                    }
                }
            }
        ';

        $response = $this->api->graph($query, [
            'id' => $resourceGid,
            'tags' => $tags
        ]);

        if (!$this->_checkResponse($response, $mutation)) {
            return false;
        }

        return true;
    }
}
