<?php

namespace Velis\Bpm\Ticket\Mixin;

use ReflectionException;
use Velis\App;
use Velis\Arrays;
use Velis\Bpm\Ticket\Log;
use Velis\Bpm\Ticket\Post;
use Velis\Bpm\Ticket\Post\File;
use Velis\Model\DataObject\NoColumnsException;

/**
 * Ticket posts & history trait
 * @author Olek Procki <olo@velis.pl>
 */
trait HistoryTrait
{
    /**
     * Ticket posts
     * @var Post[]
     */
    protected $_posts;


    /**
     * Merged changelog & posts
     * @var Log[]
     */
    protected $_history;


    /**
     * Files
     * @var File[]
     */
    protected $_files;


    /**
     * Ticket changes log
     * @var Log[]
     */
    protected array $_log = [];

    /**
     * Log entry of last executed action
     * @var Log|null
     */
    private ?Log $firstLog = null;

    /**
     * Returns posts
     *
     * @param string|array $type
     * @return array
     * @throws ReflectionException
     */
    public function getPosts($type = null)
    {
        if (!isset($this->_posts)) {
            $params = ['ticket_id' => $this->id()];
            $this->_posts = Post::listAll($params, 'date_entered DESC');
            $this->loadFiles();
        }

        if ($type != null) {
            $postsFiltered = [];
            foreach ($this->_posts as $post) {
                if (
                    (!is_array($type) && $post['ticket_post_visibility_id'] == $type)
                    || (is_array($type) && in_array($post['ticket_post_visibility_id'], $type))
                ) {
                    $postsFiltered[$post->id()] = $post;
                }
            }
            return $postsFiltered;
        } else {
            return $this->_posts;
        }
    }


    /**
     * Returns full ticket history (changes & posts)
     *
     * @param array|string|null $visibility
     * @param array $params
     * @return Log[]
     * @throws ReflectionException
     * @throws NoColumnsException
     */
    public function getHistory($visibility = null, $params = [])
    {
        if (!isset($this->_history)) {
            $this->_history = Log::getTicketHistory($this, true, $params);
        }

        if (null == $visibility) {
            return $this->_history;
        }

        $filteredHistory = [];

        if (!is_array($visibility)) {
            $visibility = [$visibility];
        }

        foreach ($this->_history as $entry) {
            if (in_array($entry['ticket_post_visibility_id'], $visibility)) {
                $filteredHistory[$entry->id()] = $entry;
            }
        }

        return $filteredHistory;
    }


    /**
     * Load posts files
     * @throws ReflectionException
     */
    public function loadFiles()
    {
        if (!isset($this->_posts)) {
            $this->_posts = $this->getPosts();
        }
        if (!isset($this->_files)) {
            $this->_files = Post::loadPostsFiles($this->_posts);
        }
    }


    /**
     * Sets loaded files
     *
     * @param File[] $files
     */
    public function setFiles($files)
    {
        $this->_files = $files;
    }


    /**
     * Returns all attached files
     *
     * @param bool $orderByVersion
     * @return File[]
     * @throws ReflectionException
     * @throws NoColumnsException
     */
    public function getFiles($orderByVersion = true)
    {
        if (!isset($this->_files)) {
            $this->loadFiles();
        }

        if (App::$user->hasCompany()) {
            foreach ($this->_files as $key => $file) {
                if (!$file->getPost()->isPublic()) {
                    unset($this->_files[$key]);

                    $files = $this->_files;

                    $filesByVersion = [];

                    // search for other versions
                    foreach ($files as $versionFile) {
                        if ($versionFile->getParentId() == $file->getParentId() || $versionFile->id() == $file->getParentId()) {
                            $filesByVersion[$versionFile->getVersion()] = $versionFile;
                        }
                    }
                    // sort files by version descending just in case
                    krsort($filesByVersion);

                    $newCurrent = reset($filesByVersion);
                    if ($newCurrent['ticket_post_file_id']) {
                        $this->_files[$newCurrent['ticket_post_file_id']]['version_is_current'] = 1;
                    }
                }
            }
        }

        if (!$orderByVersion) {
            return $this->_files;
        } else {
            $files = [];

            $currentFiles = $this->getCurrentFiles();
            krsort($currentFiles);

            foreach ($currentFiles as $currentFile) {
                $files[$currentFile->id()] = $currentFile;

                if ($currentFile->getParentId()) {
                    $filesByVersion = [];

                    // search for other versions
                    foreach ($this->_files as $file) {
                        if ($file->getParentId() == $currentFile->getParentId() || $file->id() == $currentFile->getParentId()) {
                            $filesByVersion[$file->getVersion()] = $file;
                        }
                    }
                    // sort files by version descending just in case
                    krsort($filesByVersion);

                    foreach ($filesByVersion as $file) {
                        $files[$file->id()] = $file;
                    }
                }
            }
            return $files;
        }
    }


    /**
     * @param bool $onlyVersioned
     * @return File[]
     * @throws ReflectionException
     * @throws NoColumnsException
     */
    public function getCurrentFiles($onlyVersioned = false)
    {
        $files = [];
        foreach ($this->getFiles(false) as $file) {
            if ($file->version_is_current || (!strlen($file->version_is_current) && !$onlyVersioned)) {
                $files[$file->id()] = $file;
            }
        }

        return $files;
    }


    /**
     * Returns ticket's log of changes
     * @return Log[]
     * @throws ReflectionException
     */
    public function getChangelog(): array
    {
        if (empty($this->_log)) {
            $this->_log = Log::listAll([
                'ticket_id' => $this->id(),
            ], 'ticket_log_id ASC');
        }

        return $this->_log;
    }

    /**
     * Get first log entry from ticket history
     * @return Log
     */
    public function getFirstLog(): Log
    {
        if (!$this->firstLog) {
            $this->firstLog = Arrays::getFirst(Log::getList(null, $this->_getPrimaryKeyParam(), 'ticket_log_id', 1));
            $this->firstLog['direction'] = '>';
            $this->firstLog->setTicket($this);
        }

        return $this->firstLog;
    }
}
