<?php

namespace Velis\Bpm\Ticket\Mixin;

use Velis\Arrays;
use Velis\Bpm\Ticket\Ticket;
use Velis\Exception;
use Velis\Exception\BusinessLogicException;
use Velis\Lang;

/**
 * Linked tickets functionality
 * @author Olek Procki <olo@velis.pl>
 */
trait LinkTrait
{
    /**
     * Lined tickets
     * @var Ticket[]
     */
    protected $_linkedTickets;

    /**
     * Add link to other ticket
     *
     * @param Ticket|int $linkTicket
     * @return bool
     *
     * @throws BusinessLogicException
     */
    public function addLink($linkTicket)
    {
        $params = [
            'ticket_from_id' => $this->id(),
            'ticket_to_id' => $linkTicket instanceof self ? $linkTicket->id() : $linkTicket,
        ];

        if ($params['ticket_to_id'] == $this->id()) {
            throw new BusinessLogicException(Lang::get('TICKET_LINK_MYSELF'));
        }

        return self::$_db->insert('app.ticket_link_tab', $params);
    }


    /**
     * Add linked ticket to $_linkedTickets array
     *
     * @param Ticket $linkTicket
     * @return $this
     */
    public function addLinkedTicket($linkTicket)
    {
        if ($linkTicket instanceof self) {
            $this->_linkedTickets[$linkTicket->id()] = $linkTicket;
        }

        return $this;
    }


    /**
     * Add link to other ticket
     *
     * @param Ticket|int $linkTicket
     * @return void
     */
    public function removeLink($linkTicket)
    {
        $linkTicketId = $linkTicket instanceof self ? $linkTicket->id() : $linkTicket;

        $query = 'DELETE FROM app.ticket_link_tab WHERE ticket_from_id = :ticketFromId AND ticket_to_id = :ticketToId';
        self::$_db->execDML($query, ['ticketFromId' => $this->id(), 'ticketToId' => $linkTicketId]);
        self::$_db->execDML($query, ['ticketFromId' => $linkTicketId,  'ticketToId' => $this->id()]);
    }


    /**
     * Loads linked tickets id list
     * @return int[]
     * @throws Exception
     */
    protected function _loadLinkTickets()
    {
        $result = self::$_db->getAll('
            SELECT DISTINCT
                (CASE WHEN ticket_from_id = :ticket_id THEN ticket_to_id ELSE ticket_from_id END) linked_ticket_id
            FROM app.ticket_link_tab
            WHERE
                ticket_from_id = :ticket_id
                OR ticket_to_id = :ticket_id
        ', [
            'ticket_id' => $this->id(),
        ]);

        return Arrays::getColumn($result, 'linked_ticket_id');
    }


    /**
     * Returns linked tickets
     *
     * @param bool              $checkPrivs Hide tickets for which the current user has no privs
     * @param array|string|bool $fields
     * @return Ticket|Ticket[]
     * @throws Exception
     */
    public function getLinkedTickets($checkPrivs = true, $fields = false)
    {
        if (!isset($this->_linkedTickets)) {
            if ($this->offsetExists('ticket_link_ids')) {
                $idList = $this->fieldToArray('ticket_link_ids');
            } else {
                $idList = $this->_loadLinkTickets();
            }

            if ($idList) {
                $fields = $fields ?: $this->getLinkedTicketFields();
                if ($fields) {
                    $linkedTickets = Ticket::instance($idList, $fields);
                } else {
                    $linkedTickets = Ticket::instance($idList);
                }

                if ($checkPrivs) {
                    foreach ($linkedTickets as $linkedTicket) {
                        if (!$linkedTicket->checkAccess()) {
                            unset($linkedTickets[$linkedTicket->id()]);
                        }
                    }
                }

                krsort($linkedTickets);
            } else {
                $linkedTickets = [];
            }

            $this->_linkedTickets = $linkedTickets;
        }

        return $this->_linkedTickets;
    }

    /**
     * In order to optimize query for linked tickets, you can specify fields that will be loaded
     * @return array|null
     */
    protected function getLinkedTicketFields(): ?array
    {
        return null;
    }
}
