<?php

namespace App\Model\Entities;

use App\Model\Entities\Interfaces\IBaseRecord;
use App\Model\Entities\Security\User;
use App\Model\Exceptions\Db\FinalStateException;
use App\Model\Exceptions\Db\ForbiddenTransitionException;
use App\Model\Exceptions\Db\InvalidStateException;
use App\Model\Interfaces\ICommonRecord;
use DateTime;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\MappedSuperclass
 */
abstract class BaseRecord extends BaseEntity implements IBaseRecord
{
    /**
     * @ORM\Column(type="string")
     */
    protected $state;

    /**
     * @var DateTime
     * @ORM\Column(type="datetime")
     */
    protected $createdAt;

    /**
     * @var User
     * @ORM\ManyToOne(targetEntity="App\Model\Entities\Security\User")
     */
    protected $createdBy;

    /**
     * @var DateTime
     * @ORM\Column(type="datetime", nullable=true)
     */
    protected $editedAt;

    /**
     * @var User
     * @ORM\ManyToOne(targetEntity="App\Model\Entities\Security\User")
     * @ORM\JoinColumn(nullable=true)
     */
    protected $editedBy;

    /**
     * BaseRecord constructor.
     * @param User $createdBy
     */
    public function __construct(User $createdBy)
    {
        $this->createdBy = $createdBy;
        $this->createdAt = new DateTime();

        if ($this instanceof ICommonRecord) {
            $this->setState(ICommonRecord::CREATED);
        }
    }

    /**
     * @return DateTime
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }

    /**
     * @return User
     */
    public function getCreatedBy() : User
    {
        return $this->createdBy;
    }

    /**
     * @return DateTime|null
     */
    public function getEditedAt()
    {
        return $this->editedAt;
    }

    /**
     * @return User|null
     */
    public function getEditedBy()
    {
        return $this->editedBy;
    }

    /**
     * @param User $editedBy
     */
    public function setEditedBy(User $editedBy)
    {
        $this->editedBy = $editedBy;

        $this->editedAt = new DateTime();
    }

    /**
     * @return string
     */
    public function getState() : string
    {
        return $this->state;
    }

    /**
     * @param string $state
     * @throws FinalStateException
     * @throws ForbiddenTransitionException
     * @throws InvalidStateException
     */
    public function setState(string $state)
    {
        if (!$this->isValidState($state)) {
            throw new InvalidStateException("Unexpected state value: {$state}.");
        }

        $this->state = $state;
    }

    protected function isValidState(string $state) : bool
    {
        return in_array($state, $this->getValidStates());
    }

    protected function getValidStates() : array
    {
        $states = [self::TRASH, self::DELETED];
        if ($this instanceof ICommonRecord) {
            $states[] = ICommonRecord::CREATED;
            $states[] = ICommonRecord::ACTIVE;
        }

        return $states;
    }


}