<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\TicketBundle\Entity;

use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserGroup;
use Chill\PersonBundle\Entity\Person;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * This entity represents a `Ticket` in the application and provides functionality for managing its associated
 * histories, comments, and current data such as addressees, inputs, and motives.
 *
 * Most of the associated data are handle through "histories", with a many-to-one relationship (a ticket may have multiple)
 * history. The history contains the associated data (see below) and start dates and end dates.
 *
 * There are histories for:
 *
 * - association between the ticket and persons: @see{PersonHistory};
 * - association between the ticket and motive: @see{MotiveHistory};
 * - association between the ticket and addresses: @see{AddresseeHistory};
 * - association between the ticket and input: @see{InputHistory};
 * - association between the ticket and state: @see{StateHistory};
 */
#[ORM\Entity]
#[ORM\Table(name: 'ticket', schema: 'chill_ticket')]
class Ticket implements TrackCreationInterface, TrackUpdateInterface
{
    use TrackCreationTrait;
    use TrackUpdateTrait;

    #[ORM\Id]
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
    #[ORM\GeneratedValue(strategy: 'AUTO')]
    private ?int $id = null;

    /**
     * @var Collection<int, AddresseeHistory>
     */
    #[ORM\OneToMany(targetEntity: AddresseeHistory::class, mappedBy: 'ticket')]
    private Collection $addresseeHistory;

    /**
     * @var Collection<int, Comment>
     */
    #[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'ticket')]
    private Collection $comments;

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
    private string $externalRef = '';

    /**
     * @var Collection<int, InputHistory>
     */
    #[ORM\OneToMany(targetEntity: InputHistory::class, mappedBy: 'ticket')]
    private Collection $inputHistories;

    /**
     * @var Collection<int, MotiveHistory>
     */
    #[ORM\OneToMany(targetEntity: MotiveHistory::class, mappedBy: 'ticket')]
    private Collection $motiveHistories;

    /**
     * @var Collection<int, PersonHistory>
     */
    #[ORM\OneToMany(targetEntity: PersonHistory::class, mappedBy: 'ticket')]
    private Collection $personHistories;

    /**
     * @var Collection<int, StateHistory>
     */
    #[ORM\OneToMany(targetEntity: StateHistory::class, mappedBy: 'ticket', cascade: ['persist', 'remove'])]
    private Collection $stateHistories;

    /**
     * @var Collection<int, EmergencyStatusHistory>
     */
    #[ORM\OneToMany(targetEntity: EmergencyStatusHistory::class, mappedBy: 'ticket', cascade: ['persist', 'remove'])]
    private Collection $emergencyStatusHistories;

    /**
     * @var Collection<int, CallerHistory>
     */
    #[ORM\OneToMany(targetEntity: CallerHistory::class, mappedBy: 'ticket', cascade: ['persist', 'remove'])]
    private Collection $callerHistories;

    public function __construct()
    {
        $this->addresseeHistory = new ArrayCollection();
        $this->comments = new ArrayCollection();
        $this->motiveHistories = new ArrayCollection();
        $this->personHistories = new ArrayCollection();
        $this->inputHistories = new ArrayCollection();
        $this->stateHistories = new ArrayCollection();
        $this->emergencyStatusHistories = new ArrayCollection();
        $this->callerHistories = new ArrayCollection();
    }

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

    public function getExternalRef(): string
    {
        return $this->externalRef;
    }

    public function setExternalRef(string $externalRef): void
    {
        $this->externalRef = $externalRef;
    }

    /**
     * @return list<Person>
     */
    public function getPersons(): array
    {
        return $this->personHistories
            ->filter(fn (PersonHistory $personHistory) => null === $personHistory->getEndDate())
            ->map(fn (PersonHistory $personHistory) => $personHistory->getPerson())
            ->getValues();
    }

    /**
     * @internal use @see{Comment::__construct} instead
     */
    public function addComment(Comment $comment): void
    {
        $this->comments->add($comment);
    }

    /**
     * Add a PersonHistory.
     *
     * @internal use @see{PersonHistory::__construct} instead
     */
    public function addPersonHistory(PersonHistory $personHistory): void
    {
        $this->personHistories->add($personHistory);
    }

    /**
     * @internal use @see{MotiveHistory::__construct} instead
     */
    public function addMotiveHistory(MotiveHistory $motiveHistory): void
    {
        $this->motiveHistories->add($motiveHistory);
    }

    /**
     * @internal use @see{AddresseHistory::__construct} instead
     */
    public function addAddresseeHistory(AddresseeHistory $addresseeHistory): void
    {
        $this->addresseeHistory->add($addresseeHistory);
    }

    /**
     * @return list<UserGroup|User>
     */
    public function getCurrentAddressee(): array
    {
        $addresses = [];

        foreach ($this->addresseeHistory
            ->filter(fn (AddresseeHistory $addresseeHistory) => null === $addresseeHistory->getEndDate()) as $addressHistory) {
            $addresses[] = $addressHistory->getAddressee();
        }

        return $addresses;
    }

    /**
     * @return ReadableCollection<int, Comment>
     */
    public function getComments(): ReadableCollection
    {
        return $this->comments;
    }

    /**
     * @return list<ThirdParty|Person>
     */
    public function getCurrentInputs(): array
    {
        $inputs = [];

        foreach ($this->inputHistories
            ->filter(fn (InputHistory $inputHistory) => null === $inputHistory->getEndDate()) as $inputHistory
        ) {
            $inputs[] = $inputHistory->getInput();
        }

        return $inputs;
    }

    public function getMotive(): ?Motive
    {
        foreach ($this->motiveHistories as $motiveHistory) {
            if (null === $motiveHistory->getEndDate()) {
                return $motiveHistory->getMotive();
            }
        }

        return null;
    }

    /**
     * @return ReadableCollection<int, MotiveHistory>
     */
    public function getMotiveHistories(): ReadableCollection
    {
        return $this->motiveHistories;
    }

    /**
     * @return ReadableCollection<int, PersonHistory>
     */
    public function getPersonHistories(): ReadableCollection
    {
        return $this->personHistories;
    }

    /**
     * @return ReadableCollection<int, AddresseeHistory>
     */
    public function getAddresseeHistories(): ReadableCollection
    {
        return $this->addresseeHistory;
    }

    /**
     * @internal use @see{StateHistory::__construct} instead
     */
    public function addStateHistory(StateHistory $stateHistory): void
    {
        $this->stateHistories->add($stateHistory);
    }

    public function getState(): ?StateEnum
    {
        foreach ($this->stateHistories as $stateHistory) {
            if (null === $stateHistory->getEndDate()) {
                return $stateHistory->getState();
            }
        }

        return null;
    }

    /**
     * @return ReadableCollection<int, StateHistory>
     */
    public function getStateHistories(): ReadableCollection
    {
        return $this->stateHistories;
    }

    /**
     * @internal use @see{EmergencyStatusHistory::__construct} instead
     */
    public function addEmergencyStatusHistory(EmergencyStatusHistory $emergencyStatusHistory): void
    {
        $this->emergencyStatusHistories->add($emergencyStatusHistory);
    }

    public function getEmergencyStatus(): ?EmergencyStatusEnum
    {
        foreach ($this->emergencyStatusHistories as $emergencyStatusHistory) {
            if (null === $emergencyStatusHistory->getEndDate()) {
                return $emergencyStatusHistory->getEmergencyStatus();
            }
        }

        return null;
    }

    /**
     * @return ReadableCollection<int, EmergencyStatusHistory>
     */
    public function getEmergencyStatusHistories(): ReadableCollection
    {
        return $this->emergencyStatusHistories;
    }

    /**
     * @internal use @see{CallerHistory::__construct} instead
     */
    public function addCallerHistory(CallerHistory $callerHistory): void
    {
        $this->callerHistories->add($callerHistory);
    }

    /**
     * Get the current caller (Person or ThirdParty) associated with this ticket.
     *
     * @return Person|ThirdParty|null
     */
    public function getCaller()
    {
        foreach ($this->callerHistories as $callerHistory) {
            if (null === $callerHistory->getEndDate()) {
                return $callerHistory->getCaller();
            }
        }

        return null;
    }

    /**
     * @return ReadableCollection<int, CallerHistory>
     */
    public function getCallerHistories(): ReadableCollection
    {
        return $this->callerHistories;
    }
}
