<?php

namespace core\entities\post;

use core\behaviors\MetaBehavior;
use core\entities\Meta;
use core\entities\post\queries\PostQuery;
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\UploadedFile;
use yiidreamteam\upload\ImageUploadBehavior;
use Yii;


/**
 * This is the model class for table "posts".
 *
 * @property int $id
 * @property int $category_id
 * @property int $published_at
 * @property int $created_at
 * @property int $updated_at
 * @property string $title
 * @property string $description
 * @property string $content
 * @property string $image
 * @property string $video
 * @property int $status
 * @property string $meta_json
 * @property int $comments_count
 * @property int $type_id
 * @property int $views
 * @property string $slug
 *
 * @property PostComment[] $postComments
 * @property PostTagAssignment[] $postTagAssignments
 * @property PostTag[] $tags
 * @property PostCategory $category
 * @property PostType $type
 *
 * @mixin ImageUploadBehavior
 */
class Post extends ActiveRecord
{
	const STATUS_DRAFT = 0;
	const STATUS_ACTIVE = 1;

	const FILE_ORIGINAL_PATH = '@staticRoot/origin/posts';

	public $meta;

	public static function create($categoryId, $title, $slug, $description, $content, $type_id, $published_at, $video, Meta $meta): self
	{
		$post = new static();
		$post->category_id = $categoryId;
		$post->title = $title;
		$post->slug = $slug;
		$post->description = $description;
		$post->content = $content;
		$post->meta = $meta;
		$post->status = self::STATUS_DRAFT;
		$post->created_at = time();
		$post->comments_count = 0;
		$post->type_id = $type_id;
		$post->published_at = $published_at;
		$post->video = $video;
		return $post;
	}

	public function setImage(UploadedFile $image): void
	{
		$this->image = $image;
	}


	public function edit($categoryId, $title, $slug, $description, $content, $published_at, $video, Meta $meta): void
	{
		$this->category_id = $categoryId;
		$this->title = $title;
		$this->slug = $slug;
		$this->description = $description;
		$this->content = $content;
		$this->meta = $meta;
		$this->published_at = $published_at;
		$this->video = $video;
	}

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'posts';
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => Yii::t('post', 'ID'),
            'category_id' => Yii::t('post', 'Category'),
            'published_at' => Yii::t('post', 'Published At'),
            'created_at' => Yii::t('post', 'Created At'),
            'updated_at' => Yii::t('post', 'Updated At'),
            'title' => Yii::t('post', 'Title'),
            'description' => Yii::t('post', 'Description'),
            'content' => Yii::t('post', 'Content'),
            'image' => Yii::t('post', 'Image'),
            'video' => Yii::t('post', 'Video'),
            'status' => Yii::t('post', 'Status'),
            'meta_json' => Yii::t('post', 'Meta Json'),
            'comments_count' => Yii::t('post', 'Comments Count'),
            'type_id' => Yii::t('post', 'Type'),
            'views' => Yii::t('post', 'Views'),
            'slug' => Yii::t('post', 'Slug'),
            'meta.title' => Yii::t('post', 'Meta Title'),
            'meta.description' => Yii::t('post', 'Meta Description'),
            'meta.keywords' => Yii::t('post', 'Meta Keywords'),
        ];
    }

	public function activate(): void
	{
		if ($this->isActive()) {
			throw new \DomainException(Yii::t('post', 'Post is already active.'));
		}
		$this->status = self::STATUS_ACTIVE;
	}

	public function draft(): void
	{
		if ($this->isDraft()) {
			throw new \DomainException('Post is already draft.');
		}
		$this->status = self::STATUS_DRAFT;
	}

	public function isActive(): bool
	{
		return $this->status == self::STATUS_ACTIVE;
	}


	public function isDraft(): bool
	{
		return $this->status == self::STATUS_DRAFT;
	}

	public function getSeoTitle(): string
	{
		return $this->meta->title ?: $this->title;
	}

	// Tags

	public function assignTag($id): void
	{
		$assignments = $this->postTagAssignments;
		foreach ($assignments as $assignment) {
			if ($assignment->isForTag($id)) {
				return;
			}
		}
		$assignments[] = PostTagAssignment::create($id);
		$this->postTagAssignments = $assignments;
	}

	public function revokeTag($id): void
	{
		$assignments = $this->postTagAssignments;
		foreach ($assignments as $i => $assignment) {
			if ($assignment->isForTag($id)) {
				unset($assignments[$i]);
				$this->postTagAssignments = $assignments;
				return;
			}
		}
		throw new \DomainException('Assignment is not found.');
	}

	public function revokeTags(): void
	{
		$this->postTagAssignments = [];
	}

	// Comments

	public function addComment($userId, $parentId, $text): PostComment
	{
		$parent = $parentId ? $this->getComment($parentId) : null;
		if ($parent && !$parent->isActive()) {
			throw new \DomainException('Cannot add comment to inactive parent.');
		}
		$comments = $this->postComments;
		$comments[] = $comment = PostComment::create($userId, $parent ? $parent->id : null, $text);
		$this->updateComments($comments);
		return $comment;
	}

	public function editComment($id, $parentId, $text): void
	{
		$parent = $parentId ? $this->getComment($parentId) : null;
		$comments = $this->postComments;
		foreach ($comments as $comment) {
			if ($comment->isIdEqualTo($id)) {
				$comment->edit($parent ? $parent->id : null, $text);
				$this->updateComments($comments);
				return;
			}
		}
		throw new \DomainException('Comment is not found.');
	}

	public function activateComment($id): void
	{
		$comments = $this->postComments;
		foreach ($comments as $comment) {
			if ($comment->isIdEqualTo($id)) {
				$comment->activate();
				$this->updateComments($comments);
				return;
			}
		}
		throw new \DomainException('Comment is not found.');
	}

	public function removeComment($id): void
	{
		$comments = $this->postComments;
		foreach ($comments as $i => $comment) {
			if ($comment->isIdEqualTo($id)) {
				if ($this->hasChildren($comment->id)) {
					$comment->draft();
				} else {
					unset($comments[$i]);
				}
				$this->updateComments($comments);
				return;
			}
		}
		throw new \DomainException('Comment is not found.');
	}

	public function getComment($id): PostComment
	{
		foreach ($this->postComments as $comment) {
			if ($comment->isIdEqualTo($id)) {
				return $comment;
			}
		}
		throw new \DomainException('Comment is not found.');
	}

	private function hasChildren($id): bool
	{
		foreach ($this->postComments as $comment) {
			if ($comment->isChildOf($id)) {
				return true;
			}
		}
		return false;
	}

	private function updateComments(array $comments): void
	{
		$this->postComments = $comments;
		$this->comments_count = count(array_filter($comments, function (PostComment $comment) {
			return $comment->isActive();
		}));
	}

	######################################

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getPostComments()
    {
        return $this->hasMany(PostComment::class, ['post_id' => 'id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getPostTagAssignments()
    {
        return $this->hasMany(PostTagAssignment::class, ['post_id' => 'id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getTags()
    {
        return $this->hasMany(PostTag::className(), ['id' => 'tag_id'])->viaTable('post_tag_assignments', ['post_id' => 'id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getCategory()
    {
        return $this->hasOne(PostCategory::className(), ['id' => 'category_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getType()
    {
        return $this->hasOne(PostType::className(), ['id' => 'type_id']);
    }

    ######################################

	public function behaviors(): array
	{
		return [
			TimestampBehavior::className(),
			MetaBehavior::className(),
			[
				'class' => SaveRelationsBehavior::className(),
				'relations' => ['postTagAssignments', 'postComments'],
			],
			[
				'class' => ImageUploadBehavior::className(),
				'attribute' => 'image',
				'createThumbsOnRequest' => true,
				//'filePath' => $this::FILE_ORIGINAL_PATH . '/[[id]].[[extension]]',
				'filePath' => '@staticRoot/origin/posts/[[id]].[[extension]]',
				'fileUrl' => '@static/origin/posts/[[id]].[[extension]]',
				'thumbPath' => '@staticRoot/cache/posts/[[profile]]_[[id]].[[extension]]',
				'thumbUrl' => '@static/cache/posts/[[profile]]_[[id]].[[extension]]',
				'thumbs' => [
					'admin' => ['width' => 60, 'height' => 60],
					'thumb' => ['width' => 150, 'height' => 150],
					'list' => ['width' => 200, 'height' => 200],
					'home_slider' => ['width' => 369, 'height' => 343],
					'94_94' => ['width' => 94, 'height' => 94],
					'368_287' => ['width' => 368, 'height' => 287],
					'370_325' => ['width' => 370, 'height' => 325],
					'683_407' => ['width' => 683, 'height' => 407],
					'thumb_gallery_view' => ['width' => 300, 'height' => 170],
					//'widget_list' => ['width' => 228, 'height' => 228],
					//'origin' => ['processor' => [new WaterMarker(1024, 768, '@frontend/web/image/logo.png'), 'process']],
					'origin' => ['width' => 1024, 'height' => 768],
				],
			],
		];
	}

	public function transactions(): array
	{
		return [
			self::SCENARIO_DEFAULT => self::OP_ALL,
		];
	}

	public static function find(): PostQuery
	{
		return new PostQuery(static::class);
	}
}