<?php

namespace WHOOP\Entities\Elements\Traits;

use WHOOP\Entities\Element;
use WHOOP\Entities\Elements\Html;

trait ElementBuilderTraits
{
  /**
   * @var Element[]
   */
  protected $elements = [];
  protected ?string $content = '';

  public function append(string $html): Element
  {
    $element = Html::create($html);

    return $this->addElement($element);
  }

  public function prepend(string $html): Element
  {
    $element = Html::create($html);

    return $this->addElement($element, null, true);
  }

  /**
   * @return Element|object
   */
  public function appendElement($element, ?string $key = null)
  {
    return $this->addElement($element, $key);
  }

  /**
   * @return Element|object
   */
  public function prependElement($element, ?string $key = null)
  {
    return $this->addElement($element, $key, true);
  }

  /**
   * @return Element|object
   */
  public function addElement($element, ?string $key = null, bool $prepend = false)
  {
    if (is_null($key)) {
      $key = count($this->elements);
    }

    if ($prepend) {
      $this->elements = [ $key => $element ] + $this->elements;
    } else {
      $this->elements[$key] = $element;
    }

    return $this->elements[$key];
  }

  public function setContent(array|string|Element|callable $content): self
  {
    $this->elements = [];

    if (
      is_callable($content) && $content instanceof \Closure
    ) {
      $content = $content();
    }

    if (is_array($content)) {
      $this->content = '';

      foreach ($content as $contentItem) {
        if ($content instanceof Element) {
          $this->appendElement($contentItem);
        } else {
          $this->append($contentItem);
        }
      }
    } elseif ($content instanceof Element) {
      $this->addElement($content);
    } else {
      $this->content = $content;
    }

    return $this;
  }

  public function getContent(): ?string
  {
    return $this->content;
  }

  public function getContentString(): ?string
  {
    if (!empty($this->elements)) {
      $contentString = '';

      foreach ($this->elements as $element) {
        $contentString .= (string) $element;
      }
    } else {
      $contentString = $this->content;
    }

    return $contentString;
  }

  public function __toString(): string
  {
    $tag = static::TAG;
    $content = $this->getContentString();

    if (!$tag) {
      return $content;
    }

    if (empty($content) && !static::$renderIfEmpty) {
      return '';
    }

    $attributesString = $this->getAttributesString();
    if ($attributesString) {
      $attributesString = ' ' . $attributesString;
    }

    if (static::$selfClosing) {
      return sprintf('<%s%s>', $tag, $attributesString);
    } else {
      return sprintf('<%s%s>%s</%s>', $tag, $attributesString, $content, $tag);
    }
  }

  public static function create(mixed $content = null, ?array $attributes = null): static
  {
    $element = new static($content);

    if ($attributes) {
      foreach ($attributes as $attribute => $value) {
        $element->setAttribute($attribute, $value);
      }
    }

    return $element;
  }
}
