<?php

namespace Drupal\abinbev_gmap\Form;

use Drupal\abinbev_gmap\PlaceInfoService;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Configure example settings for this site.
 */
class ImportPocsForm extends ConfigFormBase {

  /**
   * Batch Builder.
   *
   * @var \Drupal\Core\Batch\BatchBuilder
   */
  protected $batchBuilder;

  protected $placeInfoService;

  protected $cache;

  /**
   * CustomService constructor.
   */
  public function __construct(PlaceInfoService $placeInfoService, CacheBackendInterface $cache) {
    $this->batchBuilder = new BatchBuilder();
    $this->placeInfoService = $placeInfoService;
    $this->cache = $cache;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('place_info_service'),
      $container->get('cache.discovery'),
    );
  }

  /**
   * Config settings.
   *
   * @var string
   */
  const SETTINGS = 'abinbev_gmap.import_locations';

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'abinbev_gmap_import_na_pocs';
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return [
      static::SETTINGS,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config(static::SETTINGS);

    $apiConfig = \Drupal::config('abinbev_gmap.settings');
    $api_key = $apiConfig->get('google_api_key');

    if (empty($api_key)) {
      \Drupal::messenger()->addWarning(t('No Google Api key provided'));
    }

    //    \Drupal::service('cache_tags.invalidator')
    //      ->invalidateTags(['all_map_info']);

    //    $locs = \Drupal::service('place_info_service')
    //      ->getAllPlaces();
    //
    //    $cid = 'all_map_info';
    //
    //    /** @var \Drupal\Core\Cache\Cache $cache */
    //    $cache = $this->cache->get($cid);
    //
    //    kint($cache->data);

    $form['events'] = [
      '#type' => 'details',
      '#title' => $this->t('Import Locations'),
      '#open' => TRUE,
      '#weight' => 1,
    ];

    $form['events']['locations_file'] = [
      '#type' => 'managed_file',
      '#upload_location' => 'public://abinbev_gmap/',
      '#multiple' => FALSE,
      '#description' => t('Allowed extensions: csv'),
      '#upload_validators' => [
        'FileExtension' => ['extensions' => 'csv'],
        //'file_validate_size' => [25600000],
      ],
    ];

    $form['events']['reimport_all'] = [
      '#type' => 'checkbox',
      '#title' => t('Re-import All Locations'),
      '#description' => t('Check this if you want to re-import all locations'),
      '#default_value' => $config->get('reimport_all'),
    ];
//    $preferred_country = \Drupal::config('abinbev_gmap.import_locations')
//      ->get('preferred_country');
//    $region = '';
//    if (!empty($preferred_country)) {
//      $region = '&region=' . $preferred_country;
//    }
//
//    $address = 'Ma Boyles Ale House & Eatery, Merseyside,7 Tower Gardens, L3 1LG';
//    $url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?query=' . $address . $region . '&key=' . $api_key;

    $form['events']['preferred_country'] = [
      '#type' => 'select',
      '#title' => t('Preferred country'),
      '#options' => [
        'EN' => 'EN',
        'FR' => 'FR',
        'DE' => "DE",
        'IT' => "IT",
        'ES' => 'ES',
        'PT' => "PT",
        'NL' => 'NL',
        'PL' => 'PL',
        'CZ' => 'CZ',
        'BE' => 'BE',
        'LU' => 'LU',
        'CH' => 'CH',
        'AT' => 'AT',
        'IE' => 'IE',
      ],
      '#description' => t('Select preferred country for search geolocation in selected country.'),
      '#default_value' => $config->get('preferred_country'),
    ];

    $form['events']['import_data'] = [
      '#type' => 'submit',
      '#value' => t('Import Data'),
      '#submit' => ['::importPocs'],
      '#validate' => ["::importPocsValidate"],
    ];

    $form['icons'] = [
      '#type' => 'details',
      '#title' => $this->t('Map and List Settings'),
      '#open' => TRUE,
      '#weight' => 3,
    ];
    $form['icons']['render_list'] = [
      '#type' => 'checkbox',
      '#title' => t('Render List of locations'),
      '#description' => t('Render List of locations'),
      '#default_value' => $config->get('render_list'),
    ];
    $form['icons']['map_preloader'] = [
      '#type' => 'checkbox',
      '#title' => t('Use Map Preloader'),
      '#description' => t('Use placeholder for map and user should click Show map button to show map'),
      '#default_value' => $config->get('map_preloader'),
    ];
    $form['icons']['show_list_categories'] = [
      '#type' => 'checkbox',
      '#title' => t('Show categories in the list.'),
      '#description' => t('Show category near title'),
      '#default_value' => $config->get('show_list_categories'),
    ];
    $form['icons']['show_list_directions'] = [
      '#type' => 'checkbox',
      '#title' => t('Show directions buttons.'),
      '#description' => t('Show directions buttons.'),
      '#default_value' => $config->get('show_list_directions'),
    ];
    $form['icons']['show_list_share'] = [
      '#type' => 'checkbox',
      '#title' => t('Show Share button.'),
      '#description' => t('Show share button in the list.'),
      '#default_value' => $config->get('show_list_share'),
    ];

    $form['icons']['use_select_poc'] = [
      '#type' => 'checkbox',
      '#title' => t('Use select POC link'),
      '#description' => t('Show button to select POC for redirect with pocId as GET param.'),
      '#default_value' => $config->get('use_select_poc'),
    ];
    $form['icons']['use_select_poc_link'] = [
      '#type' => 'textfield',
      '#title' => t('Redirect link for select POC'),
      '#description' => t('Link to redirect with pocId as GET param.'),
      '#default_value' => $config->get('use_select_poc_link'),
      '#states' => [
        'visible' => [
          ':input[name="use_select_poc"]' => ['checked' => TRUE],
        ],
        'required' => [
          ':input[name="use_select_poc"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['icons']['show_list_counter'] = [
      '#type' => 'checkbox',
      '#title' => t('Show List Counter.'),
      '#description' => t('Show total counter of locations in the list.'),
      '#default_value' => $config->get('show_list_counter'),
    ];
    $form['icons']['use_clusters'] = [
      '#type' => 'checkbox',
      '#title' => t('Group markers to clusters on map.'),
      '#description' => t('Group markers to clusters on map.'),
      '#default_value' => $config->get('use_clusters'),
    ];
    $form['icons']['use_filters'] = [
      '#type' => 'checkbox',
      '#title' => t('Enable seatch by categories'),
      '#description' => t('Select this if you use filters by categories for search'),
      '#default_value' => $config->get('use_filters'),
    ];

    $form['icons']['use_numbers'] = [
      '#type' => 'checkbox',
      '#title' => t('Enable numbers in list for items'),
      '#description' => t('Select this if you use numbers for each item. 1,2,3 ....'),
      '#default_value' => $config->get('use_numbers'),
    ];

    $form['icons']['use_categories'] = [
      '#type' => 'checkbox',
      '#title' => t('Use categories for different icons on map.'),
      '#description' => t('Select this if you use icons for different categories'),
      '#default_value' => $config->get('use_categories'),
    ];

    $form['icons']['use_datalayer'] = [
      '#type' => 'checkbox',
      '#title' => t('Use Datalayer for storage of actions.'),
      '#description' => t('Select this if you use Datalayer. It required GTM to be installed to site'),
      '#default_value' => $config->get('use_datalayer'),
    ];

    $form['icons']['use_mobile_view'] = [
      '#type' => 'checkbox',
      '#title' => t('Use Mobile View Only'),
      '#description' => t('Use mobile view only. Map is 100%. List can be switched by button'),
      '#default_value' => $config->get('use_mobile_view'),
    ];

    $form['icons']['di'] = [
      '#type' => 'container',
      '#states' => [
        'invisible' => [
          ':input[name="use_categories"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['icons']['di']['default_icon'] = [
      '#type' => 'managed_file',
      '#upload_location' => 'public://abinbev_gmap/icons/',
      '#multiple' => FALSE,
      '#description' => t('Allowed extensions: png'),
      '#upload_validators' => [
        'FileExtension' => ['extensions' => 'png'],
      ],
      '#default_value' => $config->get('default_icon'),
      '#title' => t('Upload an image file for map default pin icon'),
      '#states' => [
        'invisible' => [
          ':input[name="use_categories"]' => ['checked' => TRUE],
        ],
      ],
    ];
    for ($i = 0; $i < 5; $i++) {
      $form['icons']['icon_' . $i] = [
        '#type' => 'details',
        '#title' => $this->t('Category Details'),
        '#open' => TRUE,
        '#states' => [
          'visible' => [
            ':input[name="use_categories"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['icons']['icon_' . $i]['icon_' . $i . '_title'] = [
        '#type' => 'textfield',
        '#title' => t('Category Name'),
        '#description' => t('Icon title'),
        '#default_value' => $config->get('icon_' . $i . '_title'),
      ];
      $form['icons']['icon_' . $i]['icon_' . $i . '_file'] = [
        '#type' => 'managed_file',
        '#upload_location' => 'public://abinbev_gmap/icons/',
        '#multiple' => FALSE,
        '#description' => t('Allowed extensions: png'),
        '#upload_validators' => [
          'FileIsImage' => [],
          'FileExtension' => ['extensions' => 'png'],
          'FileSizeLimit' => ['fileLimit' => 25600000],
        ],
        '#default_value' => $config->get('icon_' . $i . '_file'),
        '#title' => t('Upload an image file for map pin icon'),
      ];
    }

    $form['imported'] = [
      '#type' => 'details',
      '#title' => $this->t('Imported data'),
      '#open' => TRUE,
      '#weight' => 2,
    ];

    $locations = \Drupal::service('place_info_service')->getAllPlaces();

    $imported_count = is_array($locations) ? count($locations) : '0';
    $form['imported']['data'] = [
      '#theme' => 'item_list',
      '#list_type' => 'ol',
      '#title' => 'Locations',
      '#items' => [],
      '#attributes' => ['class' => 'location-list'],
      '#wrapper_attributes' => ['class' => 'container'],
      '#prefix' => '<div class="total"><h2>Published Total Count : ' . $imported_count . '</h2></div>',
    ];

    $form['#cache'] = ['max-age' => 0];
    return parent::buildForm($form, $form_state);
  }

  public function validateForm(array &$form, FormStateInterface $form_state) {
    // Retrieve the configuration.
    $values = $form_state->getValues();
    $has_icon_settings = FALSE;
    for ($i = 0; $i < 5; $i++) {
      $file = $values['icon_' . $i . '_file'];

      $title = $values['icon_' . $i . '_title'];
      if (!empty($file) || !empty($title)) {
        $has_icon_settings = TRUE;
        break;
      }
    }

    if (!$has_icon_settings && !$form_state->getValue('locations_file')) {
      $form_state->setErrorByName('locations_file', 'Please upload file for import.');
    }

    parent::submitForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Retrieve the configuration.
    $values = $form_state->getValues();
    $config = $this->config(static::SETTINGS);
    for ($i = 0; $i < 5; $i++) {
      if (!empty($values['icon_' . $i . '_title'])) {
        $config->set('icon_' . $i . '_title', $values['icon_' . $i . '_title']);
      }
      if (!empty($values['icon_' . $i . '_file'])) {
        $config->set('icon_' . $i . '_file', $values['icon_' . $i . '_file']);
        $file = \Drupal::entityTypeManager()
          ->getStorage('file')
          ->load($values['icon_' . $i . '_file'][0]);
        /* Set the status flag permanent of the file object */
        $file->setPermanent();
        /* Save the file in database */
        $file->save();
      }
    }
    if (!empty($values['default_icon'])) {
      $config->set('default_icon', $values['default_icon']);

      $file = \Drupal::entityTypeManager()
        ->getStorage('file')
        ->load($values['default_icon'][0]);
      $file->setPermanent();
      $file->save();
    }

    $config->set('preferred_country', $values['preferred_country']);
    $config->set('use_categories', $values['use_categories']);
    $config->set('show_list_categories', $values['show_list_categories']);
    $config->set('show_list_directions', $values['show_list_directions']);
    $config->set('show_list_share', $values['show_list_share']);
    $config->set('show_list_counter', $values['show_list_counter']);
    $config->set('use_filters', $values['use_filters']);
    $config->set('use_clusters', $values['use_clusters']);
    $config->set('render_list', $values['render_list']);
    $config->set('map_preloader', $values['map_preloader']);
    $config->set('use_numbers', $values['use_numbers']);
    $config->set('use_datalayer', $values['use_datalayer']);
    $config->set('use_select_poc', $values['use_select_poc']);
    $config->set('use_select_poc_link', $values['use_select_poc_link']);
    $config->set('use_mobile_view', $values['use_mobile_view']);
    $config->save();

    parent::submitForm($form, $form_state);
  }

  /** Validation for correct file format.
   *
   * @param $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *
   * @return void
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function importPocsValidate(&$form, FormStateInterface $form_state) {
    $fileId = $form_state->getValue('locations_file');

    /** @var \Drupal\file\Entity\File $file */
    $file = \Drupal::entityTypeManager()->getStorage('file')->load($fileId[0]);
    $file_path = \Drupal::service('file_system')->realpath($file->getFileUri());

    if (($handle = fopen($file_path, "r")) !== FALSE) {
      while (($data = fgetcsv($handle, 1000, ";")) !== FALSE) {
        if (count($data) < 5) {
          $form_state->setErrorByName('locations_file', 'File has wrong format. Please check the file.  Correct format is POC_ID, Title, Address (Street + town), ZIP, Category (Optional. Can be empty.)');
        }
      }
    }
  }

  /**
   * @param $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *
   * @return void
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function importPocs(&$form, FormStateInterface $form_state) {
    \Drupal::messenger()->addMessage(t('File has been imported.'));
    $fileId = $form_state->getValue('locations_file');
    $reimport = $form_state->getValue('reimport_all');
    /** @var \Drupal\file\Entity\File $file */
    $file = \Drupal::entityTypeManager()->getStorage('file')->load($fileId[0]);
    $file_path = \Drupal::service('file_system')->realpath($file->getFileUri());

    $row = 0;
    $locations = [];
    if (($handle = fopen($file_path, "r")) !== FALSE) {
      while (($data = fgetcsv($handle, 1000, ";")) !== FALSE) {
        if ($row == 0) {
          $row++;
          continue;
        }
        if (!empty($data[0])) {
          $locations[$data[0] . ':' . $data[2]] = $data;
        }

        $row++;
      }
      fclose($handle);
    }

    $kv_store = \Drupal::service('keyvalue.expirable')
      ->get('abinbev_gmap');
    $kv_store->setWithExpire('bq_list_of_na_pocs', $locations, 3600 * 100 * 100);
    //$s = $kv_store->get('bq_list_of_na_pocs', NULL);

    $this->batchBuilder
      ->setTitle(t('Processing'))
      ->setInitMessage(t('Initializing.'))
      ->setProgressMessage(t('Completed @current of @total.'))
      ->setErrorMessage(t('An error has occurred.'));

    // Clear before import.
    if ($reimport) {
      $this->placeInfoService->truncateData();
    }

    $this->batchBuilder->addOperation([$this, 'processItems'], [$locations]);
    $this->batchBuilder->setFinishCallback([$this, 'finished']);

    batch_set($this->batchBuilder->toArray());
  }

  /**
   * Processor for batch operations.
   */
  public function processItems($items, array &$context) {
    // Elements per operation.
    $limit = 50;

    // Set default progress values.
    if (empty($context['sandbox']['progress'])) {
      $context['sandbox']['progress'] = 0;
      $context['sandbox']['max'] = count($items);
    }

    // Save items to array which will be changed during processing.
    if (empty($context['sandbox']['items'])) {
      $context['sandbox']['items'] = $items;
    }

    $counter = 0;
    if (!empty($context['sandbox']['items'])) {
      // Remove already processed items.
      if ($context['sandbox']['progress'] != 0) {
        array_splice($context['sandbox']['items'], 0, $limit);
      }

      foreach ($context['sandbox']['items'] as $item) {
        if ($counter != $limit) {
          $this->processItem($item);

          $counter++;
          $context['sandbox']['progress']++;

          $context['message'] = t('Now processing place :progress of :count', [
            ':progress' => $context['sandbox']['progress'],
            ':count' => $context['sandbox']['max'],
          ]);

          // Increment total processed item values. Will be used in finished
          // callback.
          $context['results']['processed'] = $context['sandbox']['progress'];
        }
      }
    }

    // If not finished all tasks, we count percentage of process. 1 = 100%.
    if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
      $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
    }
  }

  /**
   * Process single item.
   *
   * @param int|string $nid
   *   An id of Node.
   * @param \Drupal\Core\Datetime\DrupalDateTime $date
   *   An object with new published date.
   */
  public function processItem($place) {
    $poc_id = $place[0];
    $status = 'Published';

    $placeInfo = $this->placeInfoService->getPlaceData($poc_id);

    //if (!$placeInfo) {
    $result = $this->placeInfoService->getPlaceInfoFromGoogleByAdress($place);
    if (empty($result)) {
      \Drupal::logger('place google info fail')->error(print_r($place, 1));
      $status = 'Failed';
    }
    if (empty($result[0]['geometry']['location']['lat']) || empty($result[0]['geometry']['location']['lng'])) {
      //\Drupal::logger('import fail')->debug(print_r($result, 1));
      $status = 'Failed';
    }

    if ($status == 'Failed') {
      $values = [
        'title' => $place[1],
        'place_id' => $place[0],
        'poc_id' => $place[0],
        //'address' => implode(', ', [$place[2], $place[3]]),
        'lat' => 0,
        'lon' => 0,
        'full_info' => json_encode([
          "address_components" => [
            'formatted_address' => implode(', ', [
              $place[1],
              $place[2],
              $place[3],
            ]),
          ],
        ]),
        'zip' => $place[3] ?? '',
        'category' => $place[4] ?? '',
        'status' => $status,
      ];
    }
    if (!empty($result)) {
      \Drupal::logger('import success')->debug('<pre>'.print_r($result, 1).'</pre>');
      $values = [
        'title' => $place[1],
        'place_id' => $result[0]['place_id'] ?? $place[0],
        'poc_id' => $place[0],
        //'address' => implode(', ', [$place[2], $place[3]]),
        'lat' => $result[0]['geometry']['location']['lat'] ?? 0,
        'lon' => $result[0]['geometry']['location']['lng'] ?? 0,
        'full_info' => !empty($result[0]) ? json_encode($result[0]) : json_encode([]),
        'zip' => $place[3] ?? '',
        'category' => $place[4] ?? '',
        'status' => $status,
      ];
    }
    //$place_id = $result[0]['place_id'] ?? $place[1];
    $this->placeInfoService->saveData($poc_id, $values, $placeInfo ? TRUE : FALSE);
    //}
    return TRUE;
  }

  /**
   * Finished callback for batch.
   */
  public function finished($success, $results, $operations) {
    $message = t('Number of nodes affected by batch: @count', [
      '@count' => $results['processed'],
    ]);

    $this->placeInfoService->getAllPlaces(TRUE);
    \Drupal::messenger()
      ->addStatus($message);
    $view = \Drupal\views\Views::getView('gmap_locations_list');
    $view->storage->invalidateCaches();
  }

}
