<?php

namespace Velis\Bpm\Ticket\Mixin;

use User\User;
use Velis\App;
use Velis\Bpm\Ticket\Log;

/**
 * Ticket access functions
 * @author Olek Procki <olo@velis.pl>
 */
trait AccessTrait
{
     /**
     * Default other people cases visibility privilege
     * @var array
     */
    protected static $_fullVisibilityPriv = [
        'Ticket',
        'ShowOtherPeopleCases'
    ];

    /**
     * Read by user flag
     * @var bool
     */
    protected bool $_read;


    /**
     * Returns true if user can access ticket
     *
     * @param \Velis\User|int $user
     * @return bool
     */
    public function checkAccess($user = null)
    {
        if ($user == null) {
            $user = App::$user;
        } elseif (!$user instanceof \Velis\User) {
            $user = User::get($user);
        }

        if ($user->id()) {
            // ticket owner can always access ticket
            if ($this->owner_user_id == $user->id()) {
                return true;
            }

            // responsible user is allowed to access ticket
            if ($this->responsible_user_id == $user->id()) {
                return true;
            }
        }

        // user in responsible department is allowed to access ticket
        if ($this->hasField('responsible_department_id') && $this->responsible_department_id) {
            $userDepartment = $user->getDepartment();
            $userDepartmentId = $userDepartment ? $userDepartment->id() : null;

            if ($this->responsible_department_id == $userDepartmentId) {
                return true;
            }
        }

        // any observer is allowed to access ticket
        if ($this->isObserver($user)) {
            return true;
        }

        // substitute user can also access ticket
        if (
            !$this->isFinished()
            && $user->isSubstitute()
            && is_int($this->responsible_user_id) && $user->isSubstituteForUserId($this->responsible_user_id)
            && is_int($this->coordinator_user_id) && $user->isSubstituteForUserId($this->coordinator_user_id)
        ) {
            return true;
        }

        return false;
    }


    /**
     * Marks ticket read/unread
     *
     * @param bool $read
     * @param \Velis\User|int $user
     *
     * @return $this
     */
    public function markRead($read = true, $user = null): static
    {
        if ($user == null) {
            $user = App::$user;
        }
        $params = [
            'user_id' => $user instanceof \Velis\User ? $user->id() : $user,
            'ticket_id' => $this->id(),
        ];
        if ($read) {
            self::$_db->insertDelayed('app.ticket_access_log_tab', $params);
            $this->_read = true;
        } else {
            self::$_db->execDML('
                DELETE FROM app.ticket_access_log_tab
                WHERE
                    ticket_id = :ticket_id
                    AND user_id = :user_id
            ', $params);
        }

        return $this;
    }


    /**
     * Checks if ticket is read by $user
     *
     * @return bool
     */
    public function isRead(): bool
    {
        if (!isset($this->_read)) {
            self::checkReadFlag([$this]);
        }

        return $this->_read;
    }

    /**
     * Return additional condition if setting exists
     * @return string
     */
    protected static function getAdditionalNotesConditions(): string
    {
        return App::$config->settings->unreadOnlyPosts
            ? "AND ticket_log_action_id IN ('AddPost', 'Create') "
            : '';
    }


    /**
     * Checks if tickets are read by user
     *
     * @param \Velis\Bpm\Ticket\Ticket[] $tickets
     * @param \Velis\User|int $user
     */
    public static function checkReadFlag(array $tickets, $user = null)
    {
        if ($user == null) {
            $user = App::$user;
        }
        $params = [
            'user_id' => $user instanceof \Velis\User ? $user->id() : $user,
        ];

        $ticketLogUserColumn = Log::hasField('user_id') ? 'user_id' : 'person_id';

        if (count($tickets)) {
            $idList = self::getCollectionIds($tickets);
            $idListString = implode(', ', $idList);

            if ($user->hasCompany()) {
                $query = "SELECT DISTINCT t.ticket_id FROM (
                          SELECT tl.ticket_id,
                                 max(date_modified) last_update

                              FROM app.ticket_log_tab tl
                              LEFT JOIN app.ticket_post_tab tp USING (ticket_post_id)
                              WHERE tl.ticket_id IN(" . $idListString . ") AND
                              tp.ticket_post_visibility_id = 'Public'
                              " . self::getAdditionalNotesConditions() . "
                              AND tl." . $ticketLogUserColumn . " <> :user_id
                              GROUP BY tl.ticket_id) t

                          WHERE NOT EXISTS (
                              SELECT 1 FROM app.ticket_access_log_tab tal
                              WHERE tal.ticket_id    = t.ticket_id
                                AND tal.user_id      =:user_id
                                AND tal.date_entered > t.last_update
                            )
                        UNION ALL
                            SELECT ticket_id FROM UNNEST(ARRAY[" . $idListString . "] ) v(ticket_id)
                            WHERE NOT EXISTS (
                                SELECT 1 FROM app.ticket_access_log_tab tal
                                WHERE tal.ticket_id = v.ticket_id
                                AND tal.user_id = :user_id
                            );
                        ";
            } else {
                $query = "SELECT DISTINCT t.ticket_id FROM (
                          SELECT ticket_id,
                                 max(date_modified) last_update

                              FROM app.ticket_log_tab
                              WHERE ticket_id IN(" . $idListString . ")
                              " . self::getAdditionalNotesConditions() . "
                              AND " . $ticketLogUserColumn . " <> :user_id
                              GROUP BY ticket_id) t

                          WHERE NOT EXISTS (
                              SELECT 1 FROM app.ticket_access_log_tab tal
                              WHERE tal.ticket_id    = t.ticket_id
                                AND tal.user_id      =:user_id
                                AND tal.date_entered > t.last_update
                            )
                        UNION ALL
                            SELECT ticket_id FROM UNNEST(ARRAY[" . $idListString . "] ) v(ticket_id)
                            WHERE NOT EXISTS (
                                SELECT 1 FROM app.ticket_access_log_tab tal
                                WHERE tal.ticket_id = v.ticket_id
                                AND tal.user_id = :user_id
                            );
                        ";
            }



            $readTickets = self::$_db->getAll($query, $params, 'ticket_id');

            foreach ($tickets as $ticket) {
                if (in_array($ticket->id(), array_keys($readTickets))) {
                    $ticket->_read = false;
                } else {
                    $ticket->_read = true;
                }
            }
        }
    }

    /**
     * Returns default other people cases visibility privilege
     * @return array
     */
    public static function getFullVisibilityPriv()
    {
        return self::$_fullVisibilityPriv;
    }
}
