<?php
/**
 * Class represents records from table newsletter_user_subscription
 * {autogenerated}
 * @property int $subscription_id
 * @property int $user_id
 * @property int $list_id
 * @property string $type enum('auto','user','unsubscribed')
 * @property int $is_active
 * @see Am_Table
 */
class NewsletterUserSubscription extends Am_Record
{
    /** user subscribed automatically to this subscription */
    const TYPE_AUTO = 'auto';
    /** user specially agreed to receive this list */
    const TYPE_USER = 'user';
    /** user specially decied to unsubscribe from this list */
    const TYPE_UNSUBSCRIBED = 'unsubscribed';

    protected $_user;

    function enable($type = null, $throwExceptions = false)
    {
        if ($type !== null)
            $this->updateQuick('type', $type);
        try {
            if (!$this->is_active) {
                if (!$this->getList()->getPlugin()->changeSubscription($this->getUser(),
                    [$this->getList()->plugin_list_id], [],
                    (!empty($this->begin_date) ?
                        [
                            $this->getList()->plugin_list_id => [
                                'begin_date' => $this->begin_date,
                                'expire_date' => $this->expire_date
                            ]
                        ] : []))) {
                    return $this; // do not update if call failed (!)
                }
            }
        } catch (Exception $e) {
            $this->getDi()->logger->error(
                "EXCEPTION during subscribe user {user_id} to list {list_id}",
                ['user_id' => $this->user_id, 'list_id' => $this->list_id]
            );
            $this->getDi()->logger->error($e);

            if ($throwExceptions)
                throw $e;

            return $this;
        }
        $this->updateQuick('is_active', 1);
        return $this;
    }

    function disable($throwExceptions=false)
    {
        try {
            if ($this->is_active) {
                try {
                    $pl = $this->getList()->getPlugin();
                } catch (Exception $e) {
                    //plugin was disabled
                    $this->updateQuick('is_active', 0);
                    return $this;
                }
                if (!$pl->changeSubscription($this->getUser(), [], [$this->getList()->plugin_list_id]))
                    return $this; // do not update active flag if failed (!)
            }
        } catch (Exception $e) {
            $this->getDi()->logger->error(
                "EXCEPTION during unsubscribe user {user_id} from list {list_id}",
                [
                    'user_id' => $this->user_id,
                    'list_id' => $this->list_id
                ]
            );
            $this->getDi()->logger->error($e);

            if ($throwExceptions)
                throw $e;

            return $this;
        }
        $this->updateQuick('is_active', 0);

        try {
            $this->getDi()->hook->call(
                Bootstrap_Newsletter::NEWSLETTER_LIST_SUBSCRIPTION_REMOVED,
                ['user' => $this->getUser(), 'list' => $this->getList()]
            );
        } catch(Am_Exception_Db_NotFound $e) {
            $this->getDi()->errorLogTable->logException($e);
        }
        return $this;
    }

    /**
     * @return NewsletterList
     */
    function getList()
    {
        return $this->getDi()->newsletterListTable->load($this->list_id);
    }

    function getUser()
    {
        if (!$this->_user)
            $this->_user = $this->getDi()->userTable->load($this->user_id);
        return $this->_user;
    }

    function _setUser(User $user)
    {
        $this->_user = $user;
    }

    function unsubscribe($throwExceptions=false)
    {
        $this->disable($throwExceptions);
        $this->updateQuick('type', self::TYPE_UNSUBSCRIBED);
        return $this;
    }

    function isUnsubscribed()
    {
        return $this->type == self::TYPE_UNSUBSCRIBED;
    }

    function delete($throwExceptions = false)
    {
        $this->disable($throwExceptions);
        parent::delete();
    }
}

/**
 * @extends Am_Table<NewsletterUserSubscription>
 * @method void deleteByListId($value, $useObjects = true)
 * @method void deleteByUserId($value, $useObjects = true)
 * @method NewsletterUserSubscription[] findByUserId($value, $start = null, $count = null, $orderBy = null)
 */
class NewsletterUserSubscriptionTable extends Am_Table
{
    protected $_key = 'subscription_id';
    protected $_table = '?_newsletter_user_subscription';

    /**
     * Add or enable subscription
     */
    function add(User $user, NewsletterList $list, $type)
    {
        $list_id = $list->pk();
        $user_id = $user->pk();
        $sub = $this->findFirstBy([
            'user_id' => $user_id,
            'list_id' => $list_id,
        ]);
        if (!$sub) {
            $sub = $this->createRecord();
            $sub->user_id = $user_id;
            $sub->list_id = $list_id;
            $sub->is_active = 0;
            $sub->insert();
        }
        if (!empty($list->begin_date)) {
            $sub->begin_date = $list->begin_date;
            $sub->expire_date = $list->expire_date;
        }
        $sub->enable($type);
        return $sub;
    }

    function checkSubscriptions(User $user)
    {
        if (!empty($user->unsubscribed)) {
            foreach ($this->findByUserId($user->pk()) as $sub) {
                $sub->unsubscribe();
            }
            return;
        }

        // array of NewsletterList user has access at all
        $ids = [];

        foreach ($this->getDi()->newsletterListTable->getAllowed($user) as $list) {
            $ids[$list->pk()] = ['l' => $list];
        }
        foreach ($this->findByUserId($user->pk()) as $r) {
            $ids[$r->list_id]['s'] = $r;
        }

        ////
        foreach ($ids as $list_id => $record)
        {
            /* @var $availableList NewsletterList */
            if (empty($record['l'])) $record['l'] = null;
            if (empty($record['s'])) $record['s'] = null;
            $availableList = $record['l'];
            /* @var $existingSub NewsletterUserSubscription */
            $existingSub = $record['s'];

            if ($existingSub) {
                if (!$availableList) {
                    // if not available now, de-activate
                    $existingSub->disable();
                    continue;
                } elseif ($existingSub->isUnsubscribed()) {
                    // if user unsubscribed previously, make sure it disabled
                    $existingSub->disable();
                    continue;
                } else  { // status = user or status = auto
                    // if user was subscribed before, enable it
                    $existingSub->enable();
                }
            } elseif ($user->data()->get(Bootstrap_Newsletter::NEWSLETTER_SIGNUP_DATA) == 1) {
                $this->add($user, $availableList, NewsletterUserSubscription::TYPE_USER);
            } elseif (
                ($data = $user->data()->get(Bootstrap_Newsletter::NEWSLETTER_SIGNUP_DATA))
                && !empty($data[$availableList->pk()])
            ) {
                $this->add($user, $availableList, NewsletterUserSubscription::TYPE_USER);
            } elseif (
                $availableList
                && $availableList->auto_subscribe
            ) { // no existing list - create if 'auto'
                $this->add($user, $availableList, NewsletterUserSubscription::TYPE_AUTO);
            }
        }
    }

    /**
     * Return ids of lists user is enabled to receive, even if currently subscription is not available
     * (say subscription expired)
     */
    function getSubscribedIds($user_id)
    {
        return $this->_db->selectCol(
            "SELECT list_id FROM $this->_table WHERE user_id=?d AND type <> ? AND is_active > 0",
            $user_id,
            NewsletterUserSubscription::TYPE_UNSUBSCRIBED
        );
    }

    /**
     * Update list of subscriptions by admin - it does not check any permissions
     * immediately, but subscriptions may be deleted during @link checkSubscriptions()
     */
    function adminSetIds($user_id, array $ids)
    {
        $user = $this->getDi()->userTable->load($user_id);
        $ids = array_filter(array_map('intval', $ids));
        $setSubs = [];
        foreach ($ids as $i) {
            if ($i > 0) {
                $setSubs[(int)$i] = [];
            }
        }

        $enabled = $disabled = [];
        foreach ($this->findByUserId($user_id) as $r) {
            if ($r->is_active) {
                $enabled[$r->list_id] = $r;
            } else {
                $disabled[$r->list_id] = $r;
            }
        }

        foreach (array_diff_key($enabled, $setSubs, $disabled) as $sub) {
            if (!$sub->is_active) continue;
            $sub->_setUser($user);
            $sub->delete();
        }
        //
        foreach (array_diff_key($setSubs, $enabled) as $list_id => $sub) {
            $list = $this->getDi()->newsletterListTable->load($list_id);
            $this->add($user, $list, NewsletterUserSubscription::TYPE_AUTO);
        }
        // enable disable subscriptions
        foreach (array_intersect_key($disabled, $setSubs) as $sub) {
            $sub->enable();
        }
    }
}