<?php

/**
 * @file
 * Hook implementations.
 */

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\feeds\FeedInterface;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds_log\Entity\ImportLog;
use Drupal\feeds_log\Form\ClearLogConfirmForm;

/**
 * Implements hook_cron().
 *
 * Expires old logged imports.
 */
function feeds_log_cron() {
  $age_limit = \Drupal::config('feeds_log.settings')->get('age_limit');
  if (!$age_limit) {
    return;
  }
  $storage = \Drupal::entityTypeManager()->getStorage('feeds_import_log');
  $result = $storage->getQuery()
    ->condition('end', \Drupal::time()->getRequestTime() - $age_limit, '<')
    ->accessCheck(FALSE)
    ->range(0, 100)
    ->execute();

  if (!empty($result)) {
    $entities = $storage->loadMultiple($result);
    $storage->delete($entities);
  }

  // Clean up logs as well for old imports that never finished and for which
  // newer imports exist.
  $result = $storage->getQuery()
    ->condition('start', \Drupal::time()->getRequestTime() - $age_limit, '<')
    ->condition('end', '', 'IS NULL')
    ->accessCheck(FALSE)
    ->range(0, 20)
    ->execute();

  if (!empty($result)) {
    // For each entity, check if a newer import exists.
    foreach ($storage->loadMultiple($result) as $entity) {
      $newer_import = $storage->getQuery()
        ->condition('start', $entity->start->value, '>')
        ->condition('feed', $entity->feed->target_id)
        ->accessCheck(FALSE)
        ->range(0, 1)
        ->execute();

      if (!empty($newer_import)) {
        // A newer import was started. Clean up this old one.
        $entity->delete();
      }
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_delete() for 'feeds_feed'.
 *
 * Cleans up logs for feeds that get deleted.
 */
function feeds_log_feeds_feed_delete(FeedInterface $feed) {
  $storage = \Drupal::entityTypeManager()->getStorage('feeds_import_log');
  $result = $storage->getQuery()
    ->condition('feed', $feed->id())
    ->accessCheck(FALSE)
    ->execute();

  if (!empty($result)) {
    $entities = $storage->loadMultiple($result);
    $storage->delete($entities);
  }
}

/**
 * Implements hook_entity_type_alter().
 */
function feeds_log_entity_type_alter(array &$entity_types) {
  // Add a link template to the feeds_feed entity type.
  if (isset($entity_types['feeds_feed'])) {
    $entity_types['feeds_feed']->setLinkTemplate('clear_logs', '/feed/{feeds_feed}/log/clear');
    $entity_types['feeds_feed']->setFormClass('clear_logs', ClearLogConfirmForm::class);
  }
}

/**
 * Implements hook_entity_base_field_info().
 */
function feeds_log_entity_base_field_info(EntityTypeInterface $entity_type) {
  $fields = [];

  // Allow logs to be enabled per feed.
  if ($entity_type->id() === 'feeds_feed') {
    $fields['feeds_log'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Log'))
      ->setDescription(t('Whether or not logs should be recorded for this feed.'))
      ->setRevisionable(TRUE)
      ->setDefaultValue(TRUE)
      ->setDisplayOptions('form', [
        'type' => 'boolean_checkbox',
        'settings' => [
          'display_label' => TRUE,
        ],
      ])
      ->setDisplayConfigurable('form', TRUE);
  }

  return $fields;
}

/**
 * Implements hook_local_tasks_alter().
 *
 * Makes the tab 'Logs' for a feed entity use the feeds_import_log view in case
 * that is available.
 */
function feeds_log_local_tasks_alter(array &$local_tasks) {
  if (count(\Drupal::service('router.route_provider')->getRoutesByNames(['view.feeds_import_logs.page_1']))) {
    $local_tasks['feeds_import_log.logs_view']['route_name'] = 'view.feeds_import_logs.page_1';
  }
}

/**
 * Implements hook_file_download().
 *
 * Controls access of logged items on the filesystem.
 */
function feeds_log_file_download($uri) {
  // Check if the current user may view logs.
  if (!\Drupal::currentUser()->hasPermission('feeds_log.access')) {
    // No permission to view Feeds logs. No reason to check further.
    return;
  }

  // Check if the file is a logged item.
  $import_id = \Drupal::database()->select('feeds_import_log_entry')
    ->fields('feeds_import_log_entry', ['import_id'])
    ->condition('item', $uri)
    ->execute()
    ->fetchField();

  if (!$import_id) {
    // Check if the file is a logged source.
    $import_id = \Drupal::database()->select('feeds_import_log__sources')
      ->fields('feeds_import_log__sources', ['entity_id'])
      ->condition('sources_value', $uri)
      ->execute()
      ->fetchField();

    if (!$import_id) {
      // No associated logged import found.
      return;
    }
  }

  // Try to load the import log object.
  $import_log = ImportLog::load($import_id);
  if (!$import_log) {
    // Import log object no longer exist. Abort.
    return;
  }

  // Check if the current user may view files from this import.
  if (!$import_log->access('view')) {
    // No permission to view. Deny access.
    return -1;
  }

  // Guess the mimetype.
  if (preg_match('/\.json$/', $uri)) {
    $mimetype = 'application/json';
  }
  else {
    $mimetype = \Drupal::service('file.mime_type.guesser')->guessMimeType($uri);
  }

  // Access is granted.
  return [
    'Content-Type' => $mimetype,
    'Content-Length' => @filesize($uri),
    'Cache-Control' => 'private',
  ];
}

/**
 * Implements hook_form_FORM_ID_alter() for 'feeds_feed_type_form'.
 *
 * Adds log settings to the feed type.
 */
function feeds_log_form_feeds_feed_type_form_alter(array &$form, FormStateInterface $form_state) {
  $feed_type = $form_state->getFormObject()->getEntity();
  $enabled_operations = $feed_type->getThirdPartySetting('feeds_log', 'operations');
  $enabled_items = $feed_type->getThirdPartySetting('feeds_log', 'items');
  $enabled_source = $feed_type->getThirdPartySetting('feeds_log', 'source');

  $form['log_configuration'] = [
    '#type' => 'details',
    '#group' => 'plugin_settings',
    '#title' => t('Log settings'),
    '#attached' => [
      'library' => ['feeds_log/feed_type_settings'],
    ],
  ];

  $form['log_configuration']['status'] = [
    '#type' => 'checkbox',
    '#title' => t('Logging enabled'),
    '#default_value' => $feed_type->getThirdPartySetting('feeds_log', 'status') !== FALSE,
  ];

  $operations = _feeds_log_get_operations();
  $form['log_configuration']['operations'] = [
    '#tree' => TRUE,
    '#type' => 'table',
    '#header' => [
      'label' => t('Operation'),
      'enabled' => t('Log message'),
      'item' => t('Log item'),
    ],
    '#caption' => t('Select for which operations to log messages and items. Logged items are written to the filesystem.'),
  ];
  foreach ($operations as $operation => $label) {
    $row = [
      'label' => ['#plain_text' => $label],
      'enabled' => [
        '#type' => 'checkbox',
        '#title' => t('Enabled'),
        '#title_display' => 'invisible',
        '#default_value' => is_null($enabled_operations) || !empty($enabled_operations[$operation]) ? TRUE : FALSE,
        '#states' => [
          'enabled' => [
            ':input[name="log_configuration[status]"]' => ['checked' => TRUE],
          ],
        ],
      ],
      'item' => [
        '#type' => 'checkbox',
        '#title' => t('Log item'),
        '#title_display' => 'invisible',
        '#default_value' => is_null($enabled_items) || !empty($enabled_items[$operation]) ? TRUE : FALSE,
        '#states' => [
          'enabled' => [
            ':input[name="log_configuration[status]"]' => ['checked' => TRUE],
            ':input[name="log_configuration[operations][' . $operation . '][enabled]"]' => ['checked' => TRUE],
          ],
        ],
      ],
    ];

    // For the operation 'cleaned' no processed items are logged, so remove that
    // option for that operation.
    if ($operation == 'cleaned') {
      unset($row['item']);
    }

    $form['log_configuration']['operations'][$operation] = $row;
  }

  $form['log_configuration']['source'] = [
    '#type' => 'checkbox',
    '#title' => t('Log fetched source'),
    '#description' => t('If enabled, the fetched raw source data gets written to the Feeds log directory. This allows you to see how the raw source data looked like at the time of an import.'),
    '#default_value' => is_null($enabled_source) || $enabled_source,
    '#states' => [
      'enabled' => [
        ':input[name="log_configuration[status]"]' => ['checked' => TRUE],
      ],
    ],
  ];

  $form['log_configuration']['help'] = [
    '#markup' => t('On the <a href=":link" target="_blank">Feeds Log settings</a> page you can configure how long logs should be kept.', [
      ':link' => Url::fromRoute('feeds_log.settings')->toString(),
    ]),
  ];

  $form['#entity_builders'][] = 'feeds_log_form_feeds_feed_type_form_builder';
}

/**
 * Entity builder for the feed type entity.
 */
function feeds_log_form_feeds_feed_type_form_builder($entity_type, FeedTypeInterface $feed_type, array &$form, FormStateInterface $form_state) {
  $log_configuration = $form_state->getValue('log_configuration');

  // Check for which operations logs are enabled.
  $operations = [];
  $items = [];
  foreach ($log_configuration['operations'] as $operation => $details) {
    if (!empty($details['enabled'])) {
      $operations[$operation] = $operation;
    }
    if (!empty($details['item'])) {
      $items[$operation] = $operation;
    }
  }

  $feed_type->setThirdPartySetting('feeds_log', 'status', (bool) $log_configuration['status']);
  $feed_type->setThirdPartySetting('feeds_log', 'operations', $operations);
  $feed_type->setThirdPartySetting('feeds_log', 'items', $items);
  $feed_type->setThirdPartySetting('feeds_log', 'source', (bool) $log_configuration['source']);
}

/**
 * Implements hook_form_FORM_ID_alter() for 'feeds_feed_form'.
 */
function feeds_log_form_feeds_feed_form_alter(array &$form, FormStateInterface $form_state) {
  if (isset($form['feeds_log'])) {
    $form['feeds_log']['#group'] = 'options';

    // Check if logs are disabled on the feed type.
    $logs_enabled = $form_state->getFormObject()
      ->getEntity()
      ->getType()
      ->getThirdPartySetting('feeds_log', 'status');
    if ($logs_enabled === FALSE) {
      $form['feeds_log']['#disabled'] = TRUE;
    }
  }
}

/**
 * Gathers a list of uniquely defined feeds log operations.
 *
 * @return array
 *   List of uniquely defined feeds log operations.
 */
function _feeds_log_get_operations() {
  return [
    'created' => t('Created'),
    'updated' => t('Updated'),
    'deleted' => t('Deleted'),
    'skipped' => t('Skipped'),
    'failed' => t('Failed'),
    'cleaned' => t('Cleaned'),
  ];
}
