<?php

/**
 * @file
 * Install, update and uninstall functions for the YAML form module.
 */

use Drupal\node\Entity\NodeType;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Field\BaseFieldDefinition;

/**
 * Implements hook_install().
 */
function yamlform_install() {
  // Don't override the system.mail.interface.yamlform if the default interface
  // is the 'test_mail_collector'.
  if (\Drupal::configFactory()->get('system.mail')->get('interface.default') == 'test_mail_collector') {
    return;
  }

  // Enable the YAML form mail plugin which allows HTML email to sent.
  // IMPORTANT: Since the Mail System module completely overrides the
  // 'plugin.manager.mail' service the below settings will be completely
  // ignored.
  // @see \Drupal\mailsystem\MailsystemServiceProvider.
  $config = \Drupal::configFactory()->getEditable('system.mail');
  $mail_plugins = $config->get('interface');
  $mail_plugins['yamlform'] = 'yamlform_php_mail';
  $config->set('interface', $mail_plugins)->save();
}

/**
 * Implements hook_uninstall().
 */
function yamlform_uninstall() {
  // Remove the YAML form mail plugin.
  $config = \Drupal::configFactory()->getEditable('system.mail');
  $mail_plugins = $config->get('interface');
  unset($mail_plugins['yamlform']);
  $config->set('interface', $mail_plugins)->save();

  // Delete the YAML form node type.
  if ($node_type = NodeType::load('yamlform')) {
    $node_type->delete();
  }

  // Delete field storage if it is no longer being used by any bundles.
  if ($field_storage = \Drupal::entityManager()->getStorage('field_storage_config')->load('node.yamlform')) {
    if (!$field_storage->getBundles()) {
      $field_storage->delete();
    }
  }
}

/**
 * Implements hook_requirements().
 */
function yamlform_requirements($phase) {
  if ($phase != 'runtime') {
    return [];
  }

  $requirements = [];

  /** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
  $library_discovery = \Drupal::service('library.discovery');
  $codemirror_library = $library_discovery->getLibraryByName('yamlform', 'codemirror');
  $codemirror_version = $codemirror_library['version'];
  $codemirror_library_exists = (file_exists(DRUPAL_ROOT . '/libraries/codemirror')) ? TRUE : FALSE;

  $t_args = [
    ':href' => "https://cdnjs.com/libraries/codemirror/$codemirror_version",
    '@version' => $codemirror_version,
  ];
  $requirements['yamlform_codemirror'] = [
    'title' => t('YAML form: CodeMirror'),
    'value' => ($codemirror_library_exists) ? t('@version (Installed)', $t_args) : t('@version (<a href=":href">CDN</a>)', $t_args),
    'severity' => REQUIREMENT_OK,
  ];
  return $requirements;
}

/**
 * Add support for HTML and file attachments to YamlFormEmailHandler.
 */
function yamlform_update_8001(&$sandbox) {
  $messages = [];

  // Update 'yamlform.settings' configuration.
  // Copied from \views_update_8001().
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');
  $default_data = Yaml::decode(file_get_contents(drupal_get_path('module', 'yamlform') . '/config/install/yamlform.settings.yml'));

  $settings_config->clear('mail.default_body');
  $settings_config->set('mail.default_body_text', $default_data['mail']['mail.default_body_text']);
  $settings_config->set('mail.default_body_html', $default_data['mail']['mail.default_body_html']);
  $settings_config->save();

  $messages[] = \Drupal::translation()->translate("Update YAML form settings removed 'mail.default_body' and replaced with 'mail_default_body_text' and 'mail_default_body_html'");

  // Update 'yamlform.yamlform.*' configuration.
  // Copied from \views_update_8001().
  $ids = [];

  $config_factory = \Drupal::configFactory();
  foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
    $yamlform_config = $config_factory->getEditable($yamlform_config_name);
    $handlers = $yamlform_config->get('handlers');

    $has_email_handler = FALSE;
    foreach ($handlers as $handler_id => $handler) {
      if ($handler['id'] != 'email') {
        continue;
      }
      $has_email_handler = TRUE;

      $base = "handlers.$handler_id";

      // Get debug setting so that we can move it last.
      $debug = $yamlform_config->get($base . '.settings.debug');
      $yamlform_config->clear($base . '.settings.debug');

      $yamlform_config->set($base . '.settings.html', FALSE);
      $yamlform_config->set($base . '.settings.attachments', FALSE);
      $yamlform_config->set($base . '.settings.debug', $debug);
    }

    if ($has_email_handler) {
      $ids[] = $yamlform_config->get('id');
      $yamlform_config->save(TRUE);
    }
  }

  if (!empty($ids)) {
    $messages[] = t('Updated email handler for YAML forms: @ids', ['@ids' => implode(', ', array_unique($ids))]);
  }

  return implode("\n", $messages);
}

/**
 * Issue #2701113: Rework YAML form submission view to use templates.
 */
function yamlform_update_8002(&$sandbox) {
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');
  $settings_config->clear('display');
  $settings_config->save();
}

/**
 * Issue #2705859  Allow YAML form elements to define custom display formats. Add emptu formats to yamlform.settings.yml.
 */
function yamlform_update_8003(&$sandbox) {
  /** @var \Drupal\yamlform\YamlFormElementManagerInterface $element_manager */
  $element_manager = \Drupal::service('plugin.manager.yamlform.element');
  $element_plugins = $element_manager->getInstances();
  $format = [];
  foreach ($element_plugins as $element_id => $element_plugin) {
    $formats = $element_plugin->getFormats();
    if (empty($formats)) {
      continue;
    }
    if (count($formats) == 1 && isset($formats['value'])) {
      continue;
    }

    $format[$element_id] = '';
  }
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');
  $settings_config->set('format', $format);
  $settings_config->save();
}


/**
 * Issue #2709933: Save export options.
 */
function yamlform_update_8004(&$sandbox) {
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');
  $settings_config->set('export', [
    'delimiter' => ',',
    'header_keys' => 'label',
    'entity_reference_format' => 'link',
    'options_format' => 'compact',
    'options_item_format' => 'label',
  ]);
  $settings_config->save();
}

/**
 * Issue #2712457: Implement Wizard/Paging. Add 'current_page' field to 'yamlform_submission' entities.
 */
function yamlform_update_8005() {
  // Install the definition that this field had in
  // \Drupal\yamlform\Entity\YamlFormSubmission::baseFieldDefinitions()
  // at the time that this update function was written. If/when code is
  // deployed that changes that definition, the corresponding module must
  // implement an update function that invokes
  // \Drupal::entityDefinitionUpdateManager()->updateFieldStorageDefinition()
  // with the new definition.
  $storage_definition = BaseFieldDefinition::create('string')
    ->setLabel(t('Current page'))
    ->setDescription(t('The current wizard page.'))
    ->setSetting('max_length', 128);
  \Drupal::entityDefinitionUpdateManager()
    ->installFieldStorageDefinition('current_page', 'yamlform_submission', 'yamlform', $storage_definition);
}

/**
 * Issue #2712457: Implement Wizard/Paging. Update 'yamlform.settings' and 'yamlform.yamlform.*' configuration.
 */
function yamlform_update_8007() {
  // Update 'yamlform.settings' configuration.
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');
  $settings_config->set('settings.default_wizard_prev_button_label', '< Previous Page');
  $settings_config->set('settings.default_wizard_next_button_label', 'Next Page >');
  $settings_config->save();

  // Update 'yamlform.yamlform.*' configuration.
  $config_factory = \Drupal::configFactory();
  foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
    $yamlform_config = $config_factory->getEditable($yamlform_config_name);
    $yamlform_config->set('settings.wizard_progress_bar', TRUE);
    $yamlform_config->set('settings.wizard_prev_button_label', '');
    $yamlform_config->set('settings.wizard_next_button_label', '');
    $yamlform_config->save(TRUE);
  }
}

/**
 * Issue #2712463: Provide Entity–attribute–value model for submissions. Create 'yamlform_submission_data' table.
 */
function yamlform_update_8008() {
  $schema = yamlform_schema();
  \Drupal::database()->schema()->createTable('yamlform_submission_data', $schema['yamlform_submission_data']);
}

/**
 * Issue #2712463: Provide Entity–attribute–value model for submissions. Populate 'yamlform_submission_data' table using batch process.
 */
function yamlform_update_8009(&$sandbox) {
  // @see https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Extension%21module.api.php/function/hook_update_N/8.1.x
  // Loop through 100 YAML form submission at at time.
  $limit = 100;
  if (!isset($sandbox['progress'])) {
    $sandbox['progress'] = 0;
    $sandbox['current_sid'] = 0;
    $sandbox['max'] = \Drupal::database()->query('SELECT COUNT(sid) FROM {yamlform_submission}')->fetchField();
  }

  // @see \Drupal\yamlform\Entity\YamlFormSubmission::save().
  $records = \Drupal::database()->select('yamlform_submission', 's')
    ->fields('s', ['sid', 'yamlform_id', 'data'])
    ->condition('sid', $sandbox['current_sid'], '>')
    ->range(0, $limit)
    ->orderBy('sid', 'ASC')
    ->execute();
  foreach ($records as $record) {
    $data = Yaml::decode($record->data);
    $yamlform_id  = $record->yamlform_id;
    $sid = $record->sid;

    $rows = [];
    $update_submission_record = FALSE;
    foreach ($data as $name => &$item) {
      if (is_array($item)) {
        foreach ($item as $key => $value) {
          // Remove target_id from saved entity_autocomplete fields.
          if (is_array($value) && isset($value['target_id'])) {
            $value = $value['target_id'];
            $item[$key] = $value;
            $update_submission_record = TRUE;
          }

          $rows[] = [
            'yamlform_id' => $yamlform_id,
            'sid' => $sid,
            'name' => $name,
            'delta' => (string) $key,
            'value' => (string) $value,
          ];
        }
      }
      else {
        $rows[] = [
          'yamlform_id' => $yamlform_id,
          'sid' => $sid,
          'name' => $name,
          'delta' => '',
          'value' => (string) $item,
        ];
      }
    }

    // Delete existing submission data rows.
    \Drupal::database()->delete('yamlform_submission_data')
      ->condition('sid', $sid)
      ->execute();

    // Insert new submission data rows.
    $query = \Drupal::database()
      ->insert('yamlform_submission_data')
      ->fields(['yamlform_id', 'sid', 'name', 'delta', 'value']);
    foreach ($rows as $row) {
      $query->values($row);
    }
    $query->execute();

    // Update submission record.
    if ($update_submission_record) {
      \Drupal::database()
        ->update('yamlform_submission')
        ->fields(['data' => Yaml::encode($data)])
        ->condition('sid', $sid)
        ->execute();
    }

    $sandbox['progress']++;
    $sandbox['current_sid'] = $sid;
  }

  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);

  if ($sandbox['#finished']) {
    return t("Populating the 'yamlform_submission_data' table is complete.");
  }
}

/**
 * Issue #2712463: Provide Entity–attribute–value model for submissions. Remove #default_value target_id.
 */
function yamlform_update_8010() {
  $ids = [];

  $config_factory = \Drupal::configFactory();
  foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
    $yamlform_config = $config_factory->getEditable($yamlform_config_name);

    $inputs = $yamlform_config->get('inputs');
    if (preg_match('/-\s+target_id:/', $inputs)) {
      // Have to regex to replace inputs target_id: in YAML because if we parse
      // the inputs YAML all comments and formatting will be lost.
      $inputs = preg_replace('/-\s+target_id: /', '- ', $inputs);

      $yamlform_config->set('inputs', $inputs);
      $yamlform_config->save(TRUE);

      $ids[] = $yamlform_config->get('id');
    }
  }

  if ($ids) {
    return t('Updated YAML forms: @ids', ['@ids' => implode(', ', array_unique($ids))]);
  }
}

/**
 * Issue #2718005: Support Confidential submissions.
 */
function yamlform_update_8011() {
  // Update 'yamlform.settings' configuration.
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');
  $settings_config->set('settings.default_form_confidential_message', 'This form is confidential. You must <a href="[site:login-url]/logout?destination=[current-page:url:relative]">Log out</a> to submit it.');
  $settings_config->save();

  // Update 'yamlform.yamlform.*' configuration.
  $config_factory = \Drupal::configFactory();
  foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
    $yamlform_config = $config_factory->getEditable($yamlform_config_name);
    $yamlform_config->set('settings.form_confidential', FALSE);
    $yamlform_config->set('settings.form_confidential_message', '');
    $yamlform_config->save(TRUE);
  }
}

/**
 * Issue #2721539: Wizard page numbers and percentage.
 */
function yamlform_update_8012() {
  // Update 'yamlform.yamlform.*' configuration.
  $config_factory = \Drupal::configFactory();
  foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
    $yamlform_config = $config_factory->getEditable($yamlform_config_name);
    $yamlform_config->set('settings.wizard_progress_pages', FALSE);
    $yamlform_config->set('settings.wizard_progress_percentage', FALSE);
    $yamlform_config->save(TRUE);
  }
}

/**
 * Issue #2721711: Add start and complete label to wizard settings.
 */
function yamlform_update_8013() {
  // Update 'yamlform.settings' configuration.
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');
  $settings_config->set('settings.default_wizard_start_label', 'Start');
  $settings_config->set('settings.default_wizard_complete_label', 'Complete');
  $settings_config->save();

  // Update 'yamlform.yamlform.*' configuration.
  $config_factory = \Drupal::configFactory();
  foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
    $yamlform_config = $config_factory->getEditable($yamlform_config_name);
    $yamlform_config->set('settings.wizard_start_label', '');
    $yamlform_config->set('settings.wizard_complete', TRUE);
    $yamlform_config->set('settings.wizard_complete_label', '');
    $yamlform_config->save(TRUE);
  }
}

/**
 * Issue #2748699: Change all references from Inputs to Elements.
 */
function yamlform_update_8014() {
  $config_factory = \Drupal::configFactory();

  // Update 'yamlform.settings' configuration.
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');

  $yaml = Yaml::encode($settings_config->getRawData());
  $yaml = preg_replace('/(\s+)inputs: /', '\1elements: ', $yaml);
  $yaml = preg_replace('/(\s+)default_inputs: /', '\1default_elements: ', $yaml);

  $settings_config->setData(Yaml::decode($yaml));
  $settings_config->save();

  // Update 'yamlform.yamlform.*' configuration.
  $yamlform_config_names = [
    'example_inputs' => 'example_elements',
    'example_inputs_extras' => 'example_elements_extras',
    'example_inputs_formats' => 'example_elements_formats',
    'example_inputs_masks' => 'example_elements_masks',
    'example_inputs_states' => 'example_elements_states',
  ];
  foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
    $yamlform_config = $config_factory->getEditable($yamlform_config_name);
    $yamlform_id = str_replace('yamlform.yamlform.', '', $yamlform_config_name);

    $yaml = Yaml::encode($yamlform_config->getRawData());
    $yaml = preg_replace('/(\s+)inputs: /', '\1elements: ', $yaml);
    $yaml = preg_replace('/(\s+)excluded_inputs: /', '\1excluded_elements: ', $yaml);
    $yaml = str_replace('_inputs_', '_elements_', $yaml);
    $yaml = str_replace(' input ', ' element ', $yaml);
    $yaml = str_replace(' Inputs', ' Elements', $yaml);
    $yaml = str_replace(' inputs', ' elements', $yaml);
    $yaml = str_replace('Private input', 'Private element ', $yaml);

    if (isset($yamlform_config_names[$yamlform_id])) {
      $yaml = str_replace($yamlform_id, $yamlform_config_names[$yamlform_id], $yaml);
      $data = Yaml::decode($yaml);
      \Drupal::configFactory()->getEditable('yamlform.yamlform.' . $yamlform_id)->delete();
      \Drupal::configFactory()->getEditable('yamlform.yamlform.' . $yamlform_config_names[$yamlform_id])->setData($data)->save();
    }
    else {
      $data = Yaml::decode($yaml);
      \Drupal::configFactory()->getEditable('yamlform.yamlform.' . $yamlform_id)->setData($data)->save();
    }
  }
}

/**
 * Issue #2749063: Load YAML form submission data using EAV table. Fix yamlform_submission_data table's deltas.
 */
function yamlform_update_8015(&$sandbox) {
  // @see https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Extension%21module.api.php/function/hook_update_N/8.1.x
  // Loop through 100 YAML form submission at at time.
  $limit = 100;
  if (!isset($sandbox['progress'])) {
    $sandbox['progress'] = 0;
    $sandbox['current_sid'] = 0;
    $sandbox['max'] = \Drupal::database()->query('SELECT COUNT(sid) FROM {yamlform_submission}')->fetchField();
  }

  // @see \Drupal\yamlform\Entity\YamlFormSubmission::save().
  $records = \Drupal::database()->select('yamlform_submission', 's')
    ->fields('s', ['sid', 'yamlform_id', 'data'])
    ->condition('sid', $sandbox['current_sid'], '>')
    ->range(0, $limit)
    ->orderBy('sid', 'ASC')
    ->execute();
  foreach ($records as $record) {
    $data = Yaml::decode($record->data);
    $yamlform_id  = $record->yamlform_id;
    $sid = $record->sid;

    $rows = [];
    $update_submission_record = FALSE;
    foreach ($data as $name => &$item) {
      if (is_array($item)) {
        $index = 0;
        foreach ($item as $key => $value) {
          // Fix multi value element's delta to be an index.
          // Change ['value1' => 'value1', 'value2' => 'value2'] to
          // [0 => 'value1', 1 => 'value2'].
          if ($key == $value) {
            $key = $index;
          }
          $index++;
          $rows[] = [
            'yamlform_id' => $yamlform_id,
            'sid' => $sid,
            'name' => $name,
            'delta' => (string) $key,
            'value' => (string) $value,
          ];
        }
      }
      else {
        $rows[] = [
          'yamlform_id' => $yamlform_id,
          'sid' => $sid,
          'name' => $name,
          'delta' => '',
          'value' => (string) $item,
        ];
      }
    }

    // Delete existing submission data rows.
    \Drupal::database()->delete('yamlform_submission_data')
      ->condition('sid', $sid)
      ->execute();

    // Insert new submission data rows.
    $query = \Drupal::database()
      ->insert('yamlform_submission_data')
      ->fields(['yamlform_id', 'sid', 'name', 'delta', 'value']);
    foreach ($rows as $row) {
      $query->values($row);
    }
    $query->execute();

    // Update submission record.
    if ($update_submission_record) {
      \Drupal::database()
        ->update('yamlform_submission')
        ->fields(['data' => Yaml::encode($data)])
        ->condition('sid', $sid)
        ->execute();
    }

    $sandbox['progress']++;
    $sandbox['current_sid'] = $sid;
  }

  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);

  if ($sandbox['#finished']) {
    return t("Populating the 'yamlform_submission_data' table is complete.");
  }
}

/**
 * Issue #2749063: Load YAML form submission data using EAV table. Remove yamlform_submission.data field.
 */
function yamlform_update_8016() {
  db_drop_field('yamlform_submission', 'data');
}

/**
 * Issue #2748699: Change all references from Inputs to Elements. Update yamlform_id in the yamlform_submission_data table.
 */
function yamlform_update_8017() {
  $yamlform_config_names = [
    'example_inputs' => 'example_elements',
    'example_inputs_extras' => 'example_elements_extras',
    'example_inputs_formats' => 'example_elements_formats',
    'example_inputs_masks' => 'example_elements_masks',
    'example_inputs_states' => 'example_elements_states',
  ];
  foreach ($yamlform_config_names as $config_name_search => $config_name_replace) {
    db_query('UPDATE {yamlform_submission_data} SET yamlform_id=:new_id WHERE yamlform_id=:old_id', [':old_id' => $config_name_search, ':new_id' => $config_name_replace]);
  }
}


/**
 * Issue #2752817: Create creditcard composite element.
 */
function yamlform_update_8018() {
  $config_factory = \Drupal::configFactory();

  // Update 'yamlform.settings' configuration.
  $settings_config = \Drupal::configFactory()->getEditable('yamlform.settings');
  $yaml = Yaml::encode($settings_config->getRawData());
  $yaml = str_replace('creditcard', 'creditcard_number', $yaml);
  $settings_config->setData(Yaml::decode($yaml));
  $settings_config->save();

  // Update 'yamlform.yamlform.*' configuration.
  foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
    $yamlform_config = $config_factory->getEditable($yamlform_config_name);
    $yaml = Yaml::encode($yamlform_config->getRawData());
    $yaml = str_replace('creditcard', 'creditcard_number', $yaml);
    $data = Yaml::decode($yaml);
    \Drupal::configFactory()->getEditable($yamlform_config_name)->setData($data)->save();
  }
}
