<?php

/**
 * Class represents records from table helpdesk_ticket
 * {autogenerated}
 * @property int $ticket_id PK
 * @property string $ticket_mask
 * @property int $user_id FK user
 * @property int|null $owner_id
 * @property string|null $watcher_ids
 * @property int|null $category_id FK helpdesk_category
 * @property string $status 'new'|'awaiting_user_response'|'awaiting_admin_response'|'closed'
 * @property string $subject
 * @property datetime $created
 * @property datetime $updated
 * @property int|null $lock_admin_id
 * @property string|null $lock_admin
 * @property datetime|null $lock_until
 * @property int $has_new
 * {/autogenerated}
 */
class HelpdeskTicket extends Am_Record_WithData
{
    const STATUS_NEW = 'new';
    const STATUS_AWAITING_USER_RESPONSE = 'awaiting_user_response';
    const STATUS_AWAITING_ADMIN_RESPONSE = 'awaiting_admin_response';
    const STATUS_CLOSED = 'closed';

    public static function getStatusOptions()
    {
        return [
            'new' => ___('New'),
            'awaiting_admin_response' => ___('Awaiting Admin Response'),
            'awaiting_user_response' => ___('Awaiting User Response'),
            'closed' => ___('Closed')
        ];
    }

    public function getMessages()
    {
        return $this->getDi()->helpdeskMessageTable->findBy(
            ['ticket_id' => $this->pk()],
            null, null, "dattm DESC");
    }

    /**
     * @return User
     */
    public function getUser()
    {
        return $this->getDi()->userTable->load($this->user_id);
    }

    /**
     * @return Admin
     */
    public function getOwner()
    {
        return $this->owner_id ? $this->getDi()->adminTable->load($this->owner_id, false) : null;
    }

    public function getWatchers()
    {
        $res = [];
        foreach (explode(',', $this->watcher_ids) as $id) {
            if ($id && $admin = $this->getDi()->adminTable->load($id, false)) {
                $res[] = $admin;
            }
        }
        return $res;
    }

    /**
     * @return HelpdeskCategory
     */
    public function getCategory()
    {
        return $this->category_id ? $this->getDi()->helpdeskCategoryTable->load($this->category_id, false) : null;
    }

    public function isLocked(Admin $admin)
    {
        return $this->lock_until &&
        ($this->lock_until > sqlTime('now')) &&
        ($this->lock_admin_id != $admin->pk());
    }

    public function lock(Admin $admin)
    {
        $this->updateQuick([
            'lock_until' => sqlTime('+2 minutes'),
            'lock_admin_id' => $admin->pk(),
            'lock_admin' => $admin->getName() ? sprintf('%s (%s)', $admin->login, $admin->getName()) : $admin->login,
        ]);
    }

    protected function _generateMask()
    {
        if (!empty($this->ticket_mask))
            return;
        $this->ticket_mask =
            $this->getDi()->security->randomString(3, 'ABCDEFGJIKLMNOPQRSTUVWXYZ')
            . '-' . rand(100000, 999999);
    }

    /**
     * before record insertion generate new ticket mask. if the generated mask is not unique,
     * try 20 times to generate new mask, then throw exception
     * @return type
     */
    public function insert($reload = true)
    {
        $event = new Am_Event(Bootstrap_Helpdesk::EVENT_TICKET_BEFORE_INSERT, ['ticket' => $this]);
        $this->getDi()->hook->call($event);

        $maxAttempts = 20;
        for ($i = 0; $i <= $maxAttempts; $i++) {
            try {
                $this->_generateMask();
                parent::insert($reload);
                $event = new Am_Event(Bootstrap_Helpdesk::EVENT_TICKET_AFTER_INSERT, ['ticket' => $this]);
                $this->getDi()->hook->call($event);
                return $this;
            } catch (Am_Exception_Db_NotUnique $e) {
                if ($i >= $maxAttempts)
                    throw new Am_Exception_InternalError("Could not generate new ticket mask after [$i] attempts");
                $this->ticket_mask = null;
            }
        }
    }

    function update()
    {
        $old = $this->getTable()->load($this->pk(), false);
        $old->toggleFrozen(true);
        $_ = parent::update();
        $event = new Am_Event(Bootstrap_Helpdesk::EVENT_TICKET_AFTER_UPDATE, [
            'ticket' => $this,
            'oldTicket' => $old,
        ]);
        $this->getDi()->hook->call($event);
        return $_;
    }

    public function delete()
    {
        foreach ($this->getMessages() as $msg) {
            $msg->delete();
        }
        parent::delete();
        $event = new Am_Event(Bootstrap_Helpdesk::EVENT_TICKET_AFTER_DELETE, ['ticket' => $this]);
        $this->getDi()->hook->call($event);
    }
}

/**
 * @extends Am_Table_WithData<HelpdeskTicket>
 * @method int countByStatus($value)
 * @method int countByUserId($value)
 * @method HelpdeskTicket|null findFirstByTicketMask($value)
 */
class HelpdeskTicketTable extends Am_Table_WithData
{
    protected $_key = 'ticket_id';
    protected $_table = '?_helpdesk_ticket';
    protected $_recordClass = 'HelpdeskTicket';

    public function insert(array $values, $returnInserted = false)
    {
        if (empty($values['created']))
            $values['created'] = $this->getDi()->sqlDateTime;
        return parent::insert($values, $returnInserted);
    }

    public function load($keyOrTicketId, $throwExceptions = true)
    {
        if (preg_match('/^\d+$/', trim($keyOrTicketId), $matches)) {
            return parent::load($matches[0], $throwExceptions);
        } else {
            $keyOrTicketId = filterId($keyOrTicketId);
            $found = $this->findFirstByTicketMask($keyOrTicketId);
            if (!$found && $throwExceptions)
                throw new Am_Exception_InternalError("Ticket with mask [$keyOrTicketId] not found");
            return $found;
        }
    }

    public function clearOld($date)
    {
        foreach ($this->selectObjects("SELECT * FROM {$this->_table} WHERE updated<?", $date) as $ticket) {
            $ticket->delete();
        }
    }
}