<?php
class Payment_Adapter_Bitcart implements \FOSSBilling\InjectionAwareInterface

{
    private $config = array();

    protected $di;

    public function setDi(\Pimple\Container  | null $di): void
    {
        $this->di = $di;
    }

    public function getDi():  ? \Pimple\Container
    {
        return $this->di;
    }

    public function __construct($config)
    {
        $this->config = $config;

        foreach (['api_endpoint', 'admin_url', 'store_id'] as $key) {
            if (!isset($this->config[$key])) {
                throw new \Payment_Exception ('The ":pay_gateway" payment gateway is not fully configured. Please configure the :missing', [':pay_gateway' => 'Bitcart', ':missing' => $key]);
            }
        }
    }

    public static function getConfig()
    {
        return array(
            'supports_one_time_payments' => true,
            'description' => 'Please refer to https://docs.bitcart.ai/integrations/fossbilling for more details',
            'logo' => array(
                'logo' => 'Bitcart.png',
                'height' => '50px',
                'width' => '50px',
            ),
            'form' => array(
                'api_endpoint' => array('text', array(
                    'label' => 'Merchants API URL',
                ),
                ),
                'admin_url' => array('text', array(
                    'label' => 'Admin panel URL',
                ),
                ),
                'store_id' => array('text', array(
                    'label' => 'Store ID',
                ),
                ),
            ),
        );
    }

    public function getHtml($api_admin, $invoice_id, $subscription)
    {
        $invoice = $this->di['db']->load('Invoice', $invoice_id);
        $invoiceService = $this->di['mod_service']('Invoice');
        $payGatewayService = $this->di['mod_service']('Invoice', 'PayGateway');
        $payGateway = $this->di['db']->findOne('PayGateway', 'gateway = "Bitcart"');
        $order_id = 'fossbilling-' . $invoice->id;
        $params = array(
            'price' => $invoiceService->getTotalWithTax($invoice),
            'store_id' => $this->config['store_id'],
            'currency' => $invoice->currency,
            'buyer_email' => $invoice->buyer_email,
            'order_id' => $order_id,
            'notification_url' => $this->config['notify_url'],
            'redirect_url' => $this->config['thankyou_url'],
        );
        $invoice = $this->send_request(sprintf('%s/%s', $this->config['api_endpoint'], 'invoices/order_id/' . urlencode($order_id)), $params);
        return $this->_generateForm($invoice->id);
    }

    public function send_request($url, $data, $post = 1)
    {
        $post_fields = json_encode($data);

        $request_headers = array();
        $request_headers[] = 'Content-Type: application/json';

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
        if ($post) {
            curl_setopt($ch, CURLOPT_POST, $post);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);

        curl_close($ch);

        return json_decode($result);
    }

    public function processTransaction($api_admin, $id, $data, $gateway_id)
    {
        $post_data = json_decode($data['http_raw_post_data'], true);
        $url_check = sprintf('%s/%s', $this->config['api_endpoint'], 'invoices/' . $post_data['id']);
        $bitcart_invoice = $this->send_request($url_check, array(), 0);
        if ($bitcart_invoice->status != 'complete') {
            throw new Payment_Exception("Invalid IPN sent");
        }
        if ($this->isIpnDuplicate($bitcart_invoice->id, $bitcart_invoice->price)) {
            throw new Payment_Exception('IPN is duplicate');
        }

        $invoice = $this->di['db']->getExistingModelById('Invoice', $data['get']['bb_invoice_id']);
        $tx = $this->di['db']->getExistingModelById('Transaction', $id);
        $tx->invoice_id = $invoice->id;
        $tx->txn_status = $bitcart_invoice->status;
        $tx->txn_id = $bitcart_invoice->id;
        $tx->amount = $bitcart_invoice->price;
        $tx->currency = $bitcart_invoice->currency;

        $bd = array(
            'amount' => $tx->amount,
            'description' => 'Bitcart transaction ' . $bitcart_invoice->id,
            'type' => 'transaction',
            'rel_id' => $tx->id,
        );
        $client = $this->di['db']->getExistingModelById('Client', $invoice->client_id);
        $clientService = $this->di['mod_service']('client');
        $clientService->addFunds($client, $bd['amount'], $bd['description'], $bd);

        $invoiceService = $this->di['mod_service']('Invoice');
        if ($tx->invoice_id) {
            $invoiceService->payInvoiceWithCredits($invoice);
        }
        $invoiceService->doBatchPayWithCredits(array('client_id' => $client->id));

        $tx->status = 'processed';
        $tx->updated_at = date('Y-m-d H:i:s');
        $this->di['db']->store($tx);
    }

    protected function _generateForm($invoiceID)
    {
        $htmlOutput = '<button name = "bitcart-payment" class = "btn btn-success btn-sm" onclick = "showModal();return false;">Pay now</button>';
        $htmlOutput .= '<script src="' . $this->config['admin_url'] . '/modal/bitcart.js" type="text/javascript"></script>';
        $htmlOutput .= '<script type=\'text/javascript\'>';
        $htmlOutput .= 'function showModal() {';
        $htmlOutput .= 'bitcart.showInvoice(\'' . $invoiceID . '\');';
        $htmlOutput .= '}
                        </script>
                        </form>';
        return $htmlOutput;
    }

    public function isIpnDuplicate($txID, $txAmount)
    {
        $sql = 'SELECT id
                FROM transaction
                WHERE txn_id = :transaction_id
                  AND amount = :transaction_amount
                LIMIT 2';

        $bindings = array(
            ':transaction_id' => $txID,
            ':transaction_amount' => $txAmount,
        );

        $rows = $this->di['db']->getAll($sql, $bindings);
        if (count($rows) >= 1) {
            return true;
        }

        return false;
    }
}