<?php

declare(strict_types=1);

namespace Drupal\api_monitor_client\Form;

use Drupal\api_monitor_client\Entity\ApiMonitorProvider;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\key\KeyRepository;
use GuzzleHttp\ClientInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Entity form for Api monitor provider entities.
 */
final class ApiMonitorProviderForm extends EntityForm {

  /**
   * The HTTP client.
   *
   * @var \GuzzleHttp\ClientInterface
   */
  protected ClientInterface $httpClient;

  /**
   * The key repository.
   *
   * @var \Drupal\key\KeyRepository
   */
  protected KeyRepository $keyRepository;

  /**
   * Constructs an ApiMonitorProviderForm object.
   *
   * @param \GuzzleHttp\ClientInterface $http_client
   *   The HTTP client.
   * @param \Drupal\key\KeyRepository $keyRepository
   *   The key repository.
   */
  public function __construct(ClientInterface $http_client, KeyRepository $keyRepository) {
    $this->httpClient = $http_client;
    $this->keyRepository = $keyRepository;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('http_client'),
      $container->get('key.repository')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state): array {
    $form = parent::form($form, $form_state);

    /** @var \Drupal\api_monitor_client\Entity\ApiMonitorProviderInterface $entity */
    $entity = $this->entity;

    $form['label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Label'),
      '#maxlength' => 255,
      '#default_value' => $entity->label(),
      '#description' => $this->t('Label for the API Monitor Provider.'),
      '#required' => TRUE,
    ];

    $form['id'] = [
      '#type' => 'machine_name',
      '#default_value' => $entity->id(),
      '#machine_name' => [
        'exists' => [ApiMonitorProvider::class, 'load'],
      ],
      '#disabled' => !$entity->isNew(),
    ];

    $form['provider_url'] = [
      '#type' => 'url',
      '#title' => $this->t('Provider URL'),
      '#default_value' => $entity->getProviderUrl(),
      '#description' => $this->t('The base URL of the provider.'),
      '#required' => TRUE,
    ];

    $form['discovery_endpoint'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Discovery Endpoint'),
      '#default_value' => $entity->getDiscoveryEndpoint() ?: '',
      '#description' => $this->t('The endpoint for discovery (e.g. /api/v1/meta/discovery). Leave empty to use only manual endpoints.'),
      '#required' => FALSE,
    ];

    $form['discovery_help'] = [
      '#type' => 'details',
      '#title' => $this->t('Discovery Response Format'),
      '#open' => FALSE,
    ];

    $example_json = [
      [
        "id" => "api_id",
        "label" => "API Label",
        "path" => "/api/v1/resource",
        "methods" => ["GET"],
        "auth_required" => TRUE,
        "auth_method" => ["oauth2"],
      ],
    ];

    $form['discovery_help']['format'] = [
      '#markup' => '<p>' . $this->t('The discovery endpoint must return a JSON array of objects with the following structure:') . '</p>' .
        '<pre><code>' . json_encode($example_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . '</code></pre>',
    ];

    $form['token_endpoint'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Token Endpoint (OAuth2)'),
      '#default_value' => $entity->getTokenEndpoint() ?: '/oauth/token',
      '#description' => $this->t('The endpoint to request the OAuth2 token (e.g. /oauth/token).'),
      '#required' => TRUE,
      '#states' => [
        'visible' => [
          ':input[name="auth_enabled"]' => ['checked' => TRUE],
        ],
        'required' => [
          ':input[name="auth_enabled"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['auth_enabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable OAuth2 Authentication'),
      '#default_value' => $entity->isAuthEnabled(),
      '#description' => $this->t('If checked, OAuth2 authentication will be used for discovery and requests.'),
    ];

    $form['auth'] = [
      '#type' => 'details',
      '#title' => $this->t('Authentication Credentials'),
      '#open' => TRUE,
      '#states' => [
        'visible' => [
          ':input[name="auth_enabled"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['auth']['client_id'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Client ID'),
      '#default_value' => $entity->getClientId(),
      '#states' => [
        'required' => [
          ':input[name="auth_enabled"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['auth']['client_secret'] = [
      '#type' => 'key_select',
      '#title' => $this->t('Client Secret'),
      '#default_value' => $entity->get('client_secret'),
      '#states' => [
        'required' => [
          ':input[name="auth_enabled"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['auth']['scope'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Scope'),
      '#default_value' => $entity->getScope(),
      '#parents' => ['scope'],
    ];

    return $form;
  }

  /**
   * Check if IP is in private range (RFC 1918).
   *
   * @param string $ip
   *   The IP address to check.
   *
   * @return bool
   *   TRUE if IP is private.
   */
  protected function isPrivateIp(string $ip): bool {
    if (!filter_var($ip, FILTER_VALIDATE_IP)) {
      return FALSE;
    }
    return !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    parent::validateForm($form, $form_state);

    $provider_url = $form_state->getValue('provider_url');
    $discovery_endpoint = $form_state->getValue('discovery_endpoint');
    $token_endpoint = $form_state->getValue('token_endpoint');

    if (empty($provider_url)) {
      $form_state->setErrorByName('provider_url', $this->t('Provider URL is required.'));
      return;
    }

    try {
      // Check if the protocol is HTTPS (mandatory for remote sites).
      $url_parts = parse_url($provider_url);
      $scheme = $url_parts['scheme'] ?? '';
      $host = $url_parts['host'] ?? '';

      $is_local = in_array($host, ['localhost', '127.0.0.1']) ||
        str_ends_with($host, '.ddev.site') ||
        str_ends_with($host, '.local') ||
        $this->isPrivateIp($host);

      if ($scheme !== 'https' && !$is_local) {
        $form_state->setErrorByName('provider_url', $this->t('Il protocollo HTTPS è obbligatorio per i siti remoti per garantire la sicurezza del token OAuth.'));
      }

      // Check if the provider URL is accessible (skip for local hosts).
      if (!$is_local) {
        $response = $this->httpClient->request('GET', $provider_url, ['timeout' => 10]);
        if ($response->getStatusCode() !== 200) {
          $form_state->setErrorByName('provider_url', $this->t('Provider URL is not accessible.'));
        }
      }

      // Check if discovery endpoint is valid (relative path) only if provided.
      if (!empty($discovery_endpoint) && !str_starts_with($discovery_endpoint, '/')) {
        $form_state->setErrorByName('discovery_endpoint', $this->t('Discovery endpoint must start with a slash (/).'));
      }

      // Check if token endpoint is valid (relative path).
      if (!str_starts_with($token_endpoint, '/')) {
        $form_state->setErrorByName('token_endpoint', $this->t('Token endpoint must start with a slash (/).'));
      }
    }
    catch (\Exception $e) {
      $form_state->setErrorByName('provider_url', $e->getMessage());
    }
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state): int {
    $result = parent::save($form, $form_state);
    $message_args = ['%label' => $this->entity->label()];
    $this->messenger()->addStatus(
      $result === SAVED_NEW
        ? $this->t('Created new api monitor provider %label.', $message_args)
        : $this->t('Updated api monitor provider %label.', $message_args)
    );
    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
    return $result;
  }

}
