<?php
/**
 * Class represents records from table invoice_payment
 * {autogenerated}
 * @property int $invoice_payment_id PK
 * @property int $invoice_id FK invoice
 * @property string $invoice_public_id
 * @property int $user_id FK user
 * @property string|null $paysys_id
 * @property string $receipt_id
 * @property string $transaction_id
 * @property datetime $dattm
 * @property string $currency
 * @property float $amount
 * @property float|null $discount
 * @property float|null $tax
 * @property string|null $tax_rate
 * @property int|null $tax_address_id
 * @property float|null $shipping
 * @property datetime|null $refund_dattm
 * @property float|null $refund_amount
 * @property float|null $base_currency_multi
 * @property string|null $display_invoice_id
 * @property int $is_first
 * {/autogenerated}
 * @package Am_Invoice
 */
class InvoicePayment extends Am_Record_WithData
{
    use Am_Invoice_Tax_Address;

    /** @var User */
    protected $_user;
    /** @var Invoice */
    protected $_invoice;
    /** @var InvoiceRefund */
    protected $_refund;

    /** Used for tax purposes */
    protected $_address;

    public function isFirst()
    {
        if ($this->getInvoice()->first_total == 0) return false; // if there is free trial
        return ! $this->getAdapter()->selectCell("
            SELECT COUNT(*) FROM ?_invoice_payment
            WHERE invoice_id=?d AND
                invoice_payment_id <> ?d AND dattm <= ? AND invoice_payment_id < ?d",
            $this->invoice_id,
            $this->invoice_payment_id, $this->dattm, $this->invoice_payment_id);
    }

    public function setFromTransaction(Invoice $invoice, Am_Paysystem_Transaction_Interface $transaction)
    {
        $this->dattm = $transaction->getTime()->format('Y-m-d H:i:s');
        $this->invoice_id = $invoice->invoice_id;
        $this->invoice_public_id = $invoice->public_id;
        $this->user_id = $invoice->user_id;
        $this->currency = $invoice->currency;
        $this->paysys_id = $transaction->getPaysysId();
        $this->receipt_id = $transaction->getReceiptId();
        $this->transaction_id = $transaction->getUniqId();
        /// get from invoice
        $isFirst = ! ((doubleval($invoice->first_total) === 0.0) || $invoice->getPaymentsCount());
        $amount = $transaction->getAmount();
        if ($amount<=0)
            $amount = $isFirst ? $invoice->first_total : $invoice->second_total;
        $this->amount = (float)$amount;
        $this->discount = $isFirst ? $invoice->first_discount : $invoice->second_discount;
        $this->setTax($invoice, $isFirst);
        $this->shipping = $isFirst ? $invoice->first_shipping : $invoice->second_shipping;
        return $this;
    }

    function setTax(Invoice $invoice, $isFirst)
    {
        if ($isFirst) {
            $this->tax = $invoice->first_tax;
            $this->tax_rate = $invoice->tax_rate;
        } else {
            $this->tax_rate = 0;
            $this->tax = 0.0;
            foreach ($this->getDi()->plugins_tax->getAllEnabled() as $plugin) {
                foreach ($invoice->getItems() as $item) {
                    if (($item->second_total > 0) && ($rate = $plugin->getRate($invoice, $item))) {
                        $this->tax_rate = $rate;
                        $this->tax = $this->getInvoice()->recalculateWithTaxRate($rate)->second_tax;
                        break 2;
                    }
                }
            }
        }
        $this->tax_address_id = $this->getDi()->addressTable->getTaxAddressId($invoice->getUser());
    }

    public function fromRow(array $vars)
    {
        if (!empty($vars['refund_dattm']) && $vars['refund_dattm'] == '0000-00-00 00:00:00')
            $vars['refund_dattm'] = null;
        return parent::fromRow($vars);
    }

    public function isRefunded(): bool
    {
        return !is_null($this->refund_dattm);
    }

    public function isFullRefunded(): bool
    {
        return $this->amount <= $this->refund_amount;
    }

    public function refund(InvoiceRefund $r)
    {
        $this->updateQuick([
            'refund_dattm' => $this->refund_dattm ?: $r->dattm,
            'refund_amount' => $this->refund_amount + $r->amount
        ]);
    }

    public function insert($reload = true)
    {
        if ($this->currency == Am_Currency::getDefault()) {
            $this->base_currency_multi = 1.0;
        } else {
            $this->base_currency_multi = $this->getDi()->currencyExchangeTable->getRate($this->currency, sqlDate($this->dattm));
        }

        $this->getDi()->hook->call(new Am_Event(Am_Event::PAYMENT_BEFORE_INSERT, [
                'payment' => $this,
                'invoice' => $this->getInvoice(),
                'user' => $this->getInvoice()->getUser(),
            ]));

        $invoice = $this->getInvoice();

        $this->is_first = ($invoice->first_total == 0 || $invoice->getPaymentsCount() > 0) ? 0 : 1;
        parent::insert($reload);
        $this->setDisplayInvoiceId();
        $this->getDi()->hook->call(new Am_Event_PaymentAfterInsert(null,
            [
                'payment' => $this,
                'invoice' => $this->getInvoice(),
                'user'    => $this->getInvoice()->getUser()
            ]));

        return $this;
    }

    function delete()
    {
        parent::delete();
        $this->getDi()->hook->call(Am_Event::PAYMENT_AFTER_DELETE, ['payment' => $this]);
        return $this;
    }

    /**
     * @return InvoiceRefund|null
     */
    public function getRefund()
    {
        if (!$this->isRefunded()) return null;
        if (empty($this->_refund))
            $this->_refund = $this->getDi()->invoiceRefundTable->findFirstBy([
                'invoice_payment_id' => $this->pk()
            ]);
        return $this->_refund;
    }

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

    public function getInvoice(): Invoice
    {
        if (empty($this->_invoice))
            $this->_invoice = $this->getDi()->invoiceTable->load($this->invoice_id);
        return $this->_invoice;
    }

    public function _setInvoice(Invoice $invoice): self
    {
        $this->_invoice = $invoice;
        return $this;
    }

    function getCurrency($value = null): Am_Currency
    {
        $c = new Am_Currency($this->currency);
        if ($value) $c->setValue($value);
        return $c;
    }

    /**
     * Set Invoice ID which will be displayed in pdf invoice
     */
    protected function setDisplayInvoiceId()
    {
        $this->display_invoice_id = $this->getDi()->hook->filter(
            "{$this->getInvoice()->public_id}/{$this->getInvoice()->getPaymentsCount()}",
            Am_Event::SET_DISPLAY_INVOICE_PAYMENT_ID,
            ['record' => $this]);
        $this->updateSelectedFields('display_invoice_id');
    }

    function getDisplayInvoiceId()
    {
        return $this->display_invoice_id ?: ($this->getInvoice()->public_id . '/' . $this->receipt_id);
    }
}

/**
 * @package Am_Invoice
 * @extends Am_Table_WithData<InvoicePayment>
 * @method int countByUserId($value)
 * @method void deleteByInvoiceId($value, $useObjects = true)
 * @method InvoicePayment[] findByInvoiceId($value, $start = null, $count = null, $orderBy = null)
 * @method InvoicePayment[] findByUserId($value, $start = null, $count = null, $orderBy = null)
 * @method InvoicePayment|null findFirstByReceiptId($value)
 */
class InvoicePaymentTable extends Am_Table_WithData
{
    protected $_key = 'invoice_payment_id';
    protected $_table = '?_invoice_payment';
    protected $_recordClass = 'InvoicePayment';

    function getPaymentsCount($invoiceId)
    {
        ///// NEED REAL ATTENTION HERE: Added to handle imported payments correctly.
        ///// Import3 script create payments with zero amount, and these payments are counted as real payments
        ///// so updateRebillDate does not work if imported invoice have free trial period.
        return $this->_db->selectCell("SELECT COUNT(*) FROM ?_invoice_payment WHERE invoice_id=?d and amount>0", $invoiceId);
    }

    /** @return string */
    function getLastReceiptId($invoiceId)
    {
        return $this->_db->selectCell("SELECT receipt_id
                FROM ?_invoice_payment
                WHERE invoice_id=?d
                ORDER BY dattm DESC
                LIMIT 1", $invoiceId);
    }

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

    /** @return TRecord[] */
    function selectLast($num, $dateThreshold = null)
    {
        return $this->selectObjects("SELECT ip.*, refund_amount, refund_dattm, i.coupon_code,
            (SELECT GROUP_CONCAT(item_title SEPARATOR ', ') FROM ?_invoice_item WHERE invoice_id=ip.invoice_id) AS items,
            u.login, u.email, CONCAT(u.name_f, ' ', u.name_l) AS name, u.name_f, u.name_l, u.added,
            ip.invoice_public_id AS public_id
            FROM ?_invoice_payment ip
            LEFT JOIN ?_user u USING (user_id)
            LEFT JOIN ?_invoice i ON ip.invoice_id = i.invoice_id
            {WHERE ip.dattm > ?}
            ORDER BY ip.dattm DESC LIMIT ?d",
            $dateThreshold ?: DBSIMPLE_SKIP, $num);
    }
}