<?php

namespace Civi\PhpStorm;

/**
 * Utility class for building new metadata files. For example:
 *
 * $builder = new PhpStormMetadata('foobar', __CLASS__);
 * $builder->addOverrideMap('\Foo::bar()', ['gee' => '\Whiz\Bang']);
 * $builder->write();
 *
 * @link https://www.jetbrains.com/help/phpstorm/2021.3/ide-advanced-metadata.html
 */
class PhpStormMetadata {

  /**
   * Short symbolic name. Maps to filename.
   *
   * @var string
   */
  protected $name;

  /**
   * The party responsible for building this thing.
   *
   * @var string
   */
  protected $attribution;

  /**
   * @var string
   */
  protected $buffer;

  /**
   * @param string $name
   *   Short symbolic name. This will be used in the filename.
   * @param string $attribution
   *   The party responsible for running this builder. If someone finds buggy, where should they look first?
   */
  public function __construct(string $name, string $attribution) {
    $this->name = $name;
    $this->attribution = $attribution;
    $this->buffer = '';
  }

  public function registerArgumentsSet(string $name, ...$args) {
    $escapedName = var_export($name, 1);
    $escapedArgs = implode(', ', array_map(function($arg) {
      return var_export($arg, 1);
    }, $args));
    $this->buffer .= "registerArgumentsSet($escapedName, $escapedArgs);\n";
    return $this;
  }

  /**
   * @param string $for
   *   Ex: '\Civi\Core\SettingsBag::get()'
   * @param int $index
   *   The positional offset among the arguments
   * @param string $argumentSet
   *   Name of the argument set. (This should already be defined by `registerArgumentsSet()`.)
   * @return $this
   */
  public function addExpectedArguments(string $for, int $index, string $argumentSet) {
    $escapedSet = var_export($argumentSet, 1);
    $this->buffer .= "expectedArguments($for, $index, argumentsSet($escapedSet));\n";
    return $this;
  }

  /**
   * @param string $for
   * @return $this
   */
  public function addExitPoint(string $for) {
    $this->buffer .= "exitPoint($for);\n";
    return $this;
  }

  /**
   * @param string $for
   * @param array $map
   *   Array(string $parameterId => string $className)
   *
   * @return $this
   */
  public function addOverrideMap(string $for, array $map) {
    // PhpStorm 2022.3.1: 'Civi\\Foo' doesn't work, but 'Civi\Foo' does.
    $mapData = str_replace('\\\\', '\\', var_export($map, 1));
    $this->buffer .= "override($for, map(\n$mapData));\n";
    return $this;
  }

  /**
   * Write the metadata to its file.
   *
   * @return void
   */
  public function write(): void {
    $path = phpstorm_metadata_dir();
    if ($path === NULL) {
      return;
    }

    if (file_exists($path) && !is_dir($path)) {
      unlink($path);
    }
    if (!file_exists($path)) {
      mkdir($path);
    }

    if (!preg_match(';^[-a-zA-Z0-9]+$;', $this->name)) {
      throw new \RuntimeException("Malformed name for metadata file");
    }
    $file = $path . DIRECTORY_SEPARATOR . $this->name . '.php';

    $lines = [];
    $lines[] = "<" . "?php";
    $lines[] = sprintf("// Generated by %s at %s", $this->attribution, date('Y-m-d H:i:s'));
    $lines[] = "namespace PHPSTORM_META {\n";
    $lines[] = $this->buffer;
    $lines[] = "}";

    $content = implode("\n", $lines) . "\n";
    file_put_contents($file, $content);
  }

}
