<?php
declare( strict_types=1 );

namespace WP_AUX\Entities\AuxFramework\Partials;

use WHOOP\Entities\Elements\Img;
use WP_AUX\Entities\AuxFramework\ElementClassesEntity;
use WP_AUX\Enum\CardsLayoutEnum;
use WP_AUX\Services\AuxFramework\ElementClassesService;
use WP_AUX\Services\PageTemplateService;

/**
 * The AUX Card Group, consisting of cards and an optional section intro
 *
 * All card groups have one or more sets of cards. Most cards groups only have
 * one set. This 'primary' set is created automatically. Unless otherwise
 * specified, cards are added to this set.
 *
 * @since 1.0.0
 * @link https://aggieux.tamu.edu/?path=/docs/components-cards-card-groups-card-groups-about-card-groups--docs
 */
class CardGroupEntity {
  protected ?string $heading = '';
  protected ?string $superheader = '';
  protected ?string $subheader = '';
  protected ?string $description = '';
  protected array $ctas = [];
  protected ?string $background = '';
  protected ?string $style = '';
  protected array $modifiers = [];
  protected ?CardsLayoutEnum $cardsLayout = null;
  protected ?string $cardsCtaType = null;
  /**
   * @var CardEntity[]
   */
  protected array $cards = [];
  /**
   * @var CardGroupSetEntity[]
   */
  protected array $cardSets = [];

  public function __construct(string|CardsLayoutEnum|null $cardsLayout = null) {
    $this->setCardsLayout($cardsLayout);
    $this->cardSets['primary'] = new CardGroupSetEntity($this);
  }

  /**
   * Set the heading, which appears in the section intro
   * @since 1.0.0
   */
  public function setHeading(?string $heading): self
  {
    $this->heading = $heading;
    return $this;
  }

  /**
   * Set the superheader, which appears in the section intro
   * @since 1.0.0
   */
  public function setSuperheader(?string $superheader): self
  {
    $this->superheader = $superheader;
    return $this;
  }

  /**
   * Set the subheader, which appears in the section intro
   * @since 1.0.0
   */
  public function setSubheader(?string $subheader): self
  {
    $this->subheader = $subheader;
    return $this;
  }

  /**
   * Set the description, which appears in the section intro
   * @since 1.0.0
   */
  public function setDescription(?string $description): self
  {
    $this->description = $description;
    return $this;
  }

  /**
   * Adds a CTA, which appears in the section intro
   *
   * If a string is provided, that will be the URL of the new CTA
   *
   * @since 1.0.0
   */
  public function addCta(array|string $cta): self
  {
    if (is_string($cta) && !empty($cta)) {
      $cta = [
        'url' => $cta,
      ];
    }

    if (is_array($cta)) {
      $this->ctas[] = $cta;
    }

    return $this;
  }

  /**
   * @since 1.0.0
   */
  public function setBackground(?string $background): self
  {
    $this->background = $background;
    return $this;
  }

  /**
   * @since 1.0.0
   */
  public function setStyle(?string $style): self
  {
    $this->style = $style;
    return $this;
  }

  /**
   * Register a style modifier for the card group
   * @since 1.0.0
   * @see ElementClassesEntity::addModifier()
   */
  public function addModifier(string $modifier): self
  {
    $this->modifiers[] = $modifier;
    return $this;
  }

  /**
   * Get the layout of the cards in the card sets
   * @since 1.0.0
   */
  public function getCardsLayout(): ?CardsLayoutEnum
  {
    return $this->cardsLayout;
  }

  /**
   * Set the layout of the cards in the card sets. Supported options found in the CardsLayoutEnum.
   * @since 1.0.0
   * @see \WP_AUX\Enum\CardsLayoutEnum
   */
  public function setCardsLayout(string|CardsLayoutEnum|null $cardsLayout): self
  {
    if (is_string($cardsLayout)) {
      $cardsLayout = CardsLayoutEnum::tryFrom($cardsLayout);
    }

    if (!$cardsLayout) {
      $cardsLayout = CardsLayoutEnum::DEFAULT;
    }

    $this->cardsLayout = $cardsLayout;

    return $this;
  }

  public function getCardsCtaType(): ?string
  {
    return $this->cardsCtaType;
  }

  /**
   * Set the CTA types for cards in card sets
   *
   * All configured sets of cart CTA type options are found in the
   * <code>\WP_AUX\Enum\CtaTypes\\</code> directory.
   *
   * @since 1.0.0
   */
  public function setCardsCtaType(?string $cardsCtaType): self
  {
    $this->cardsCtaType = $cardsCtaType;
    return $this;
  }

  /**
   * Add a card set with the given key to the group
   * @since 1.0.0
   */
  public function addCardSet(string $setKey): CardGroupSetEntity
  {
    if (!isset($this->cardSets[$setKey])) {
      $this->cardSets[$setKey] = new CardGroupSetEntity($this);
    }

    return $this->cardSets[$setKey];
  }

  /**
   * Get a card set by key
   * @since 1.0.0
   */
  public function getCardSet(string $setKey): ?CardGroupSetEntity
  {
    return $this->cardSets[$setKey] ?? null;
  }

  /**
   * Set the identifier of a card group set by key
   * @since 1.0.0
   * @uses CardGroupSetEntity::setIdentifier()
   */
  public function setCardSetIdentifier(string $setKey, string $identifier): self
  {
    if (!isset($this->cardSets[$setKey])) {
      $this->addCardSet($setKey);
    }

    $this->cardSets[$setKey]->setIdentifier($identifier);
    return $this;
  }

  /**
   * Add fully-formed card entity to a card group set (defaults to 'primary' set)
   *
   * If a set with the provided key is not yet defined, one will be created and
   * added to the card group with that key.
   *
   * @since 1.0.0
   */
  public function addCardEntity(CardEntity $cardEntity, string $setKey = 'primary'): self
  {
    if (!isset($this->cardSets[$setKey])) {
      $this->addCardSet($setKey);
    }

    $this->cardSets[$setKey]->addCardEntity($cardEntity);
    return $this;
  }

  /**
   * Add card to a card group set (defaults to 'primary' set)
   *
   * If a set with the provided key is not yet defined, one will be created and
   * added to the card group with that key.
   *
   * @since 1.0.0
   */
  public function addCard(
    ?string $heading,
    ?string $content,
    ?string $superheader = null,
    ?string $subheader = null,
    ?string $icon = null,
    array|null|string|Img $image = [],
    string $setKey = 'primary'
  ): ?CardEntity
  {
    if (!isset($this->cardSets[$setKey])) {
      $this->addCardSet($setKey);
    }

    return $this->cardSets[$setKey]->addCard(
      $heading,
      $content,
      $superheader,
      $subheader,
      $icon,
      $image
    );
  }

  /**
   * Get the section intro entity for the group if a heading is provided
   * @since 1.0.0
   */
  public function getSectionIntro(): ?SectionIntroEntity
  {
    $sectionIntro = null;

    if ($this->heading) {
      $sectionIntro = SectionIntroEntity::create()->setDescription($this->description);

      $sectionIntro->addHeader(
        $this->heading,
        null,
        $this->superheader,
        $this->subheader
      );

      if (!empty($this->ctas)) {
        foreach ($this->ctas as $cta) {
          $sectionIntro->addCta($cta);
        }
      }
    }

    return $sectionIntro;
  }

  /**
   * HTML string of the card group
   * @since 1.0.0
   */
  public function __toString(): string
  {
    $sectionClasses = ElementClassesService::buildClasses('section-wrap', '', $this->background);

    $sectionIntro = $this->getSectionIntro();

    $cardsContent = '';
    $cardsSetCounts = [];

    if ($this->cardSets) {
      foreach ($this->cardSets as $cardSet) {
        $cardsContent .= (string) $cardSet;
        $cardsSetCounts[] = $cardSet->cardsCount();
      }

      if (count($this->cardSets) > 1) {
        $cardsCount = join('-', $cardsSetCounts);
        $cardsContent = sprintf(
          '<div class="card-container" post-count="%s">%s</div>',
          $cardsCount,
          $cardsContent
        );
      }
    }

    $componentClasses = ElementClassesService::buildClasses(
      $this->cardsLayout->baseClass(),
      $this->style,
      $sectionClasses
    );

    if ($this->modifiers) {
      foreach ($this->modifiers as $modifier) {
        $componentClasses->addModifier($modifier);
      }
    }

    PageTemplateService::applyInternalPageClass($componentClasses);

    $divider = $sectionClasses->hasDivider() ? '<span class="divider-dots gray-300"></span>' : '';

    $output = <<<HTML
      <section class="$sectionClasses">
        $divider
        <div class="$componentClasses">
          $sectionIntro
          $cardsContent
        </div>
      </section>
    HTML;

    return $output;
  }

  /**
   * Create an empty card group with the specified layout
   * @since 1.0.0
   */
  public static function create(string|CardsLayoutEnum|null $cardsLayout): static
  {
    return new static($cardsLayout);
  }
}
