<?php

/**
 * @am_plugin_api 6.0
*/
class Am_Plugin_GiftVouchers extends Am_Plugin
{
    const PLUGIN_STATUS = self::STATUS_PRODUCTION;
    const PLUGIN_REVISION = '6.3.43';
    const PERM = 'gift-vouchers';
    protected $_configPrefix = 'misc.';

    static function getDbXml()
    {
        return <<<CUT
<schema version="4.0.0">
    <table name="store_gift_vouchers">
        <field name="store_gift_vouchers_id" type="int" notnull="1" extra="auto_increment"/>
        <field name="product_ids" type="varchar" len="255"/>
        <field name="bp_ids" type="varchar" len="255"/>
        <field name="invoice_id_grantor" type="int"/>
        <field name="invoice_id_donee" type="int"/>
        <field name="voucher_email" type="varchar" len="255"/>
        <field name="voucher_name" type="varchar" len="255"/>
        <field name="voucher_comment" type="text"/>
        <field name="voucher_date" type="date"/>
        <field name="voucher_code" type="varchar" len="255"/>
        <field name="voucher_status" type="tinyint" notnull="1"/>
        <field name="voucher_expire" type="date"/>
        <field name="grantor_email" type="varchar" len="255"/>
        <field name="grantor_namef" type="varchar" len="255"/>
        <field name="grantor_namel" type="varchar" len="255"/>
        <index name="PRIMARY" unique="1">
            <field name="store_gift_vouchers_id" />
        </index>
    </table>
</schema>
CUT;
    }

    static function getEtXml()
    {
        return <<<CUT
<table_data name="email_template">
    <row type="email_template">
        <field name="name">misc.gift-vouchers.email_donee</field>
        <field name="lang">en</field>
        <field name="format">text</field>
        <field name="subject">%site_title%: Gift Voucher from %gv_grantor_namef% %gv_grantor_namel%</field>
        <field name="txt">
** Gift Voucher from %gv_grantor_email% **

Hello %gv_donee_name%,

%gv_grantor_namef% %gv_grantor_namel% sent you a gift voucher for product(s): %gv_product_title% at site:
%root_url%/%gv_signup_page%?giftVoucherCode=%gv_code%

VOUCHER CODE: %gv_code%

------ MESSAGE FROM GRANTOR ------
%gv_comment%
----------------------------------

Thank you for your time!

--
Best Regards,
%site_title%
%root_url%
        </field>
    </row>
    <row type="email_template">
        <field name="name">misc.gift-vouchers.email_grantor</field>
        <field name="email_template_layout_id">1</field>
        <field name="lang">en</field>
        <field name="format">text</field>
        <field name="subject">%site_title%: Gift Voucher Information</field>
        <field name="txt">
** You sent a gift voucher to %gv_donee_email% **

Hello %gv_grantor_namef% %gv_grantor_namel%,

You ordered a gift voucher for %gv_donee_email% for product(s): %gv_product_title%.
It will be sent to receiver on %gv_date|date%.

------ MESSAGE FROM GRANTOR ------
%gv_comment%
----------------------------------

Thank you for your order!
        </field>
    </row>
</table_data>
CUT;
    }

    function onGetPermissionsList(Am_Event $e)
    {
        $e->addReturn(___('Gift Voucher'), self::PERM);
    }

    protected function getVoucherCode()
    {
        $attempt = 0;
        $length = $this->getConfig('lengthVoucherCode', 8);
        do {
            if ($attempt > 2) {
                $attempt = 0;
                $length++;
            }
            $code = strtoupper($this->getDi()->security->randomString($length));
            $attempt++;
        } while ($this->getDi()->giftVoucherTable->findFirstByVoucherCode($code));

        return $code;
    }

    protected function getGiftFormUrl()
    {
        $signup_form_id = $this->getConfig('gvSignupPage', 'signup');
        if (!is_numeric($signup_form_id))
            return $signup_form_id; //backward compatibility

        try {
            if ($form = $this->getDi()->savedFormTable->load($signup_form_id, false))
                return $form->getUrl("");
            else
                return 'signup';
        } catch (Am_Exception $e) {
            return 'signup'; //last chance
        }
    }

    public function sendEmailGV($to, $grantor, $voucher, $prIds = [])
    {
        $template = Am_Mail_Template::load('misc.gift-vouchers.email_' . $to);
        if ($to == 'donee') {
            $template->setGv_grantor_email($grantor['email']);
            $template->setGv_donee_name($voucher['name']);

            $template->setGv_signup_page($this->getGiftFormUrl());
            $template->setGv_code($voucher['code']);
            $email = $voucher['email'];
        } elseif ($to == 'grantor') {
            $template->setGv_donee_email($voucher['email']);
            $template->setGv_date($voucher['date']);
            $template->setGv_code($voucher['code']);
            $email = $grantor['email'];
        }
        $template->setGv_grantor_namef($grantor['namef']);
        $template->setGv_grantor_namel($grantor['namel']);
        $template->setGv_comment($voucher['comment']);
        if (!empty($prIds)) {
            $template->setGv_product_title(implode(', ', $this->getDi()->productTable->getProductTitles($prIds)));
        }
        $template->send($email);
    }

    public function onAdminMenu(Am_Event $event)
    {
        $event->getMenu()->addPage([
            'id' => 'giftvouchers',
            'module' => 'default',
            'controller' => 'admin-gift-vouchers',
            'action' => 'index',
            'label' => ___('Gift Vouchers'),
            'resource' => self::PERM,
            'order' => 100,
        ]);
    }

    function onSetupEmailTemplateTypes(Am_Event $event)
    {
        $event->addReturn([
            'id' => 'misc.gift-vouchers.email_donee',
            'title' => 'Donee\'s message',
            'mailPeriodic' => Am_Mail::USER_REQUESTED,
            'vars' => [
                'gv_grantor_email' => "grantor's email",
                'gv_grantor_namef' => "grantor's first name",
                'gv_grantor_namel' => "grantor's last name",
                'gv_donee_name' => "donee's name",
                'gv_signup_page' => "path to signup form for enter code on your site",
                'gv_code' => "coupon code",
                'gv_comment' => "grantor's comment",
                'gv_product_title' => "gift product(s) title"
            ],
        ], 'misc.gift-vouchers.email_donee');

        $event->addReturn([
            'id' => 'misc.gift-vouchers.email_grantor',
            'title' => 'Grantor\'s message',
            'mailPeriodic' => Am_Mail::USER_REQUESTED,
            'vars' => [
                'gv_donee_email' => "donee's email",
                'gv_grantor_namef' => "grantor's first name",
                'gv_grantor_namel' => "grantor's last name",
                'gv_date' => "date of email sending",
                'gv_code' => "coupon code",
                'gv_comment' => "grantor's comment",
                'gv_product_title' => "gift product(s) title"
            ],
        ], 'misc.gift-vouchers.email_grantor');
    }

    function getTitle()
    {
        return 'Gift Vouchers';
    }

    public function _initSetupForm(Am_Form_Setup $form)
    {
        $signupForms = $this->getDi()->savedFormTable->getOptions(SavedForm::D_SIGNUP);

        $form->addSelect('gvSignupPage')
            ->setLabel('Signup page for use gift voucher')
            ->loadOptions($signupForms);

        $grPeriod = $form->addGroup()
                ->setSeparator(' ')
                ->setLabel(___('Gift Voucher Period Of Validity After Creation'));
        $grPeriod->addText('voucherPeriod', ['size' => 5])
            ->setId('voucher-period');
        $grPeriod->addSelect('voucherPeriodType')
            ->setId('voucher-period-type')
            ->loadOptions([
                'day' => ___('Days'),
                'month' => ___('Months'),
                'year' => ___('Years'),
                'lifetime' => ___('Lifetime')
            ]);

        $form->addScript()
            ->setScript(<<<CUT
jQuery(function(){
    jQuery('#voucher-period-type').change(function(){
        jQuery('#voucher-period').toggle(jQuery(this).val() != 'lifetime');
        jQuery('#voucher-period').val(jQuery(this).val() == 'lifetime' ? '' : jQuery('#voucher-period').val());
    })
    if (jQuery('#voucher-period').val() == '') {
        jQuery('#voucher-period-type').val('lifetime');
        jQuery('#voucher-period').hide();
    }
})
CUT
        );

        $form->addText('lengthVoucherCode', ['size' => 5, 'placeholder' => 8])
            ->setLabel(___('Voucher Code Length'));

        $form->addElement('email_link', 'email_donee')
            ->setLabel(___('Email Template For Donee'));

        $gr = $form->addGroup()
                ->setSeparator(' ')
                ->setLabel(___('Is Send Email to Grantor?'));

        $gr->addAdvCheckbox('isSendEmailGrantor');
        $gr->addElement('email_link', 'email_grantor')
            ->setLabel(___('Email Template For Grantor'));
    }

    public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
    {
        $giftVoucher = null;
        if ($code = $request->getParam('code')) {
            $giftVoucher = $this->getDi()->giftVoucherTable->findFirstByVoucherCode($code);
        }
        return $response->ajaxResponse($giftVoucher ? ['product_ids' => $giftVoucher->product_ids] : []);
    }

    function onSignupInvoiceItems(Am_Event $e)
    {
        /** @var Invoice $invoice */
        $invoice = $e->getInvoice();
        if (
            ($code = $invoice->data()->get('giftVoucherCode'))
            && ($voucher = $this->getDi()->giftVoucherTable->findFirstByVoucherCode($code))
            && is_null($voucher->validateVoucher())
            && $voucher->bp_ids
        ) {
            //guarantee we have correct items
            //signup form can have products with different plans

            foreach ($invoice->getItems() as $item) {
                $invoice->deleteItem($item);
            }

            foreach (explode(',', $voucher->bp_ids) as $bp_id) {
                /** @var BillingPlan $bp */
                $bp = $this->getDi()->billingPlanTable->load($bp_id);
                $invoice->add($bp->getProduct());
            }
        }
    }

    public function onInvoiceGetCalculators(Am_Event_InvoiceGetCalculators $e)
    {
        $invoice = $e->getInvoice();
        if ($invoice->data()->get('giftVoucherCode')) {
            $e->insertBeforeTax(new Am_Invoice_Calc_GiftVoucher);
            foreach ($e->getReturn() as $calc) {
                if ($calc instanceof Am_Invoice_Calc_Tax) {
                    $e->remove($calc);
                }
            }
        }
    }

    function onInvoiceBeforePayment(Am_Event $e)
    {
        /** @var Invoice $invoice */
        $invoice = $e->getInvoice();
        if ($invoice->data()->get('isGiftVoucher')) {
            foreach ($invoice->getItems() as $item) {
                $item->data()->set('no-access', true)->update();
            }
        }
    }

    public function onInvoiceStarted(Am_Event_InvoiceStarted $e)
    {
        /* @var $invoice Invoice */
        $invoice = $e->getInvoice();
        $user = $invoice->getUser();

        if ($invoice->data()->get('isGiftVoucher')) {
            $productsIds = [];
            $bpIds = [];
            foreach ($this->getDi()->invoiceItemTable->findByInvoiceId($invoice->invoice_id) as $value) {
                $productsIds[] = $value->item_id;
                $bpIds[] = $value->billing_plan_id;
            }

            $giftVoucherRecord = $this->getDi()->giftVoucherRecord;

            $giftVoucherRecord->product_ids = implode(',', $productsIds);
            $giftVoucherRecord->bp_ids = implode(',', $bpIds);
            $giftVoucherRecord->invoice_id_grantor = $invoice->pk();
            $giftVoucherRecord->voucher_email = $invoice->data()->get('voucherEmail');
            $giftVoucherRecord->voucher_name = $invoice->data()->get('voucherName');
            $giftVoucherRecord->voucher_comment = $invoice->data()->get('voucherComment');
            $giftVoucherRecord->voucher_date = $invoice->data()->get('voucherDate');
            $giftVoucherRecord->voucher_code = $this->getVoucherCode();
            $giftVoucherRecord->voucher_expire = ($this->getConfig('voucherPeriod') && $this->getConfig('voucherPeriodType')) ?
                sqlDate(strtotime('+' . $this->getConfig('voucherPeriod') . ' ' . $this->getConfig('voucherPeriodType'), $this->getDi()->time)) :
                Am_Period::MAX_SQL_DATE;
            $giftVoucherRecord->grantor_email = $user->email;
            $giftVoucherRecord->grantor_namef = $user->name_f;
            $giftVoucherRecord->grantor_namel = $user->name_l;

            $giftVoucherRecord->save();

            if ($giftVoucherRecord->voucher_date <= $this->getDi()->sqlDate) {
                $this->sendEmailGV(
                    'donee',
                    [// grantor's data
                        'email' => $giftVoucherRecord->grantor_email,
                        'namef' => $giftVoucherRecord->grantor_namef,
                        'namel' => $giftVoucherRecord->grantor_namel,
                    ],
                    [// voucher data
                        'email' => $giftVoucherRecord->voucher_email,
                        'name' => $giftVoucherRecord->voucher_name,
                        'code' => $giftVoucherRecord->voucher_code,
                        'comment' => $giftVoucherRecord->voucher_comment
                    ],
                    $productsIds
                );
                $giftVoucherRecord->voucher_status = GiftVoucher::STATUS_SENT;
                $giftVoucherRecord->update();
            }
            // send email to grantor?
            if ($this->getConfig('isSendEmailGrantor')) {
                $this->sendEmailGV(
                    'grantor',
                    [// grantor's data
                        'email' => $giftVoucherRecord->grantor_email,
                        'namef' => $giftVoucherRecord->grantor_namef,
                        'namel' => $giftVoucherRecord->grantor_namel,
                    ],
                    [// voucher data
                        'email' => $giftVoucherRecord->voucher_email,
                        'date' => $giftVoucherRecord->voucher_date,
                        'code' => $giftVoucherRecord->voucher_code,
                        'comment' => $giftVoucherRecord->voucher_comment
                    ],
                    $productsIds
                );
            }

            // delete saved gift data
            $invoice->data()->set('isGiftVoucher', null);
            $invoice->data()->set('voucherEmail', null);
            $invoice->data()->set('voucherName', null);
            $invoice->data()->set('voucherDate', null);
            $invoice->data()->set('voucherComment', null);
            $invoice->data()->update();
        }

        if ($giftVoucherCode = $invoice->data()->get('giftVoucherCode')) {
            $giftVoucher = $this->getDi()->giftVoucherTable->findFirstByVoucherCode($giftVoucherCode);
            $giftVoucher->updateQuick('voucher_status', GiftVoucher::STATUS_USED);
            $giftVoucher->updateQuick('invoice_id_donee', $invoice->pk());
        }
    }

    function onRefundAfterInsert(Am_Event $e)
    {
        /** @var Invoice $inv */
        $inv = $e->getInvoice();
        /** @var InvoiceRefund $r */
        $r = $e->getRefund();
        if (!$this->isFull($inv, $r)) return; //partial refund, admin should deal with access records manually

        if ($ids = array_map(
            function($_){return $_->invoice_item_id;},
            $this->getDi()->invoiceItemTable->findByData('gift_invoice_id', $inv->pk())
        )) {
            $this->getDi()->accessTable->deleteByInvoiceItemId($ids);
        }
    }

    function isFull(Invoice $inv, InvoiceRefund $r)
    {
        return abs($inv->first_total-$r->amount) < 0.1;
    }

    public function onDaily(Am_Event $event)
    {
        $giftVoucher = $this->getDi()->giftVoucherTable->findBy([
                ['voucher_date', '<=', $event->getDate()],
                'voucher_status' => GiftVoucher::STATUS_PENDING
        ]);

        foreach ($giftVoucher as $record) {
            $this->sendEmailGV(
                'donee',
                [// grantor's data
                    'email' => $record->grantor_email,
                    'namef' => $record->grantor_namef,
                    'namel' => $record->grantor_namel,
                ],
                [// voucher data
                    'email' => $record->voucher_email,
                    'name' => $record->voucher_name,
                    'code' => $record->voucher_code,
                    'comment' => $record->voucher_comment
                ],
                explode(',', $record->product_ids)
            );
            $record->voucher_status = max($record->voucher_status, GiftVoucher::STATUS_SENT);
            $record->update();
        }
    }
}

/**
 * @extends Am_Table<GiftVoucher>
 * @method GiftVoucher|null findFirstByVoucherCode($value)
 */
class GiftVoucherTable extends Am_Table
{
    protected $_table = '?_store_gift_vouchers';
    protected $_recordClass = 'GiftVoucher';
    protected $_key = 'store_gift_vouchers_id';
}

class GiftVoucher extends Am_Record
{
    const STATUS_PENDING = 0;
    const STATUS_SENT = 1;
    const STATUS_USED = 2;

    public function validateVoucher()
    {
        if ($this->voucher_status == GiftVoucher::STATUS_PENDING)
            return ___('Voucher is not yet active');
        if ($this->voucher_status == GiftVoucher::STATUS_USED)
            return ___('Voucher is already used');
        if ($this->voucher_expire < $this->getDi()->sqlDate)
            return ___('Voucher is expired');
        return null;
    }
}

class Am_Invoice_Calc_GiftVoucher extends Am_Invoice_Calc
{
    function calculate(Invoice $invoice)
    {
        if (!$giftVoucherCode = $invoice->data()->get('giftVoucherCode'))
            return;

        $giftVoucher = Am_Di::getInstance()->giftVoucherTable->findFirstByVoucherCode($giftVoucherCode);
        if (!$giftVoucher || !is_null($giftVoucher->validateVoucher()))
            return;

        $productIds = explode(',', $giftVoucher->product_ids);
        $bpIds = explode(',', $giftVoucher->bp_ids);
        $field = $bpIds ? 'billing_plan_id' : 'item_id';
        $ids = $bpIds ?: $productIds;
        foreach ($invoice->getItems() as $item) {
            if ($item->item_type == 'product' && in_array($item->{$field}, $ids)) {
                $item->first_discount = $item->first_total;
                $item->first_total = 0.0;
                $item->data()->set('gift_invoice_id', $giftVoucher->invoice_id_grantor);
            }
        }
    }
}

class AdminGiftVouchersController extends Am_Mvc_Controller_Grid
{
    public function checkAdminPermissions(Admin $admin)
    {
        return $admin->hasPermission(Am_Plugin_GiftVouchers::PERM);
    }

    public function createGrid()
    {
        $ds = new Am_Query($this->getDi()->giftVoucherTable);
        $ds->leftJoin('?_invoice', 'ig', 't.invoice_id_grantor = ig.invoice_id')
            ->leftJoin('?_user', 'ug', 'ig.user_id=ug.user_id')
            ->leftJoin('?_invoice', 'id', 't.invoice_id_donee = id.invoice_id')
            ->leftJoin('?_user', 'ud', 'id.user_id=ud.user_id')
            ->addField('ig.invoice_id', 'g_invoice_id')
            ->addField('ig.public_id', 'g_public_id')
            ->addField('ug.user_id', 'user_id_grantor')
            ->addField('ug.name_f', 'g_name_f')
            ->addField('ug.name_l', 'g_name_l')
            ->addField('ug.login', 'g_login')
            ->addField('id.invoice_id', 'd_invoice_id')
            ->addField('id.public_id', 'd_public_id')
            ->addField('ud.user_id', 'user_id_donee')
            ->addField('ud.name_f', 'd_name_f')
            ->addField('ud.name_l', 'd_name_l')
            ->addField('ud.login', 'd_login')
            ->setOrder('store_gift_vouchers_id', 'desc');

        $grid = new Am_Grid_Editable('_giftvouchers', ___("Gift Vouchers"), $ds, $this->_request, $this->view);
        $grid->setPermissionId(Am_Plugin_GiftVouchers::PERM);
        $grid->actionsClear();
        $grid->actionAdd(new Am_Grid_Action_SendGiftVoucher)
            ->setIsAvailableCallback(function($r) {return $r->voucher_status != GiftVoucher::STATUS_USED;});
        $grid->actionAdd(new Am_Grid_Action_GiftVoucherMarkUsed)
            ->setIsAvailableCallback(function($r) {return $r->voucher_status != GiftVoucher::STATUS_USED;});

        $grid->addField('store_gift_vouchers_id', '#');
        $grid->addField('product_ids', ___('Products'), false, '', [$this, 'renderProducts']);
        $grid->addField('_grantor', ___('Grantor'), false)
            ->setRenderFunction([$this, 'renderGrantor']);
        $grid->addField('invoice_id_grantor', ___("Grantor's Invoice"), false)
            ->setGetFunction([$this, '_getInvoiceGrantorNum'])
            ->addDecorator(new Am_Grid_Field_Decorator_Link(
                    'admin-user-payments/index/user_id/{user_id_grantor}#invoice-{invoice_id_grantor}', '_top'));
        $grid->addField('_donee', ___('Donee'), false)
            ->setRenderFunction([$this, 'renderDonee']);
        $grid->addField('invoice_id_donee', ___("Donee's Invoice"), false)
            ->setGetFunction([$this, '_getInvoiceDoneeNum'])
            ->addDecorator(new Am_Grid_Field_Decorator_Link(
                    'admin-user-payments/index/user_id/{user_id_donee}#invoice-{invoice_id_donee}', '_top'));
        $grid->addField('voucher_code', ___('Code'), false);
        $grid->addField('voucher_status', ___('Status'), false, '', [$this, 'renderStatus']);
        $grid->addField(new Am_Grid_Field_Date('voucher_date', ___('Sending')))->setFormatDate();
        $grid->addField(new Am_Grid_Field_Date('voucher_expire', ___('Expire')))->setFormatDate();
        $grid->addField(new Am_Grid_Field_Expandable('voucher_comment', ___('Comment'), false))
            ->setPlaceholder(Am_Grid_Field_Expandable::PLACEHOLDER_SELF_TRUNCATE_END);

        $grid->addCallback(Am_Grid_ReadOnly::CB_TR_ATTRIBS, [$this, 'getTrAttr']);
        $grid->setFilter(new Am_Grid_Filter_Text(___('Search by Voucher Code'), ['voucher_code' => 'LIKE']));

        return $grid;
    }

    function getTrAttr(& $ret, $record)
    {
        if ($record->voucher_status == GiftVoucher::STATUS_USED) {
            $ret['class'] = isset($ret['class']) ? $ret['class'] . ' disabled' : 'disabled';
        }
    }

    function _getInvoiceGrantorNum(Am_Record $invoice)
    {
        return $invoice->g_invoice_id . '/' . $invoice->g_public_id;
    }

    function _getInvoiceDoneeNum(Am_Record $invoice)
    {
        return $invoice->d_invoice_id ?
            $invoice->d_invoice_id . '/' . $invoice->d_public_id :
            null;
    }

    function renderGrantor($record)
    {
        $s = sprintf('<a href="%s" target="_top">%s %s (%s)</a>',
                $this->view->escape($this->view->userUrl($record->user_id_grantor)),
                $this->view->escape($record->g_name_f),
                $this->view->escape($record->g_name_l),
                $this->view->escape($record->g_login));

        return $this->renderTd($s, false);
    }

    function renderDonee($record)
    {
        $s = $record->user_id_donee ?
            sprintf('<a href="%s" target="_top">%s %s (%s)</a>',
                $this->view->escape($this->view->userUrl($record->user_id_donee)),
                $this->view->escape($record->d_name_f),
                $this->view->escape($record->d_name_l),
                $this->view->escape($record->d_login)) :
            sprintf('%s (%s)',
                $this->view->escape($record->voucher_name),
                $this->view->escape($record->voucher_email));

        return $this->renderTd($s, false);
    }

    function renderStatus($record)
    {
        $exp = ($record->voucher_expire < $this->getDi()->sqlDate) ? ' / <span class="red">' . ___('Expired') . '</span>' : '';
        switch ($record->voucher_status) {
            case GiftVoucher::STATUS_PENDING:
                $status = '<em>' . ___('Not Send') . '</em>';
                break;
            case GiftVoucher::STATUS_SENT:
                $status = '<strong>' . ___('Not Used') . '</strong>';
                break;
            case GiftVoucher::STATUS_USED:
                $status = ___('Used');
                $exp = '';
                break;
        }
        return sprintf('<td>%s</td>', $status . $exp);
    }

    function renderProducts($record)
    {
        $titles = $this->getDi()->productTable->getProductTitles(explode(',', $record->product_ids));
        return sprintf('<td>%s</td>', Am_Html::escape(implode(', ', $titles)));
    }
}

class Am_Grid_Action_SendGiftVoucher extends Am_Grid_Action_Abstract
{
    protected $type = self::SINGLE;

    public function __construct($id = null, $title = null)
    {
        $this->title = ___('Send Gift Voucher');
        $this->attributes['data-confirm'] = ___("Do you really want to send gift voucher?");
        parent::__construct($id, $title);
    }

    public function run()
    {
        if ($this->grid->getRequest()->get('confirm')) {
            return $this->send();
        } else {
            echo $this->renderConfirmation();
        }
    }

    protected function send()
    {
        $record = $this->grid->getRecord();
        $this->grid->getDi()->plugins_misc->loadGet('gift-vouchers')
            ->sendEmailGV('donee',
                [
                    'email' => $record->grantor_email,
                    'namef' => $record->grantor_namef,
                    'namel' => $record->grantor_namel,
                ],
                [
                    'email' => $record->voucher_email,
                    'name' => $record->voucher_name,
                    'code' => $record->voucher_code,
                    'comment' => $record->voucher_comment
                ],
                explode(',', $record->product_ids)
            );
        $record->voucher_status = max($record->voucher_status, GiftVoucher::STATUS_SENT);
        $record->voucher_date = null;
        $record->update();
        $this->log();
        $this->grid->redirectBack();
    }
}

class Am_Grid_Action_GiftVoucherMarkUsed extends Am_Grid_Action_Abstract
{
    protected $type = self::SINGLE;

    public function __construct($id = null, $title = null)
    {
        $this->title = ___('Mark Used');
        $this->attributes['data-confirm'] = ___("Do you really want to mark this gift voucher as used?");
        parent::__construct($id, $title);
    }

    public function run()
    {
        if ($this->grid->getRequest()->get('confirm')) {
            return $this->_do();
        } else {
            echo $this->renderConfirmation();
        }
    }

    protected function _do()
    {
        $record = $this->grid->getRecord();
        $record->voucher_status = GiftVoucher::STATUS_USED;
        $record->voucher_date = null;
        $record->update();
        $this->log();
        $this->grid->redirectBack();
    }
}

Am_Di::getInstance()->hook->add(Am_Event::LOAD_BRICKS, function(Am_Event $e) {

    class Am_Form_Brick_GiftVoucherGrantor extends Am_Form_Brick
    {
        protected static $brickPosition = 900;
        protected $hideIfLoggedInPossible = self::HIDE_DONT;
        protected $labels = [
            "Is Gift Voucher?\ncheck it if you want to gift subscription on this product",
            "Send Voucher To\nafter completing order, voucher will be sent to specified address if not specified, voucher will be sent to your email",
            'Invalid Email Address',
            "Insert comment into email\nemail will contain this sentence",
            'Donee name',
            'Is Gift Voucher?',
            "Is Send At Later Date?",
        ];

        public function __construct($id = null, $config = null)
        {
            $this->name = ___("Gift Voucher Grantor");
            parent::__construct($id, $config);
        }

        public function init()
        {
            Am_Di::getInstance()->hook->prepend(Am_Event::INVOICE_BEFORE_PAYMENT_SIGNUP, [$this, 'saveGiftData']);
        }

        public function initConfigForm(Am_Form $form)
        {
            $form->addAdvCheckbox('always_gift')
                ->setLabel(___('Enable Gift Option By Default'));
        }

        public function saveGiftData(Am_Event $event)
        {
            $invoice = $event->getInvoice();
            $vars = $event->getVars();

            if (!$this->getConfig('always_gift') && (!isset($vars['isGiftVoucher']) || !$vars['isGiftVoucher']))
                return;

            if (isset($vars['isSendLater']) &&
                $vars['isSendLater'] &&
                $vars['voucherDate']) {

                $date = $vars['voucherDate'];
            } else {
                $date = Am_Di::getInstance()->sqlDate;
            }

            $email = ($vars['voucherEmail']) ? $vars['voucherEmail'] : $invoice->getUser()->email;

            $invoice->data()->set('isGiftVoucher', 1);
            $invoice->data()->set('voucherEmail', $email);
            $invoice->data()->set('voucherDate', $date);
            $invoice->data()->set('voucherComment', $vars['voucherComment']);
            $invoice->data()->set('voucherName', $vars['voucherName']);
            $invoice->comment = 'gift voucher for ' . $email;
            foreach ($invoice->getItems() as $item) {
                $item->data()->set('no-access', true);
            }
            $invoice->save();
        }

        public function isAcceptableForForm(Am_Form_Bricked $form)
        {
            return $form instanceof Am_Form_Signup;
        }

        public function insertBrick(HTML_QuickForm2_Container $form)
        {
            $alwaysGift = $this->getConfig('always_gift', 0);
            $form->addScript()->setScript(<<<CUT
    jQuery(document).ready(function($){
        function showGiftFields(flag){
            jQuery('#isGiftVoucher-0').prop('checked',flag);
            if (flag){
                jQuery('#row-voucherEmail-0').show();
                jQuery('#row-voucherComment-0').show();
                jQuery('#row-voucherName-0').show();
                jQuery('#isSendLater-0').closest('.am-row').show();
                if (jQuery('#isSendLater-0').is(':checked'))
                    jQuery('#voucherDate-0').show();
            } else {
                jQuery('#row-voucherEmail-0').hide();
                jQuery('#row-voucherComment-0').hide();
                jQuery('#row-voucherName-0').hide();
                jQuery('#isSendLater-0').closest('.am-row').hide();
                jQuery('#voucherDate-0').hide();
            }
        }
        if ($alwaysGift) {
            showGiftFields(true);
        }
        function showDateField(flag){
            jQuery('#isSendLater-0').attr('checked',flag);
            if (flag){
                jQuery('#voucherDate-0').show();
            } else {
                jQuery('#voucherDate-0').hide();
            }
        }
        jQuery('#isGiftVoucher-0').change(function(){
            showGiftFields(jQuery(this).is(':checked'));
        }).change();
        jQuery('#isSendLater-0').change(function(){
            showDateField(jQuery(this).is(':checked'));
        }).change();
    });
CUT
            );

            if (!$alwaysGift) {
                $form->addAdvCheckbox('isGiftVoucher')
                    ->setLabel($this->___("Is Gift Voucher?\ncheck it if you want to gift subscription on this product"));
            }
            $form->addText('voucherEmail')
                ->setLabel($this->___("Send Voucher To\n"
                    . "after completing order, voucher will be sent to specified address "
                    . "if not specified, voucher will be sent to your email"
                ))
                ->addRule('email', ___('Invalid Email Address'), null, HTML_QuickForm2_Rule::ONBLUR_CLIENT);
            $form->addText('voucherName')
                ->setLabel($this->___("Donee name"));
            $form->addTextarea('voucherComment', ['rows' => 5, 'class'=>'am-el-wide'])
                ->setLabel($this->___("Insert comment into email\nemail will contain this sentence"));

            $gr = $form->addGroup()
                ->setSeparator(' ')
                ->setLabel($this->___("Is Send At Later Date?"));

            $gr->addAdvCheckbox('isSendLater');
            $gr->addDate('voucherDate');
        }
    }

    class Am_Form_Brick_GiftVoucherDonee extends Am_Form_Brick
    {
        protected $labels = [
            'Enter gift voucher code',
            'No gift vouchers found with such code',
            'Please enter gift voucher code',
            'Gift Voucher'
        ];
        protected $hideIfLoggedInPossible = self::HIDE_DONT;

        public function __construct($id = null, $config = null)
        {
            $this->name = ___("Gift Voucher Donee");
            parent::__construct($id, $config);
        }

        public function init()
        {
            Am_Di::getInstance()->hook->add(Am_Event::INVOICE_SIGNUP, [$this, 'setGiftData']);
        }

        public function setGiftData(Am_Event $event)
        {
            $invoice = $event->getInvoice();
            $vars = $event->getVars();

            if (isset($vars['giftVoucherCode']) && $vars['giftVoucherCode']) {
                if (!$giftVoucher = Am_Di::getInstance()->giftVoucherTable->findFirstByVoucherCode($vars['giftVoucherCode']))
                    return;

                $invoice->data()->set('giftVoucherCode', $vars['giftVoucherCode']);
                $invoice->comment = 'gift voucher from: ' . $giftVoucher->grantor_email . ', code: ' . $giftVoucher->voucher_code;
            }
        }

        public function initConfigForm(Am_Form $form)
        {
            $form->addAdvCheckbox('required')
                ->setLabel(___('Required'));
        }

        public function insertBrick(HTML_QuickForm2_Container $form)
        {
            $fieldSet = $form->addFieldset()->setLabel($this->___('Gift Voucher'));
            $voucher = $fieldSet->addText('giftVoucherCode', ['size' => 25])
                    ->setLabel($this->___('Enter gift voucher code'));
            if ($this->getConfig('required')) {
                $voucher->addRule('required', $this->___('Please enter gift voucher code'));
            }
            $voucher->addRule('callback2', 'error', [$this, 'validateGiftVoucher']);

            $root_rel_url = json_encode(Am_Di::getInstance()->url('misc/gift-vouchers',false));
            $form->addScript()->setScript(<<<CUT
    jQuery(document).ready(function($){
        function checkGiftCode(code){
            if (!code.length) return;

            jQuery.post($root_rel_url, {code}, function(res){
                    if(!res['product_ids'])
                        return;

                    var prIds = res.product_ids.split(',');

                    jQuery("input[name^=product_id]").not('select').each(function(){
                        var prId = jQuery(this).val().split('-')[0];
                        if (jQuery.inArray(prId, prIds) == -1) {
                            jQuery(this).prop('disabled', true);
                        } else {
                            jQuery(this).prop('checked', 'checked');
                            jQuery(this).change();
                        }
                    });

                    jQuery("select[name^=product_id] option").each(function(){
                        prId = jQuery(this).val().split('-')[0];
                        if (jQuery.inArray(prId, prIds) != -1) {
                            jQuery(this).prop("selected", "selected");
                            jQuery(this).change();
                        }
                    });

                    jQuery("#row-paysys_id").hide();
            });
        }

        jQuery('#giftVoucherCode-0').change(function(){
            checkGiftCode(jQuery(this).val());
        }).change();
    });
CUT
            );
        }

        public function validateGiftVoucher($code)
        {
            if ($code == "")
                return null;
            $voucher = Am_Di::getInstance()->giftVoucherTable->findFirstByVoucherCode($code);
            $msg = $voucher ? $voucher->validateVoucher() : $this->___('No gift vouchers found with such code');
            return $msg === null ? null : $msg;
        }

        public function isAcceptableForForm(Am_Form_Bricked $form)
        {
            return $form instanceof Am_Form_Signup;
        }
    }

});