<?php

declare(strict_types=1);

namespace Drupal\simple_interactive_maps;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\UseCacheBackendTrait;
use Drupal\simple_interactive_maps\Event\MapBuildEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
 * Builds the render array and settings array for a map.
 */
final class MapBuilder {

  private const CACHE_PREFIX = 'simple_interactive_maps:';

  use UseCacheBackendTrait;

  /**
   * Constructs a MapBuilder object.
   */
  public function __construct(
    private MapDataLoader $mapDataLoader,
    private MapDefinitionPluginManager $mapDefinitionPluginManager,
    protected $cacheBackend,
    protected EventDispatcherInterface $eventDispatcher,
  ) {}

  /**
   * Builds the render array and settings array for a map.
   *
   * @param \Drupal\simple_interactive_maps\InteractiveMapInterface $map
   *   Map to build render data for.
   * @param string $context
   *   Render/build context.
   *
   * @return \Drupal\simple_interactive_maps\MapRenderHelper
   *   Helper object containing the render array and settings array.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function getRenderHelper(InteractiveMapInterface $map, string $context = ''): MapRenderHelper {
    $cid = self::CACHE_PREFIX . 'map:' . $map->id();

    if ($context !== '') {
      $cid .= ':' . $context;
    }

    if ($cache = $this->cacheGet($cid)) {
      return $cache->data;
    }

    $renderData = new MapRenderHelper();

    $regions = $this->mapDataLoader->loadMapData($map);
    $settings = [];

    // Fails fast (PluginNotFoundException) if the map's base_map plugin no
    // longer exists.
    $this->mapDefinitionPluginManager->getDefinition($map->get('base_map'));

    $svg = [
      '#type' => 'html_tag',
      '#tag' => 'svg',
      '#attributes' => [
        'xmlns' => 'http://www.w3.org/2000/svg',
        'xmlns:xlink' => 'http://www.w3.org/1999/xlink',
        'preserveAspectRatio' => 'xMidYMid meet',
        'version' => '1.1',
      ],
    ];

    $description = [
      '#type' => 'html_tag',
      '#tag' => 'desc',
      '#value' => $map->label(),
    ];

    $svg[] = $description;

    $bboxPaths = [];

    foreach ($regions as $region) {
      if ($region['hidden']) {
        continue;
      }
      $settings[$region['id']] = [
        'label' => $region['label'],
        'action' => [
          'name' => $region['action'],
          'configuration' => $region['action_configuration'],
        ],
        'style' => [
          'fill_color' => $region['fill_color'],
          'hover_color' => $region['hover_color'],
          'stroke' => $region['stroke_color'],
          'text_color' => $region['text_color'],
        ],
        'tooltip' => $region['tooltip']['processed'],
      ];

      $regionTag = [
        '#type' => 'html_tag',
        '#tag' => 'g',
        '#attributes' => [
          'class' => 'region action-' . $region['action'],
          'data-region-id' => $region['id'],
        ],
      ];

      foreach ($region['svg-data']['paths'] as $pathData) {
        $bboxPaths[] = $pathData;
        $regionTag[] = [
          '#type' => 'html_tag',
          '#tag' => 'path',
          '#attributes' => [
            'd' => $pathData,
            'fill' => $region['fill_color'],
            'stroke' => $region['stroke_color'],
            'stroke-width' => 1,
            'stroke-linejoin' => 'round',
            'aria-label' => $region['label'],
          ],
        ];
      }

      if (array_key_exists('text', $region)) {
        $textTag = [
          '#type' => 'html_tag',
          '#tag' => 'text',
          '#value' => $region['text']['text'],
          '#attributes' => [
            'x' => $region['text']['x'],
            'y' => $region['text']['y'],
            'font-size' => '11px',
            'style' => 'text-anchor: middle; dominant-baseline: central; fill: ' . $region['text_color'] . '; font-family: Arial, sans-serif;',
            'text-anchor' => 'middle',
          ],
        ];
        $regionTag[] = $textTag;
      }

      $svg[] = $regionTag;
    }

    $viewBox = SvgHelper::calculateViewBox($bboxPaths);
    $svg['#attributes']['viewBox'] = $viewBox;

    $renderData->setRenderArray($svg);
    $renderData->setSettings($settings);

    $event = new MapBuildEvent($map, $renderData, $context);
    $this->eventDispatcher->dispatch($event, MapBuildEvent::EVENT_NAME);

    $this->cacheSet($cid, $renderData, Cache::PERMANENT, $map->getCacheTags());

    return $renderData;
  }

}
