<?php

/**
 * @file
 * Hook implementations for the yuja_media module.
 */

/**
 * Implements hook_form_alter().
 *
 * Targets the standalone add form, the Media Library modal add form, and the
 * edit form for the yuja_video bundle. On all three:
 * - Adds a duplicate-detection warning (non-blocking, runs first).
 * - Adds a remote credential validation callback (blocks on mismatch).
 */
function yuja_media_form_alter(array &$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $targeted = [
    'media_yuja_video_add_form',
    'media_yuja_video_edit_form',
    'media_library_add_form_yuja_video',
  ];
  if (!in_array($form_id, $targeted, TRUE)) {
    return;
  }
  // Rename the core "Name" label to "Title" for this media type.
  if (isset($form['name']['widget'][0]['value'])) {
    $form['name']['widget'][0]['value']['#title'] = t('Title');
  }

  // Dedup runs first so the warning appears even when credential validation
  // subsequently sets a form error and blocks submission.
  $form['#validate'][] = '_yuja_media_dedup_warn';
  $form['#validate'][] = '_yuja_media_validate_credentials';
}

/**
 * Form validation callback: verify video_pid and auth_code against YuJa server.
 *
 * Calls the DrupalVideoMetadata endpoint using the configured YuJaURL and
 * AccessToken. Blocks the save when:
 * - The video_pid is not found or does not belong to this institution.
 * - The auth_code does not match the one stored for that video in YuJa.
 *
 * node_pid is server-validated via isVideoInNode() in the servlet.
 *
 * When the YuJa server is unreachable, validation is skipped with a warning so
 * a network hiccup does not permanently block saves.
 */
function _yuja_media_validate_credentials(array &$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $values = $form_state->getValue('field_yuja_video_id');
  if (empty($values[0])) {
    return;
  }

  $videoPid = trim((string) ($values[0]['video_pid'] ?? ''));
  $nodePid  = trim((string) ($values[0]['node_pid']  ?? ''));
  $authCode = trim((string) ($values[0]['auth_code'] ?? ''));

  if ($videoPid === '') {
    return;
  }

  $config = \Drupal::config('yuja_media.settings');
  $base   = rtrim((string) $config->get('YuJaURL'), '/');
  $token  = (string) $config->get('AccessToken');

  if ($base === '' || $token === '') {
    return;
  }

  $ts  = (string) (int) round(microtime(TRUE) * 1000);
  $sig = _yuja_media_sign($token, $videoPid, $ts);

  $query = ['videoPID' => $videoPid, 'ts' => $ts, 'sig' => $sig];
  if ($nodePid !== '') {
    $query['nodePID'] = $nodePid;
  }
  if ($authCode !== '') {
    $query['authCode'] = $authCode;
  }

  try {
    $response = \Drupal::httpClient()->get($base . '/DrupalVideoMetadata', [
      'query'   => $query,
      'timeout' => 10,
    ]);
    $data = json_decode((string) $response->getBody(), TRUE);
  }
  catch (\Exception $e) {
    \Drupal::messenger()->addWarning(t(
      'Could not reach the YuJa server to validate the video (@msg). Verify the Video PID, Node PID and Auth code are correct.',
      ['@msg' => $e->getMessage()]
    ));
    return;
  }

  if (!is_array($data) || empty($data['success'])) {
    $reason = is_array($data) ? ($data['message'] ?? t('Video not found.')) : t('Invalid response from server.');
    $form_state->setErrorByName(
      'field_yuja_video_id',
      t('YuJa video validation failed: @reason', ['@reason' => $reason])
    );
  }
}

/**
 * Form validation callback: warn when a matching yuja_video entity already exists.
 *
 * Checks all three identifiers (video_pid, node_pid, auth_code). On the edit
 * form the entity being edited is excluded from the check so it does not warn
 * about itself. If another matching entity is found, a warning with a link is
 * shown but the save is not blocked.
 */
function _yuja_media_dedup_warn(array &$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $values = $form_state->getValue('field_yuja_video_id');
  if (empty($values[0])) {
    return;
  }

  $videoPid = trim((string) ($values[0]['video_pid'] ?? ''));
  $nodePid  = trim((string) ($values[0]['node_pid']  ?? ''));
  $authCode = trim((string) ($values[0]['auth_code'] ?? ''));

  if ($videoPid === '') {
    return;
  }

  // On the edit form, exclude the entity currently being edited so it does
  // not warn about itself.
  $currentId = NULL;
  $formObject = $form_state->getFormObject();
  if (method_exists($formObject, 'getEntity')) {
    $entity = $formObject->getEntity();
    if ($entity && !$entity->isNew()) {
      $currentId = $entity->id();
    }
  }

  $storage = \Drupal::entityTypeManager()->getStorage('media');

  $ids = $storage->getQuery()
    ->accessCheck(FALSE)
    ->condition('bundle', 'yuja_video')
    ->condition('field_yuja_video_id.video_pid', $videoPid)
    ->execute();

  foreach ($storage->loadMultiple($ids) as $candidate) {
    if ($currentId !== NULL && $candidate->id() == $currentId) {
      continue;
    }
    $item = $candidate->get('field_yuja_video_id')->first();
    $existingNodePid  = $item ? trim((string) ($item->node_pid  ?? '')) : '';
    $existingAuthCode = $item ? trim((string) ($item->auth_code ?? '')) : '';
    if ($existingNodePid === $nodePid && $existingAuthCode === $authCode) {
      $link = $candidate->toLink(t('view existing entity'))->toString();
      \Drupal::messenger()->addWarning(t(
        'A YuJa video media entity for this video already exists — @link.',
        ['@link' => $link]
      ));
      return;
    }
  }
}

/**
 * Computes the Base64URL HMAC-SHA256 signature over "{subject}\n{ts}".
 *
 * Mirrors com.yuja.drupal.tokenManagement.DrupalSignatureHelper#sign.
 */
function _yuja_media_sign($token, $subject, $ts) {
  $raw = hash_hmac('sha256', $subject . "\n" . $ts, $token, TRUE);
  return rtrim(strtr(base64_encode($raw), '+/', '-_'), '=');
}
