<?php

declare(strict_types=1);

namespace Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor;

use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\flowdrop\Attribute\FlowDropNodeProcessor;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\flowdrop\DTO\ValidationError;
use Drupal\flowdrop\DTO\ValidationResult;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\AbstractFlowDropNodeProcessor;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Executor for Logger nodes.
 *
 * Logs messages using Drupal's logger service. Supports all standard
 * PSR log levels (emergency, alert, critical, error, warning, notice,
 * info, debug).
 *
 * @internal This class is not part of the public API and may change without notice.
 */
#[FlowDropNodeProcessor(
  id: "logger",
  label: new \Drupal\Core\StringTranslation\TranslatableMarkup("Logger"),
  description: "Log messages using Drupal's logger service",
  version: "1.0.0"
)]
class Logger extends AbstractFlowDropNodeProcessor {

  /**
   * The logger channel factory.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected LoggerChannelFactoryInterface $loggerFactory;

  /**
   * Constructs a Logger object.
   *
   * @param array<string, mixed> $configuration
   *   The plugin configuration.
   * @param string $plugin_id
   *   The plugin ID.
   * @param mixed $plugin_definition
   *   The plugin definition.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   The logger channel factory.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    LoggerChannelFactoryInterface $logger_factory,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->loggerFactory = $logger_factory;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ): static {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get("logger.factory")
    );
  }

  /**
   * {@inheritdoc}
   */
  public function validateParams(array $params): ValidationResult {
    $errors = [];

    // Validate message parameter.
    if (isset($params["message"]) && !is_string($params["message"]) && !is_numeric($params["message"])) {
      $errors[] = new ValidationError("message", "Message must be a string or number");
    }

    // Validate log level.
    if (isset($params["level"])) {
      $allowedLevels = [
        "emergency",
        "alert",
        "critical",
        "error",
        "warning",
        "notice",
        "info",
        "debug",
      ];
      if (!in_array($params["level"], $allowedLevels, TRUE)) {
        $errors[] = new ValidationError("level", "Log level must be one of: " . implode(", ", $allowedLevels));
      }
    }

    // Validate channel parameter.
    if (isset($params["channel"]) && !is_string($params["channel"])) {
      $errors[] = new ValidationError("channel", "Channel must be a string");
    }

    if (!empty($errors)) {
      return ValidationResult::failure($errors);
    }

    return ValidationResult::success();
  }

  /**
   * {@inheritdoc}
   */
  public function process(ParameterBagInterface $params): array {
    // Get message text.
    $message = $params->get("message", "");

    // Convert message to string if it's not already.
    if (!is_string($message)) {
      if (is_numeric($message)) {
        $message = (string) $message;
      }
      elseif (is_array($message) || is_object($message)) {
        $message = json_encode($message, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
      }
      else {
        $message = (string) $message;
      }
    }

    // Get log level, default to "info".
    $level = $params->getString("level", "info");

    // Get channel, default to "flowdrop".
    $channel = $params->getString("channel", "flowdrop");

    // Get logger channel.
    $logger = $this->loggerFactory->get($channel);

    // Log the message using the appropriate log level.
    $this->logMessage($logger, $level, $message);

    // Return output indicating the message was logged.
    return [
      "message" => $message,
      "level" => $level,
      "channel" => $channel,
      "logged" => TRUE,
    ];
  }

  /**
   * Logs a message using the specified log level.
   *
   * @param \Psr\Log\LoggerInterface $logger
   *   The logger instance.
   * @param string $level
   *   The log level.
   * @param string $message
   *   The message to log.
   */
  protected function logMessage(LoggerInterface $logger, string $level, string $message): void {
    switch ($level) {
      case "emergency":
        $logger->emergency($message);
        break;

      case "alert":
        $logger->alert($message);
        break;

      case "critical":
        $logger->critical($message);
        break;

      case "error":
        $logger->error($message);
        break;

      case "warning":
        $logger->warning($message);
        break;

      case "notice":
        $logger->notice($message);
        break;

      case "info":
        $logger->info($message);
        break;

      case "debug":
        $logger->debug($message);
        break;

      default:
        // Fallback to info if invalid level.
        $logger->info($message);
        break;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "message" => [
          "type" => "string",
          "title" => "Message",
          "description" => "The message text to log",
          "required" => TRUE,
        ],
        "level" => [
          "type" => "string",
          "title" => "Log Level",
          "description" => "The log level to use",
          "default" => "info",
          "enum" => [
            "emergency",
            "alert",
            "critical",
            "error",
            "warning",
            "notice",
            "info",
            "debug",
          ],
        ],
        "channel" => [
          "type" => "string",
          "title" => "Log Channel",
          "description" => "The logger channel to use (e.g., 'flowdrop', 'system', 'user')",
          "default" => "flowdrop",
        ],
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getOutputSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "message" => [
          "type" => "string",
          "description" => "The message that was logged",
        ],
        "level" => [
          "type" => "string",
          "description" => "The log level that was used",
        ],
        "channel" => [
          "type" => "string",
          "description" => "The logger channel that was used",
        ],
        "logged" => [
          "type" => "boolean",
          "description" => "Whether the message was successfully logged",
        ],
      ],
    ];
  }

}
