<?php

namespace Drupal\basket\Admin\Form\Order;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Url;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\ReplaceCommand;

class ItemsForm extends FormBase{

	protected static $Basket;
    protected $Order;
    protected static $currencyName;
    protected static $itemAjax;

	public function __construct($Order){
		self::$Basket = \Drupal::service('Basket');
        $this->Order = $Order;
        if(!empty($this->Order->currency)){
            if(is_object($this->Order->currency)){
                $currency = $this->Order->currency;
            } else {
                $currency = self::$Basket->Currency()->load($this->Order->currency);
            }
            if(!empty($currency->name)){
                self::$currencyName = self::$Basket->Translate()->t($currency->name);
            }
        }
        self::$itemAjax = [
            'wrapper'       => 'basket_order_items_form_ajax_wrap',
            'callback'      => '::ajaxReload',
            'url'           => new Url('basket.admin.pages', ['page_type' => 'api-orders-basket_order_items_form']),
            'options'       => [
                'query'         => \Drupal::request()->query->All() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]
            ]
        ];
	}
	public function getFormId(){
    	return 'basket_order_items_form';
  	}
  	public function buildForm(array $form, FormStateInterface $form_state){
        $storage = $form_state->getStorage();
        if(empty($storage['Order'])){
            $storage['Order'] = $this->Order;
            $form_state->setStorage($storage);
        }
        $FormOrder = $storage['Order'];
        $form += [
            '#prefix'       => '<div id="basket_order_items_form_ajax_wrap">',
            '#suffix'       => '</div>',
            'orderId'       => [
                '#type'         => 'hidden',
                '#value'        => $FormOrder->id
            ],
            'items'         => [
                '#tree'         => TRUE,
                '#type'         => 'table',
                '#header'       => [
                    [
                        'data'      => self::$Basket->Translate()->t('Product'),
                        'colspan'   => 2
                    ],[
                        'data'      => self::$Basket->Translate()->t('Extra options'),
                    ],[
                        'data'      => self::$Basket->Translate()->t('Price'),
                    ],[
                        'data'      => self::$Basket->Translate()->t('Quantity'),
                    ],[
                        'data'      => [
                            '#type'                     => 'inline_template',
                            '#template'                 => '{{label}} <span class="info-help">{{ ico|raw }}</span><span class="info-help-content">{{text|raw}}</span>',
                            '#context'                  => [
                                'label'                     => self::$Basket->Translate()->t('Discount'),
                                'ico'                       => self::$Basket->getIco('help.svg'),
                                'text'                      => self::$Basket->Translate()->t('Individual product discount.')
                            ]
                        ],
                    ],[
                        'data'      => self::$Basket->Translate()->t('Sum'),
                    ],[
                    ]
                ]
            ],
        ];
        $totalSum = 0;
        $totalSumItems = 0;
        $totalCount = 0;
        if(!empty($FormOrder->items)){
            $nids = [];
            foreach ($FormOrder->items as $item){
                if(empty($item->nid)) continue;
                $nids[$item->nid] = $item->nid;
                // Save temp
                self::$Basket->addBasketItemTemp($item, $this->Order->id);
            }
            $itemNodes = !empty($nids) ? \Drupal\node\Entity\Node::loadMultiple($nids) : [];
            foreach ($FormOrder->items as $item){
                // Is delete
                if(!empty($item->isDelete)) continue;
                // ---
                $rowPrice = $this->getItemInfo('price', $item->id, $form_state);
                $rowCount = $this->getItemInfo('count', $item->id, $form_state);
                $rowDiscount = $this->getItemInfo('discount', $item->id, $form_state);
                $rowSum = $rowPrice*$rowCount-($rowPrice*$rowCount/100*$rowDiscount);

                $attr = [
                    'min'       => 1,
                    'step'      => 1,
                    'max'       => 999,
                    'scale'     => 0,
                    'itemEdit'  => $item
                ];
                \Drupal::moduleHandler()->alter('basket_count_input_attr', $attr, $item->nid, $item->params);
                if ( !isset($attr['scale']) ){
                    $attr['scale'] = 0;
                }
                if(isset($attr['itemEdit'])) {
                    unset($attr['itemEdit']);
                }

                $form['items'][$item->id] = [
                    [
                        '#type'                 => 'inline_template',
                        '#template'             => '{% if uri %}{{ basket_image(uri, \'thumbnail\') }}{% endif %}',
                        '#context'              => [
                            'uri'                   => !empty($item->node_fields['img_uri']) ? $item->node_fields['img_uri'] : NULL
                        ],
                        '#wrapper_attributes'   => ['style' => 'width:100px;']
                    ],[
                        '#type'                 => 'inline_template',
                        '#template'             => '<b>{% if node %}<a href="{{ url(\'entity.node.canonical\', {\'node\': node.id}) }}" target="_blank">{{ title }}</a>{% else %}{{ title }}{% endif %}</b>',
                        '#context'              => [
                            'title'                 => !empty($item->node_fields['title']) ? $item->node_fields['title'] : NULL,
                            'node'                  => !empty($itemNodes[$item->nid]) ? $itemNodes[$item->nid] : NULL
                        ]
                    ],
                    'params'                => $this->itemParams([
                        'item'                  => $item,
                        'node'                  => !empty($itemNodes[$item->nid]) ? $itemNodes[$item->nid] : NULL,
                    ]),
                    'price'                 => [
                        '#type'                 => 'number',
                        '#default_value'        => $rowPrice,
                        '#field_suffix'         => self::$currencyName,
                        '#step'                 => 0.01,
                        '#min'                  => 0,
                        '#wrapper_attributes'   => ['class' => ['auto_width']],
                        '#ajax'                 => self::$itemAjax+['event' => 'change']
                    ],
                    'count'                 => [
                        '#type'                 => 'number',
                        '#default_value'        => number_format($rowCount, $attr['scale'], '.', ''),
                        '#min'                  => 0,
                        '#step'                 => $attr['step'] ?? 1,
                        '#max'                  => $attr['max'] ?? 999,
                        '#wrapper_attributes'   => ['class' => ['auto_width count_td']],
                        '#ajax'                 => self::$itemAjax+['event' => 'change']
                    ],
                    'discount'              => [
                        '#type'                 => 'select',
                        '#field_suffix'         => '%',
                        '#options'              => array_combine(range(0, 100), range(0, 100)),
                        '#wrapper_attributes'   => ['class' => ['auto_width']],
                        '#ajax'                 => self::$itemAjax+['event' => 'change'],
                        '#default_value'        => $rowDiscount,
                    ],
                    'sum'                   => [
                        '#type'                 => 'item',
                        '#value'                => $rowPrice*$rowCount,
                        [
                            '#type'                 => 'inline_template',
                            '#template'             => '<b>{{ row_sum|number_format(2, \',\', \' \') }} {{ currency }}</b>',
                            '#context'              => [
                                'currency'              => self::$currencyName,
                                'row_sum'               => $rowSum
                            ]
                        ]
                    ],
                    self::itemDelete($item->id, $form_state)
                ];
                $totalSum += $rowSum;
                $totalSumItems += $rowSum;
                $totalCount += $rowCount;
            }
        }
        $form['items']['add'] = [
            [
                '#type'                 => 'inline_template',
                '#template'             => '<a href="javascript:void(0);" class="form-submit button--add" onclick="{{ onclick }}" data-post="{{ post }}">+ {{ text }}</a>',
                '#context'              => [
                    'text'                  => self::$Basket->Translate()->t('Add a product'),
                    'onclick'               => 'basket_admin_ajax_link(this, \''.Url::fromRoute('basket.admin.pages', ['page_type' => 'api-orders-add_goods_popup'])->toString().'\')',
                    'post'                  => json_encode([
                        'orderId'               => $FormOrder->id
                    ])
                ],
                '#wrapper_attributes'   => [
                    'colspan'               => 6,
                    'class'                 => ['not_hover']
                ]
            ],[
                '#type'                 => 'textarea',
                '#parents'              => ['addItem'],
                '#wrapper_attributes'   => [
                    'class'                 => ['not_hover']
                ],
                '#attributes'           => ['style' => 'display:none;'],
                '#ajax'                 => self::$itemAjax+['event' => 'change'],
                '#validate'             => [
                    '\\'.__CLASS__.'::itemAddValid'
                ],
            ]
        ];
        $totalSum += $this->getTotalInfo('add_price', $form_state);
        $totalSum += $this->getTotalInfo('delivery_price', $form_state);
        $form['total'] = [
            '#type'         => 'table',
            '#header'       => [
                self::$Basket->Translate()->t('Order price'),
                self::$Basket->Translate()->t('Shipping cost'),
                self::$Basket->Translate()->t('Amount adjustment').' (+/-)',
                self::$Basket->Translate()->t('Items in order'),
                self::$Basket->Translate()->t('To pay')
            ],
            [
                [
                    '#type'                     => 'item',
                    '#value'                    => $totalSumItems,
                    '#parents'                  => ['total', 'totalSum'],
                    [
                        '#type'                 => 'inline_template',
                        '#template'             => '<b>{{ totalSum|number_format(2, \',\', \' \') }} {{ currency }}</b>',
                        '#context'              => [
                            'currency'              => self::$currencyName,
                            'totalSum'              => $totalSumItems
                        ]
                    ],
                    '#wrapper_attributes'   => ['class' => ['not_hover']],
                ],[
                    '#type'                     => 'number',
                    '#parents'                  => ['total', 'delivery_price'],
                    '#wrapper_attributes'       => ['class' => ['auto_width', 'not_hover']],
                    '#ajax'                     => self::$itemAjax+['event' => 'change'],
                    '#default_value'            => $this->getTotalInfo('delivery_price', $form_state),
                    '#field_suffix'             => self::$currencyName
                ],[
                    '#type'                     => 'number',
                    '#parents'                  => ['total', 'add_price'],
                    '#wrapper_attributes'       => ['class' => ['auto_width', 'not_hover']],
                    '#ajax'                     => self::$itemAjax+['event' => 'change'],
                    '#default_value'            => $this->getTotalInfo('add_price', $form_state),
                    '#field_suffix'             => [
                        '#type'                     => 'inline_template',
                        '#template'                 => '{{currency}} <span class="info-help">{{ ico|raw }}</span><span class="info-help-content">{{text|raw}}</span>',
                        '#context'                  => [
                            'currency'                  => self::$currencyName,
                            'ico'                       => self::$Basket->getIco('help.svg'),
                            'text'                      => self::$Basket->Translate()->t('Adjusting the cost of the order up or down.')
                        ]
                    ]
                ],[
                    '#type'                     => 'item',
                    '#value'                    => $totalCount,
                    '#parents'                  => ['total', 'totalCount'],
                    [
                        '#type'                 => 'inline_template',
                        '#template'             => '<b>{{ totalCount }}</b>',
                        '#context'              => [
                            'totalCount'            => $totalCount
                        ]
                    ],
                    '#wrapper_attributes'   => ['class' => ['not_hover']],
                ],[
                    '#type'                     => 'item',
                    '#value'                    => $totalSum,
                    '#parents'                  => ['total', 'paySum'],
                    [
                        '#type'                 => 'inline_template',
                        '#template'             => '<b>{{ totalSum|number_format(2, \',\', \' \') }} {{ currency }}</b>',
                        '#context'              => [
                            'currency'              => self::$currencyName,
                            'totalSum'              => $totalSum
                        ]
                    ],
                    '#wrapper_attributes'   => ['class' => ['not_hover']],
                ]
            ]
        ];
        $form['actions'] = [
            '#type'         => 'actions',
            'submit'        => [
                '#type'         => 'submit',
                '#name'         => 'saveItems',
                '#id'           => 'saveItems',
                '#value'        => self::$Basket->Translate()->t('Save'),
                '#ajax'         => self::$itemAjax,
            ]
        ];
        return $form;
  	}
  	public function ajaxReload(array &$form, FormStateInterface $form_state){
        $triggerElement = $form_state->getTriggeringElement();
        if(!empty($triggerElement['#name'])){
            switch($triggerElement['#name']){
                case'saveItems':
                    $response = new AjaxResponse();
                    // Message
                    \Drupal::messenger()->deleteAll();
                    $response->addCommand(new InvokeCommand(NULL, 'NotyGenerate', ['status', self::$Basket->Translate()->t('Settings saved.')]));
                    // Replace info block
                    $Order = new \Drupal\basket\Admin\Page\Order($this->Order->id);
                    $response->addCommand(new ReplaceCommand('#order_info_block', $Order->getOrderInfoBlock()));
                    $response->addCommand(new ReplaceCommand('#basket_order_items_form_ajax_wrap', $form));
                    return $response;
                break;
            }
        }
        return $form;
    }
    public function submitForm(array &$form, FormStateInterface $form_state){
        $triggerElement = $form_state->getTriggeringElement();
        if(!empty($triggerElement['#name'])){
            switch($triggerElement['#name']){
                case'saveItems':
                    $storage = $form_state->getStorage();
                    $FormOrder = $storage['Order'];
                    $values = $form_state->getValues();
                    // set goods
                    $FormOrder->goods = $values['total']['totalCount'];
                    // set price
                    $FormOrder->price = $values['total']['paySum'];
                    // set add_price
                    $FormOrder->add_price = $values['total']['add_price'];
                    // set delivery_price
                    $FormOrder->delivery_price = $values['total']['delivery_price'];
                    // items
                    if(!empty($FormOrder->items)){
                        foreach ($FormOrder->items as &$item){
                            $item->oldItem = clone $item;
                            // set price
                            $item->price = self::getItemInfo('price', $item->id, $form_state);
                            // set count
                            $item->count = self::getItemInfo('count', $item->id, $form_state);
                            // set discount
                            $item->discount['percent'] = self::getItemInfo('discount', $item->id, $form_state);
                        }
                    }
                    // Replace and Save Order
                    $Order = self::$Basket->Orders($FormOrder->id);
                    $Order->replaceOrder($FormOrder);
                    $FormOrderNew = $Order->save();
                    // Clear storage
                    if(!empty($storage['itemsDelete'])) unset($storage['itemsDelete']);
                    if(!empty($storage['itemsAdd']))    unset($storage['itemsAdd']);
                    $storage['Order'] = $FormOrderNew;
                    $form_state->setStorage($storage);
                    $form_state->setRebuild();
                break;
            }
        }
    }
    private function getItemInfo($fieldName, $key, $form_state){
        $items = $form_state->getValue('items');
        $storage = $form_state->getStorage();
        switch($fieldName){
            case'discount':
                $discount = isset($items[$key][$fieldName]) ? $items[$key][$fieldName] : (!empty($storage['Order']->items[$key]->{$fieldName}['percent']) ? $storage['Order']->items[$key]->{$fieldName}['percent'] : 0);
                return $discount > 100 ? 100 : $discount;
            break;
            default:
                return isset($items[$key][$fieldName]) ? $items[$key][$fieldName] : $storage['Order']->items[$key]->{$fieldName};
            break;
        }
    }
    private function getTotalInfo($fieldName, $form_state){
        $storage = $form_state->getStorage();
        $total = $form_state->getValue('total');
        switch($fieldName){
            default:
                return isset($total[$fieldName]) ? $total[$fieldName] : (!empty($storage['Order']->{$fieldName}) ? $storage['Order']->{$fieldName} : 0);
            break;
        }
    }
    private static function itemDelete($itemId, $form_state){
        $triggerdElement = $form_state->getTriggeringElement();
        if(isset($triggerdElement['#isDelete']) && $triggerdElement['#isDelete'] === $itemId){
            $element = [
                'yes'           => [
                    '#type'         => 'button',
                    '#value'        => self::$Basket->Translate()->t('Delete'),
                    '#name'         => 'delete_yes_item_'.$itemId,
                    '#ajax'         => self::$itemAjax,
                    '#attributes'   => [
                        'class'         => ['button--delete'],
                        'title'         => self::$Basket->Translate()->t('Delete')
                    ],
                    '#validate'     => [
                        '\\'.__CLASS__.'::itemDeleteValid'
                    ],
                    '#itemId'       => $itemId
                ],
                'cancel'        => [
                    '#type'         => 'button',
                    '#value'        => self::$Basket->Translate()->t('Cancel'),
                    '#name'         => 'delete_cancel_item_'.$itemId,
                    '#ajax'         => self::$itemAjax,
                    '#attributes'   => [
                        'class'         => ['button--cancel'],
                        'title'         => self::$Basket->Translate()->t('Cancel')
                    ],
                ],
                '#wrapper_attributes'=> ['class' => ['items_td_delete']]
            ];
        } else {
            $element = [
                '#type'         => 'button',
                '#value'        => self::$Basket->Translate()->t('Delete'),
                '#name'         => 'delete_item_'.$itemId,
                '#ajax'         => self::$itemAjax,
                '#isDelete'     => $itemId,
                '#attributes'   => [
                    'title'         => self::$Basket->Translate()->t('Delete')
                ],
                '#wrapper_attributes'=> ['class' => ['items_td_delete']]
            ];
        }
        return $element;
    }
    public static function itemDeleteValid(array &$form, FormStateInterface $form_state){
        $triggerdElement = $form_state->getTriggeringElement();
        $storage = $form_state->getStorage();
        if(!empty($storage['Order']->items[$triggerdElement['#itemId']])){
            $storage['Order']->items[$triggerdElement['#itemId']]->isDelete = TRUE;
        }
        $form_state->setStorage($storage);
    }
    public static function itemAddValid(array &$form, FormStateInterface $form_state){
        $triggerdElement = $form_state->getTriggeringElement();
        if(!empty($triggerdElement['#name']) && $triggerdElement['#name'] == 'addItem'){
            $storage = $form_state->getStorage();
            $addItemInfo = @json_decode($form_state->getValue('addItem'), TRUE);
            if(!empty($addItemInfo['nid'])){
                $addNode = \Drupal\node\Entity\Node::load($addItemInfo['nid']);
                $Cart = self::$Basket->Cart();
                $addItem = (object)[
                    'isNew'         => TRUE,
                    'id'            => time().rand(0, 9999),
                    'nid'           => $addItemInfo['nid'],
                    'order_nid'     => $storage['Order']->nid,
                    'price'         => 0,
                    'count'         => 1,
                    'fid'           => 0,
                    'params'        => $Cart->encodeParams(!empty($addItemInfo['params']) ? $addItemInfo['params'] : []),
                    'add_time'      => time(),
                    'node_fields'   => [
                        'title'         => $addNode->getTitle(),
                        'img_uri'       => ''
                    ],
                    'discount'      => [
                        'percent'       => 0
                    ]
                ];
                $addItem->price = $Cart->getItemPrice($addItem);
                $addItem->fid = $Cart->getItemImg($addItem);
                if(!empty($addItem->fid)){
                    $File = \Drupal\file\Entity\File::load($addItem->fid);
                    $addItem->node_fields['img_uri'] = $File->getFileUri();
                    $addItem->setUriByFid = $addItem->fid;
                }
                $storage['Order']->items[$addItem->id] = $addItem;
                ksort($storage['Order']->items);
                // Save temp
                self::$Basket->addBasketItemTemp($addItem, $storage['Order']->id);
            }
            $form_state->setStorage($storage);
        }
    }
    public function itemParams($info){
        $element = [
            'inline'        => !empty($info['item']->params_html['inline']) ? $info['item']->params_html['inline'] : []
        ];
        if(!empty($info['node'])){
            $element['edit'] = [
                '#type'         => 'textarea',
                '#default_value'=> json_encode($info['item']->params),
                '#ajax'         => self::$itemAjax+['event' => 'change'],
                '#validate'             => ['\\'.__CLASS__.'::itemChangeParamsValid'],
                '#parents'      => ['items', $info['item']->id, 'editParams'],
                '#attributes'   => ['style' => 'display:none;']
            ];
            $element['edit_link'] = [
                '#type'         => 'inline_template',
                '#template'     => '<a href="javascript:void(0);" class="button--edit_link" onclick="{{ onclick }}" data-post="{{ post }}">{{ ico|raw }}</a>',
                '#context'      => [
                    'ico'           => self::$Basket->getIco('settings_row.svg', 'base'),
                    'onclick'       => 'basket_admin_ajax_link(this, \''.Url::fromRoute('basket.admin.pages', ['page_type' => 'api-orders-edit_params'])->toString().'\')',
                    'post'          => json_encode([
                        'basketItemId'  => $info['item']->id,
                        'params'        => $info['item']->params,
                        'orderId'       => $this->Order->id
                    ])
                ]
            ];
        }
        return $element;
    }
    public static function itemChangeParamsValid(array &$form, FormStateInterface $form_state){
        $triggerElement = $form_state->getTriggeringElement();
        if(!empty($triggerElement['#name']) && strpos($triggerElement['#name'], 'editParams') !== FALSE){
            $orderID = $triggerElement['#parents'][1];
            $storage = $form_state->getStorage();
            if(!empty($storage['Order']->items[$orderID])){
                // ---
                $newParams = @json_decode(trim($form_state->getValue($triggerElement['#parents'])), TRUE);
                if(!empty($storage['Order']->items[$orderID]->params) && !is_array($storage['Order']->items[$orderID]->params)){
                    $storage['Order']->items[$orderID]->params = [];
                }
                $storage['Order']->items[$orderID]->params = \Drupal::service('Basket')->arrayMergeRecursive(
                    $storage['Order']->items[$orderID]->params,
                    $newParams
                );
                $storage['Order']->items[$orderID]->params_html = [
                    'full'              => \Drupal::service('BasketParams')->getDefinitionParams($storage['Order']->items[$orderID]->params, $storage['Order']->items[$orderID]->nid),
                    'inline'              => \Drupal::service('BasketParams')->getDefinitionParams($storage['Order']->items[$orderID]->params, $storage['Order']->items[$orderID]->nid, TRUE)
                ];
                // ---
                $form_state->setStorage($storage);
            }
        }
    }
}
