<?php

namespace Drupal\basket\Plugins\Delivery;

use Drupal\Component\Plugin\Factory\DefaultFactory;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;

/**
 * Provides an Basket Delivery plugin manager.
 *
 * @see \Drupal\basket\Plugins\Delivery\Annotation\BasketDelivery
 * @see \Drupal\basket\Plugins\Delivery\BasketDeliveryInterface
 * @see plugin_api
 */
class BasketDeliveryManager extends DefaultPluginManager{
	/**
   	* Constructs a DeliveryManager object.
   	*
   	* @param \Traversable $namespaces
   	*   An object that implements \Traversable which contains the root paths
   	*   keyed by the corresponding namespace to look for plugin implementations.
   	* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
   	*   Cache backend instance to use.
   	* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   	*   The module handler to invoke the alter hook with.
   	*/
	public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
    	parent::__construct(
      		'Plugin/Basket/Delivery',
      		$namespaces,
      		$module_handler,
      		'Drupal\basket\Plugins\Delivery\BasketDeliveryInterface',
      		'Drupal\basket\Plugins\Delivery\Annotation\BasketDelivery'
    	);
		$this->alterInfo('basket_delivery_info');
		$this->setCacheBackend($cache_backend, 'basket_delivery_info_plugins');
  	}

	public function getInstanceByID(string $id){
        $defs  = $this->getDefinitions();
        if ( !isset($defs[$id]) ){
            return false;
        }
        return $this->getInstance($defs[$id]);
	}
  	public function getInstance(array $options){
        if ( !$this->providerExists($options['provider']) ){
            return false;
        }
        static $cache;
        if ( isset($cache[ $options['id'] ]) ){
            return $cache[ $options['id'] ];
        }

        $cls = $options['class'];
        $instance = new $cls();
        // TODO
        $cache[ $options['id'] ] = $instance;

        return $instance;
    }
    public function extraFieldFormAlter(&$form, &$form_state, $entity){
        if(empty(\Drupal::service('Basket')->getSettings('enabled_services', 'delivery'))) return [];
        // Alter
        \Drupal::moduleHandler()->alter('basket_delivery_preInit', $form, $form_state);
        // ---
        $storage = $form_state->getStorage();
        if(empty($form_state->getValue('basket_delivery'))){
            if(!empty($storage['basket_delivery'])){
                $form_state->setValue('basket_delivery', $storage['basket_delivery']);
            } else if(empty($entity->id())){
                $deliveryDefault = \Drupal::service('Basket')->getSettings('active_services', 'delivery_default');
                if(!empty($deliveryDefault) && is_array($deliveryDefault)){
                    $form_state->setValue(['basket_delivery', 'value'], key($deliveryDefault));
                }
            }
        }
        $storage['basket_delivery'] = $form_state->getValue('basket_delivery');
        $form_state->setStorage($storage);
        // ---
        $access = !empty($entity->BasketDeliveryAccess);
        if(!$access){
            $form_display = \Drupal::entityTypeManager()->getStorage('entity_form_display')->load('node.'.$entity->getType().'.default');
            if($display = $form_display->getComponent('basket_delivery')) $access = TRUE;
        }
        if($access){
            $options = \Drupal::service('Basket')->Term()->getOptions('delivery');
            if(!empty($options)){
                $activeDelivery = \Drupal::service('Basket')->getSettings('active_services', 'delivery');
                foreach ($options as $key => $value) {
                    $access = !empty($activeDelivery[$key]);
                    // Alter
                    if($access && empty($entity->id())){
                        \Drupal::moduleHandler()->alter('basket_delivery_option_access', $access, $key, $form_state);
                    }
                    // ---
                    if(!$access){
                        if(!empty($entity->basket_admin_process)){
                            $options[$key] .= ' ('.\Drupal::service('Basket')->Translate()->t('Not active on site').')';
                        } else {
                            unset($options[$key]);
                        }
                    }
                }
            }
            if(!empty($options)){
                $deliveryWidget = \Drupal::service('Basket')->getSettings('enabled_services', 'delivery_widget');
                $form['basket_delivery'] = [
                    '#prefix'       => '<div id="delivery_ajax_wrap">',
                    '#suffix'       => '</div>',
                    '#tree'         => TRUE,
                    'value'         => [
                        '#type'         => !empty($deliveryWidget) ? $deliveryWidget : 'select',
                        '#title'        => \Drupal::service('Basket')->Translate()->t('Delivery'),
                        '#options'      => $options,
                        '#empty_option' => \Drupal::service('Basket')->Translate()->t('Not specified'),
                        '#ajax'         => [
                            'wrapper'       => 'delivery_ajax_wrap',
                            'callback'      => __CLASS__.'::ajaxReloadDelivery',
                            'progress'      => ['type' => 'fullscreen']
                        ],
                        '#required'     => TRUE,
                        '#default_value'=> $form_state->getValue(['basket_delivery', 'value'])
                    ],
                    'fields'        => [
                        '#type'         => 'container',
                        '#ajax_wrap'    => [
                            'wrapper'       => 'delivery_ajax_wrap',
                            'callback'      => __CLASS__.'::ajaxReloadDelivery',
                            'progress'      => ['type' => 'fullscreen']
                        ],
                    ]
                ];
                $_SESSION['delivery_tid'] = $form_state->getValue(['basket_delivery', 'value']);
            }
            if(!empty($entity->id()) && empty($form_state->getValues())){
                $Order = \Drupal::service('Basket')->Orders(NULL, $entity->id())->load();
                if(!empty($Order->delivery_id)){
                    $form['basket_delivery']['value']['#default_value'] = $Order->delivery_id;
                    $form_state->setValue('basket_delivery', [
                        'value'     => $Order->delivery_id
                    ]);
                }
            }
            if(!empty($deliveryValue = $form_state->getValue(['basket_delivery', 'value']))){
                $deliveryService = $this->loadService($deliveryValue);
                if(!empty($deliveryService)){
                    $form['basket_delivery']['fields']['#parents'] = $deliveryService->basketFieldParents();
                    $deliveryService->basketFormAlter($form['basket_delivery']['fields'], $form_state);
                    if(!empty($form['basket_delivery']['fields']['#validate'])){
                        foreach ($form['basket_delivery']['fields']['#validate'] as $validate) {
                            $form['#validate'][] = $validate;
                        }
                    }
                }
            }
            $form['actions']['submit']['#submit'][] =  __CLASS__.'::basketDeliverySubmit';
        }
    }
    public static function ajaxReloadDelivery($form, $form_state){
        $_SESSION['delivery_tid'] = $form_state->getValue(['basket_delivery', 'value']);
        $response = new AjaxResponse();
        unset($form['basket_delivery']['#prefix'], $form['basket_delivery']['#suffix']);
        $response->addCommand(new HtmlCommand('#delivery_ajax_wrap', $form['basket_delivery']));
        if(!empty($form['basket_payment'])){
            unset($form['basket_payment']['#prefix'], $form['basket_payment']['#suffix']);
            $response->addCommand(new HtmlCommand('#payment_ajax_wrap', $form['basket_payment']));
        }
        // Alter
        \Drupal::moduleHandler()->alter('basket_ajaxReloadDelivery', $response);
        // ---
        return $response;
    }
    public function loadService($deliveryValue){
        if(!empty($serviceId = \Drupal::service('Basket')->getSettings('delivery_services', $deliveryValue))){
            return $this->getInstanceByID($serviceId);
        }
        return FALSE;
    }
    public static function basketDeliverySubmit($form, $form_state){
        $entity = $form_state->getBuildInfo()['callback_object']->getEntity();
        $DeliveryService = \Drupal::service('BasketDelivery');
        if(!empty($entity->id())){
            /*Delete delivery services info all*/
            foreach ($DeliveryService->getDefinitions() as $delivery){
                $DeliveryService->getInstanceByID($delivery['id'])->basketDelete($entity, FALSE);
            }
            // Save delivery service info
            $deliveryValue = $form_state->getValue(['basket_delivery', 'value']);
            if(!empty($deliveryValue)){
                $deliveryService = $DeliveryService->loadService($deliveryValue);
                if(!empty($deliveryService)){
                    $deliveryService->basketSave($entity, $form_state);
                }
            }
            \Drupal::database()->merge('basket_orders_delivery')
                        ->key([
                            'nid'       => $entity->id()
                        ])
                        ->fields([
                            'did'       => !empty($deliveryValue) ? $deliveryValue : NULL,
                            'address'   => !empty($deliveryService) ? serialize($deliveryService->basketGetAddress($entity)) : NULL
                        ])
                        ->execute();
        }
    }
    public function getDeliveryInfo($entity = NULL){
        $info = [
            'sum'           => 0,
            'isPay'         => TRUE,
            'description'   => NULL,
            'entity'        => $entity
        ];
        if(!empty($_SESSION['delivery_tid'])){
            $delivery = \Drupal::service('Basket')->Term()->load($_SESSION['delivery_tid']);
            if(!empty($delivery->delivery_sum)){
                \Drupal::service('Basket')->Currency()->PriceConvert($delivery->delivery_sum, $delivery->delivery_currency);
                $info['sum'] = $delivery->delivery_sum;
            }
            $deliveryService = $this->loadService($_SESSION['delivery_tid']);
            if(!empty($deliveryService)){
                $deliveryService->deliverySumAlter($info);
            }
        }
        return $info;
    }
}