<?php


namespace AutoAdmin\Bundle\BrickBuilder\Model;


use AutoAdmin\Bundle\BrickBuilder\Entity\Brick;
use AutoAdmin\Bundle\Content\Entity\Traits\CreatedAtTrait;
use AutoAdmin\Bundle\Content\Entity\Traits\UpdatedAtTrait;
use AutoAdmin\Bundle\Content\Model\CreatedAtInterface;
use AutoAdmin\Bundle\Content\Model\UpdatedAtInterface;
use DateTime;
use AutoAdmin\Bundle\Content\Service\ObfuscatorService;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

/**
 * Class AbstractBaseBrick
 *
 * @package AutoAdmin\Bundle\BrickBuilder\Block
 */
#[ORM\MappedSuperclass]
class AbstractBaseBrick implements
    BrickInterface,
    CreatedAtInterface,
    UpdatedAtInterface
{
    use CreatedAtTrait;
    use UpdatedAtTrait;

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: Types::INTEGER)]
    protected ?int $id = null;

    #[ORM\Column(type: Types::STRING, length: 255)]
    protected string $type = '';

    #[ORM\Column(type: Types::BOOLEAN, options: ['default' => true])]
    protected bool $enabled = true;

    #[ORM\Column(type: Types::JSON)]
    protected array $settings = [];

    #[ORM\ManyToOne(targetEntity: Brick::class, inversedBy: 'children')]
    #[ORM\JoinColumn(name: 'parent_id', referencedColumnName: 'id')]
    protected ?BrickInterface $parent = null;

    #[ORM\OneToMany(mappedBy: 'parent', targetEntity: Brick::class, cascade: ['ALL'])]
    #[ORM\OrderBy(['position' => 'ASC'])]
    protected Collection $children;

    #[ORM\Column(type: Types::INTEGER, nullable: true)]
    protected ?int $position = null;

    #[ORM\Column(type: Types::STRING, length: 255, nullable: true)]
    protected ?string $templateName;

    #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
    protected DateTimeInterface $templatedAt;

    public function __construct()
    {
        $this->children = new ArrayCollection();
        $this->enabled = true;
        $this->type = '';
        $this->settings = [];
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function setId(?int $id): BrickInterface
    {
        $this->id = $id;

        return $this;
    }

    public function getType(): string
    {
        return $this->type;
    }

    public function setType(string $type): BrickInterface
    {
        $this->type = $type;

        return $this;
    }

    public function isEnabled(): bool
    {
        return $this->enabled;
    }

    public function setEnabled(bool $enabled): BrickInterface
    {
        $this->enabled = $enabled;

        return $this;
    }

    public function getSettings(): ?array
    {
        return $this->settings;
    }

    public function setSettings(?array $settings): BrickInterface
    {
        // trigger doctrine listener if we affect new settings
        $this->setUpdatedAt(new DateTime());

        $this->settings = $settings;

        return $this;
    }

    /**
     * @return mixed|null
     */
    public function getSetting(string $name, $defaultValue = null)
    {
        if (array_key_exists($name, $this->settings) && $this->settings[$name] !== null) {
            $setting = $this->settings[$name];
            if (is_string($setting)) {
                return ObfuscatorService::obfuscate($setting);
            }
            return $setting;
        }

        return $defaultValue;
    }

    public function setSetting(string $name, $value = null)
    {
        $this->settings[$name] = $value;
    }

    public function getParent(): ?BrickInterface
    {
        return $this->parent;
    }

    public function setParent(?BrickInterface $parent): ?BrickInterface
    {
        $this->parent = $parent;

        return $this;
    }

    public function getChildren(): Collection
    {
        return $this->children;
    }

    public function setChildren(Collection $children): BrickInterface
    {
        $this->children = $children;

        return $this;
    }

    public function addChild(BrickInterface $brick): BrickInterface
    {
        $brick->setParent($this);
        $this->children->add($brick);

        return $this;
    }

    public function removeChild(BrickInterface $brick): BrickInterface
    {
        $brick->setParent(null);
        $this->children->removeElement($brick);

        return $this;
    }

    public function getPosition(): ?int
    {
        return $this->position;
    }

    public function setPosition(?int $position): BrickInterface
    {
        $this->position = $position;

        return $this;
    }

    public function getTemplate()
    {
        return $this->settings['_template'] ?? null;
    }

//    public function __toString()
//    {
//        $parents = [];
//        while (($parent = $this->getParent()) && !in_array($parent = $this->getParent(), $parents)) {
//            $parents[] = $parent;
//        }
//        $level = count($parents);
//
//        $pad = str_repeat('  ', $level);
//        $str = '';
//        $str .= "\n".$level.$pad.($level > 0 ? "└─ " : '')
//            .'brick['.($this->isLoaded ? 'loaded:1' : 'loaded:0').']['.$this->getType().']#'.($this->getId() ?? '');
//        return $str;
////        try {
//////            $settingsString = str_replace("\n", ' ', print_r($this->getSettings(), true));
////        } catch (\Exception $e) {
//////            $settingsString = 'exception occurred while trying to stringify settings';
////        }
////        $settingsString = 'settings';
////        $str .= "\n".$level.$pad.($level > 0 ? "   " : '').$settingsString;
//        $str .= "\n".$level.$pad.($level > 0 ? "   " : '').'children:';
//        foreach ($this->getChildren() as $child) {
//            $str .= (string)$child;
//        }
//
//        return $str;
//    }

    /**
     * @return string|null
     */
    public function getTemplateName(): ?string
    {
        return $this->templateName;
    }

    /**
     * @param string|null $templateName
     */
    public function setTemplateName(?string $templateName): BrickInterface
    {
        $this->templateName = $templateName;

        return $this;
    }

    public function getTemplatedAt(): ?DateTimeInterface
    {
        return $this->templatedAt;
    }

    public function setTemplatedAt(?DateTimeInterface $templatedAt): self
    {
        $this->templatedAt = $templatedAt;

        return $this;
    }
}
