<?php
/**
 * Class represents records from table access
 * {autogenerated}
 * @property int $access_id PK
 * @property int|null $invoice_id FK invoice
 * @property string|null $invoice_public_id
 * @property int|null $invoice_payment_id FK invoice_payment
 * @property int|null $invoice_item_id FK invoice_item
 * @property int $user_id FK user
 * @property int $product_id FK product
 * @property string|null $transaction_id
 * @property date $begin_date
 * @property date $expire_date
 * @property int $qty
 * @property string|null $comment
 * {/autogenerated}
 */
class Access extends Am_Record_WithData
{
    protected $_disableHooks = false;

    const EXPIRED = 0;
    const ACTIVE = 1;
    const FUTURE = 2;

    function isLifetime()
    {
        return $this->expire_date >= Am_Period::RECURRING_SQL_DATE
            || $this->expire_date >= Am_Period::MAX_SQL_DATE;
    }

    /**
     * @return int status constant
     */
    function getStatus()
    {
        $now = sqlDate('now');
        if ($this->expire_date < $now) return self::EXPIRED;
        if ($this->begin_date > $now) return self::FUTURE;
        return self::ACTIVE;
    }

    /**
     * Disable any external hooks calling during this record add/delete/update
     * Do just SQL staff if true
     */
    function setDisableHooks($flag = true)
    {
        $this->_disableHooks = (bool)$flag;
    }

    protected function runHooks()
    {
        if (!$this->_disableHooks)
            $this->getDi()->userTable->load($this->user_id)->checkSubscriptions(true);
    }

    public function insert($reload = true)
    {
        $this->getDi()->hook->call(Am_Event::ACCESS_BEFORE_INSERT, ['access' => $this]);
        parent::insert($reload);
        $this->runHooks();
        $this->getDi()->hook->call(Am_Event::ACCESS_AFTER_INSERT, ['access' =>$this]);
        return $this;
    }

    public function delete()
    {
        parent::delete();
        $this->runHooks();
        $this->getDi()->hook->call(Am_Event::ACCESS_AFTER_DELETE, ['access' =>$this]);
        return $this;
    }

    public function update()
    {
        $hm = $this->getDi()->hook;
        if ($hm->have(Am_Event::ACCESS_AFTER_UPDATE)) {
            $old = $this->getTable()->load($this->pk());
        }
        parent::update();
        $this->runHooks();
        if ($hm->have(Am_Event::ACCESS_AFTER_UPDATE)) {
            $this->getDi()->hook->call(Am_Event::ACCESS_AFTER_UPDATE, ['access' => $this, 'old' => $old]);
        }
        return $this;
    }

    public function getInvoice(): ?Invoice
    {
        return $this->invoice_id ? $this->getDi()->invoiceTable->load($this->invoice_id) : null;
    }

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

    public function getProduct(): Product
    {
        return $this->getDi()->productTable->load($this->product_id);
    }
}

/**
 * @extends Am_Table_WithData<Access>
 * @method void deleteByInvoiceItemId($value, $useObjects = true)
 * @method void deleteByProductId($value, $useObjects = true)
 * @method void deleteByUserId($value, $useObjects = true)
 * @method Access[] findByInvoiceId($value, $start = null, $count = null, $orderBy = null)
 * @method Access[] findByUserId($value, $start = null, $count = null, $orderBy = null)
 * @method Access|null findFirstByInvoiceId($value)
 * @method Access|null findFirstByInvoiceItemId($value)
 */
class AccessTable extends Am_Table_WithData
{
    protected $_key = 'access_id';
    protected $_table = '?_access';
    protected $_recordClass = 'Access';

    /*
     * Return status of member subscriptions
     * according to access table:
     * @return array product_id => status (1 if active, 2 if expired)
     */
    function getStatusByUserId($user_id, $statusFilter = DBSIMPLE_SKIP)
    {
        return $this->_db->selectCol(
           "SELECT
               product_id as ARRAY_KEY,
               MIN(IF(expire_date<?,2,1)) as status
            FROM ?_access
            WHERE user_id=?d AND begin_date <= ?
            GROUP BY product_id
            {HAVING status = ?}
            ", $this->getDi()->sqlDate, $user_id, $this->getDi()->sqlDate, $statusFilter);
    }

    /**
     * As new recurring payment added, we will set existing recurring
     * records to "expire" at yesterday
     * No hook calls required as new subscription will be added instead
     */
    function setDateForRecurring(InvoiceItem $item, $date)
    {
        $this->_db->query("UPDATE ?_access
            SET expire_date=?
            WHERE invoice_id=?d AND product_id=?d AND expire_date IN (?,?)",
            $date,
            $item->invoice_id, $item->item_id,
                Am_Period::MAX_SQL_DATE, Am_Period::RECURRING_SQL_DATE
            );
    }

    function getLastExpire(array $options)
    {
        return $this->_db->selectCell("SELECT MAX(expire_date) FROM ?_access
            WHERE user_id=?d
                { AND (
                    ?d = -1 #fake condition for DBSIMPLE_SKIP
                    { OR (product_id=?d) }
                    { OR (product_id IN (SELECT product_id FROM ?_product WHERE renewal_group=?) ) }
                ) }
            ",
            $options['user_id'],
            (empty($options['product_id'])&&empty($options['renewal_group']))?DBSIMPLE_SKIP:1,
            !empty($options['product_id']) ? $options['product_id'] : DBSIMPLE_SKIP,
            !empty($options['renewal_group']) ? $options['renewal_group'] : DBSIMPLE_SKIP
            );
    }

    function queryExpirations(array $dates = [])
    {
        $where = "";
        if ($dates) {
            foreach ($dates as &$d) {
                $d = "'$d'";
            }
            $where .= sprintf(" AND expire_date IN (%s)", join(',', $dates));
        }
        return $this->_db->queryResultOnly("
                SELECT
                        DISTINCT(CONCAT(u.user_id,'-',a.expire_date, '-',a.product_id)),
                        u.*,
                        a.product_id AS _product_id,
                        a.expire_date AS _expire_date,
                        a.invoice_id AS _invoice_id,
                        IF(i.status = ?, 1, 0) AS _recurring,
                        IF(i.status = ?, 1, 0) AS _cancelled
                FROM
                    (SELECT
                            a.user_id,
                            CONCAT(user_id, '-',IF(IFNULL(p.renewal_group, '')<>'', p.renewal_group, CONCAT('-PRODUCT-', p.product_id)))
                                AS usergroup,
                            GROUP_CONCAT(DISTINCT ':', p.product_id, ':')
                                AS product_ids,
                            MAX(expire_date)
                                AS max_expire
                        FROM ?_access a LEFT JOIN ?_product p USING( product_id ) GROUP BY usergroup
                     ) AS aa
                     LEFT JOIN ?_access a ON a.user_id = aa.user_id
                        AND a.expire_date = aa.max_expire
                        AND LOCATE(CONCAT(':', a.product_id, ':'), product_ids) > 0
                     LEFT JOIN ?_user u ON u.user_id = a.user_id
                     LEFT JOIN ?_invoice i ON a.invoice_id = i.invoice_id
                WHERE
                     1 $where
                ORDER BY u.user_id
            ", Invoice::RECURRING_ACTIVE, Invoice::RECURRING_CANCELLED);
    }
}