<?php

/* For licensing terms, see /license.txt */

declare(strict_types=1);

namespace Chamilo\CoreBundle\Entity;

use ApiPlatform\Doctrine\Orm\Filter\ExistsFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use Chamilo\CoreBundle\Controller\Api\DislikeSocialPostController;
use Chamilo\CoreBundle\Controller\Api\LikeSocialPostController;
use Chamilo\CoreBundle\Controller\Api\SocialPostAttachmentsController;
use Chamilo\CoreBundle\Filter\SocialWallFilter;
use Chamilo\CoreBundle\Repository\SocialPostRepository;
use Chamilo\CoreBundle\State\SocialPostStateProvider;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Table(name: 'social_post')]
#[ORM\Index(columns: ['sender_id'], name: 'idx_social_post_sender')]
#[ORM\Index(columns: ['user_receiver_id'], name: 'idx_social_post_user')]
#[ORM\Index(columns: ['group_receiver_id'], name: 'idx_social_post_group')]
#[ORM\Index(columns: ['type'], name: 'idx_social_post_type')]
#[ORM\Entity(repositoryClass: SocialPostRepository::class)]
#[ApiResource(
    operations: [
        new Get(security: "is_granted('VIEW', object)"),
        new Put(security: "is_granted('EDIT', object)"),
        new Delete(security: "is_granted('DELETE', object)"),
        new Post(securityPostDenormalize: "is_granted('CREATE', object)"),
        new Post(
            uriTemplate: '/social_posts/{id}/like',
            controller: LikeSocialPostController::class,
            normalizationContext: ['groups' => ['social_post_feedback']],
            denormalizationContext: ['groups' => []],
            security: "is_granted('ROLE_USER')"
        ),
        new Post(
            uriTemplate: '/social_posts/{id}/dislike',
            controller: DislikeSocialPostController::class,
            normalizationContext: ['groups' => ['social_post_feedback']],
            denormalizationContext: ['groups' => []],
            security: "is_granted('ROLE_USER')"
        ),
        new Get(
            uriTemplate: '/social_posts/{id}/attachments',
            controller: SocialPostAttachmentsController::class,
            normalizationContext: ['groups' => ['attachment:read']],
        ),
        new GetCollection(security: "is_granted('ROLE_USER')"),
    ],
    normalizationContext: ['groups' => ['social_post:read']],
    denormalizationContext: ['groups' => ['social_post:write']],
    security: "is_granted('ROLE_USER')",
    provider: SocialPostStateProvider::class,
)]
#[ApiFilter(filterClass: SearchFilter::class, properties: ['parent' => 'exact', 'type' => 'exact'])]
#[ApiFilter(filterClass: ExistsFilter::class, properties: ['parent'])]
#[ApiFilter(filterClass: OrderFilter::class, properties: ['sendDate'])]
class SocialPost
{
    public const TYPE_WALL_POST = 1;
    public const TYPE_WALL_COMMENT = 2;
    public const TYPE_GROUP_MESSAGE = 3;
    public const TYPE_PROMOTED_MESSAGE = 4;
    public const STATUS_SENT = 1;
    public const STATUS_DELETED = 2;

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(name: 'id', type: 'integer')]
    protected ?int $id = null;

    #[Groups(['social_post:read', 'social_post:write'])]
    #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'sentSocialPosts')]
    #[ORM\JoinColumn(nullable: false)]
    protected User $sender;

    #[Groups(['social_post:read', 'social_post:write'])]
    #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'receivedSocialPosts')]
    #[ORM\JoinColumn(nullable: true)]
    protected ?User $userReceiver;

    #[ORM\Column(name: 'subject', type: 'text', nullable: true)]
    protected ?string $subject = null;

    #[Groups(['social_post:read', 'social_post:write'])]
    #[ORM\Column(type: 'text')]
    protected string $content;

    #[Groups(['social_post:write', 'social_post:read'])]
    #[Assert\Choice([
        self::TYPE_WALL_POST,
        self::TYPE_WALL_COMMENT,
        self::TYPE_GROUP_MESSAGE,
        self::TYPE_PROMOTED_MESSAGE,
    ], message: 'Choose a valid type.')]
    #[ORM\Column(type: 'smallint')]
    protected int $type;

    #[Assert\Choice([self::STATUS_SENT, self::STATUS_DELETED], message: 'Choose a status.')]
    #[ORM\Column(type: 'smallint')]
    protected int $status;

    #[Groups(['social_post:read'])]
    #[ORM\Column(type: 'datetime')]
    protected DateTime $sendDate;

    #[Gedmo\Timestampable(on: 'update')]
    #[ORM\Column(type: 'datetime')]
    protected DateTime $updatedAt;

    #[ORM\OneToMany(targetEntity: SocialPostFeedback::class, mappedBy: 'socialPost')]
    protected Collection $feedbacks;

    #[Groups(['social_post:read', 'social_post:write'])]
    #[ORM\ManyToOne(targetEntity: Usergroup::class)]
    #[ORM\JoinColumn(onDelete: 'CASCADE')]
    protected ?Usergroup $groupReceiver = null;

    #[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')]
    protected Collection $children;

    #[Groups(['social_post:write'])]
    #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
    #[ORM\JoinColumn(name: 'parent_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
    protected ?SocialPost $parent = null;

    #[Groups(['social_post:read', 'social_post_feedback'])]
    protected int $countFeedbackLikes = 0;

    #[Groups(['social_post:read', 'social_post_feedback'])]
    protected int $countFeedbackDislikes = 0;

    #[ApiFilter(filterClass: SocialWallFilter::class)]
    protected User $wallOwner;

    #[ORM\OneToMany(
        targetEntity: SocialPostAttachment::class,
        mappedBy: 'socialPost',
        cascade: ['persist', 'remove'],
        orphanRemoval: true
    )]
    private Collection $attachments;

    public function __construct()
    {
        $this->userReceiver = null;
        $this->groupReceiver = null;
        $this->parent = null;
        $this->sendDate = new DateTime();
        $this->updatedAt = $this->sendDate;
        $this->status = self::STATUS_SENT;
        $this->feedbacks = new ArrayCollection();
        $this->type = self::TYPE_WALL_POST;
        $this->countFeedbackLikes = 0;
        $this->countFeedbackDislikes = 0;
        $this->attachments = new ArrayCollection();
    }

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

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

        return $this;
    }

    public function getSender(): User
    {
        return $this->sender;
    }

    public function setSender(User $sender): self
    {
        $this->sender = $sender;

        return $this;
    }

    public function getUserReceiver(): ?User
    {
        return $this->userReceiver;
    }

    public function setUserReceiver(?User $userReceiver): self
    {
        $this->userReceiver = $userReceiver;

        return $this;
    }

    public function getStatus(): int
    {
        return $this->status;
    }

    public function setStatus(int $status): self
    {
        $this->status = $status;

        return $this;
    }

    public function getSendDate(): DateTime
    {
        return $this->sendDate;
    }

    public function setSendDate(DateTime $sendDate): self
    {
        $this->sendDate = $sendDate;

        return $this;
    }

    public function getSubject(): ?string
    {
        return $this->subject;
    }

    public function setSubject(?string $subject): self
    {
        $this->subject = $subject;

        return $this;
    }

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

    public function setContent(string $content): self
    {
        $this->content = $content;

        return $this;
    }

    public function getUpdatedAt(): DateTime
    {
        return $this->updatedAt;
    }

    public function setUpdatedAt(DateTime $updatedAt): self
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    public function getFeedbacks(): Collection
    {
        return $this->feedbacks;
    }

    public function setFeedbacks(Collection $feedbacks): self
    {
        $this->feedbacks = $feedbacks;

        return $this;
    }

    public function addFeedback(SocialPostFeedback $feedback): self
    {
        if (!$this->feedbacks->contains($feedback)) {
            $this->feedbacks[] = $feedback;
            $feedback->setSocialPost($this);
        }

        return $this;
    }

    public function getCountFeedbackLikes(): int
    {
        $criteria = Criteria::create();
        $criteria->where(Criteria::expr()->eq('liked', true));

        return $this->feedbacks->matching($criteria)->count();
    }

    public function getCountFeedbackDislikes(): int
    {
        $criteria = Criteria::create();
        $criteria->where(Criteria::expr()->eq('disliked', true));

        return $this->feedbacks->matching($criteria)->count();
    }

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

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

        return $this;
    }

    /**
     * @return Collection<int, SocialPost>
     */
    public function getChildren(): Collection
    {
        return $this->children;
    }

    public function addChild(self $child): self
    {
        $this->children[] = $child;
        $child->setParent($this);

        return $this;
    }

    public function getGroupReceiver(): ?Usergroup
    {
        return $this->groupReceiver;
    }

    public function setGroupReceiver(?Usergroup $groupReceiver): self
    {
        $this->groupReceiver = $groupReceiver;

        return $this;
    }

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

    public function setType(int $type): self
    {
        $this->type = $type;

        return $this;
    }

    public function getAttachments(): Collection
    {
        return $this->attachments;
    }

    public function addAttachment(SocialPostAttachment $attachment): self
    {
        if (!$this->attachments->contains($attachment)) {
            $this->attachments[] = $attachment;
            $attachment->setSocialPost($this);
        }

        return $this;
    }
}
