<?php

declare(strict_types=1);

namespace Drupal\api_monitor_client;

use Drupal\api_monitor_client\Entity\ApiMonitorProviderInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Manager for create Swagger.
 */
class ApiSwaggerManager {

  use StringTranslationTrait;

  /**
   * The configuration factory service.
   */
  protected ImmutableConfig $config;

  /**
   * Constructs a new instance of the class.
   *
   * @param ApiMonitorProbeManager $apiMonitorProbeManager
   *   The API monitor probe manager service.
   * @param ConfigFactoryInterface $configFactory
   *   The configuration factory service.
   * @param ApiSnifferService $apiSniffer
   *   The API sniffer service.
   */
  public function __construct(
    private readonly ApiMonitorProbeManager $apiMonitorProbeManager,
    private readonly ConfigFactoryInterface $configFactory,
    private readonly ApiSnifferService $apiSniffer,
  ) {
    $this->config = $this->configFactory->get('api_monitor_client.settings');
  }

  /**
   * Generates a Swagger/OpenAPI specification for a given API definition.
   *
   * @param array $api
   *   The API definition.
   * @param string $api_id
   *   The unique identifier of the API.
   * @param \Drupal\api_monitor_client\Entity\ApiMonitorProviderInterface $provider
   *   The provider entity.
   *
   * @return array|null
   *   The generated Swagger/OpenAPI specification as an associative array.
   */
  public function swagger(array $api, string $api_id, ApiMonitorProviderInterface $provider) : ?array {
    $method = strtolower($api['methods'][0] ?? 'get');
    $endpoint = $api['path'];

    $payload = [];
    $spec = [
      'openapi' => '3.0.0',
      'info' => [
        'title' => "Dettaglio API: " . ($api['label'] ?? $api_id),
        'version' => '1.0.0',
      ],
      'servers' => [
        [
          'url' => $provider->getProviderUrl(),
          'description' => $provider->label(),
        ],
      ],
      'paths' => [
        $endpoint => [
          $method => [
            'summary' => "Monitoraggio per $api_id",
            'responses' => [],
          ],
        ],
      ],
      'components' => [
        'securitySchemes' => [],
      ],
    ];

    $probes = $this->apiMonitorProbeManager->getDefinitions();
    foreach ($probes as $plugin_id => $definition) {
      if ($definition['endpoint_id'] === $api_id) {
        $plugin = $this->apiMonitorProbeManager->createInstance($plugin_id);
        if (method_exists($plugin, 'getPayload')) {
          $payload = $plugin->getPayload();
        }

        // Parameter in PATH (es. {id}).
        if (method_exists($plugin, 'getRouteParameters')) {
          foreach ($plugin->getRouteParameters() as $name => $value) {
            $spec['paths'][$endpoint][$method]['parameters'][] = [
              'name' => $name,
              'in' => 'path',
              'required' => TRUE,
              'schema' => [
                'type' => 'string',
                'example' => $value,
              ],
            ];
          }
        }

        // Parameter in queryString (es. ?filter=abc).
        if (method_exists($plugin, 'getQueryOptions')) {
          foreach ($plugin->getQueryOptions() as $name => $value) {
            $spec['paths'][$endpoint][$method]['parameters'][] = [
              'name' => $name,
              'in' => 'query',
              'required' => FALSE,
              'schema' => [
                'type' => 'string',
                'example' => $value,
              ],
            ];
          }
        }
      }
    }

    // Aggiungiamo la risposta reale eseguendo un test live.
    $response = $this->apiSniffer->call($api, $provider);
    if ($response) {
      $status = $response->getStatusCode();
      $content = $response->getBody()->getContents();
      $decoded = json_decode($content, TRUE);

      $spec['paths'][$endpoint][$method]['responses'][(string) $status] = [
        'description' => $this->t('Risposta reale dal server durante il test live')->render(),
        'content' => [
          'application/json' => [
            'schema' => [
              'type' => 'object',
              'example' => $decoded ?: $content,
            ],
          ],
        ],
      ];
    }
    else {
      // Fallback se la chiamata fallisce.
      $spec['paths'][$endpoint][$method]['responses']['500'] = [
        'description' => $this->t('Impossibile connettersi all\'endpoint per generare un esempio.')->render(),
      ];
    }

    if (in_array($method, ['post', 'put', 'patch']) && !empty($payload)) {
      $spec['paths'][$endpoint][$method]['requestBody'] = [
        'content' => [
          'application/json' => [
            'schema' => [
              'type' => 'object',
              'example' => $payload,
            ],
          ],
        ],
      ];
    }

    if ($provider->isAuthEnabled() && !empty($api['auth_required']) && in_array('oauth2', $api['auth_method'] ?? [])) {
      $token_endpoint = $provider->getTokenEndpoint() ?: '/oauth/token';
      $spec['components']['securitySchemes']['OAuth2ClientCredentials'] = [
        'type' => 'oauth2',
        'description' => 'Autenticazione tramite OAuth2 (' . $provider->label() . ')',
        'flows' => [
          'clientCredentials' => [
            'tokenUrl' => $provider->getProviderUrl() . $token_endpoint,
            'scopes' => [
              $provider->getScope() ?: 'api_monitor_agent',
            ],
          ],
        ],
      ];

      $spec['paths'][$endpoint][$method]['security'] = [
        ['OAuth2ClientCredentials' => []],
      ];
    }

    return $spec;
  }

}
