<?php

declare(strict_types=1);
/**
 * Copyright 2022-2025 FOSSBilling
 * Copyright 2011-2021 BoxBilling, Inc.
 * SPDX-License-Identifier: Apache-2.0.
 *
 * @copyright FOSSBilling (https://www.fossbilling.org)
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0
 */
class Payment_Adapter_ClientBalance implements FOSSBilling\InjectionAwareInterface
{
    protected ?Pimple\Container $di = null;

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

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

    public function __construct()
    {
    }

    public static function getConfig(): array
    {
        return [
            'logo' => [
                'logo' => 'clientbalance.png',
                'height' => '50px',
                'width' => '50px',
            ],
        ];
    }

    public function enoughInBalanceToCoverInvoice(Model_Invoice $invoice): bool
    {
        $clientModel = $this->di['db']->load('Client', $invoice->client_id);
        $clientBalanceService = $this->di['mod_service']('Client', 'Balance');
        $sumInBalance = $clientBalanceService->getClientBalance($clientModel);

        $invoiceService = $this->di['mod_service']('Invoice');
        $totalSumWithTaxes = $invoiceService->getTotalWithTax($invoice);
        if ($totalSumWithTaxes > $sumInBalance) {
            return false;
        }

        return true;
    }

    public function getHtml($api_admin, $invoice_id, $subscription): string
    {
        $invoiceModel = $this->di['db']->load('Invoice', $invoice_id);

        if (!$this->enoughInBalanceToCoverInvoice($invoiceModel)) {
            return __trans('Your account balance is insufficient to cover this invoice.');
        }

        $invoiceService = $this->di['mod_service']('Invoice');
        if ($invoiceService->isInvoiceTypeDeposit($invoiceModel)) {
            return __trans('You may not pay a deposit invoice with this payment gateway.');
        }

        $ipnUrl = $this->getServiceUrl($invoice_id);
        $invoiceUrl = $this->di['tools']->url('invoice/' . $invoiceModel->hash);

        return "<script>
                document.addEventListener('DOMContentLoaded', function() {
                    FOSSBilling.api.makeRequest('POST', '$ipnUrl', {}, function () {
                        window.location.href = '$invoiceUrl';
                    }, function (error) {
                        console.error('Payment callback failed:', error);
                        if (window.confirm('An error occurred while processing your request. Would you like to return to the invoice page?')) {
                            window.location.href = '$invoiceUrl';
                        }
                    }, false);
                });
                </script>";
    }

    public function processTransaction($api_admin, $id, $data, $gateway_id): bool
    {
        if (!$this->isIpnValid($data)) {
            throw new Payment_Exception('IPN is invalid');
        }

        $tx = $this->di['db']->load('Transaction', $id);

        if ($tx->invoice_id) {
            $invoice_id = $tx->invoice_id;
        } else {
            $invoice_id = $data['get']['invoice_id'] ?? 0;
        }

        $invoiceModel = $this->di['db']->load('Invoice', $invoice_id);
        if (!$invoiceModel instanceof Model_Invoice) {
            throw new Payment_Exception('Invoice not found');
        }

        if ((int) $invoiceModel->client_id !== (int) $this->di['loggedin_client']->id) {
            throw new Payment_Exception('You are not authorized to pay this invoice with client balance.');
        }

        if ((int) ($invoiceModel->gateway_id ?? 0) !== (int) $gateway_id) {
            throw new Payment_Exception('Invoice is not configured to use this payment gateway.');
        }

        $invoiceService = $this->di['mod_service']('Invoice');
        if ($invoiceService->isInvoiceTypeDeposit($invoiceModel)) {
            throw new Payment_Exception('You may not pay a deposit invoice with this payment gateway.', [], 303);
        }

        if ($invoice_id) {
            $invoiceService->payInvoiceWithCredits($invoiceModel);
        }
        $invoiceService->doBatchPayWithCredits(['client_id' => $invoiceModel->client_id]);

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

        return true;
    }

    public function isIpnValid($data): bool
    {
        return $this->di['auth']->isClientLoggedIn();
    }

    public function getServiceUrl($invoice_id = 0)
    {
        $gatewayModel = $this->di['db']->findOne('PayGateway', 'gateway = ? and enabled = 1', ['ClientBalance']);
        if (!$gatewayModel instanceof Model_PayGateway) {
            throw new Payment_Exception('ClientBalance gateway is not enabled', null, 301);
        }

        $invoiceModel = $this->di['db']->load('Invoice', $invoice_id);
        $invoiceService = $this->di['mod_service']('Invoice');
        if ($invoiceService->isInvoiceTypeDeposit($invoiceModel)) {
            throw new Payment_Exception('You may not pay a deposit invoice with this payment gateway.', null, 302);
        }

        $gatewayService = $this->di['mod_service']('Invoice', 'PayGateway');

        return $gatewayService->getCallbackUrl($gatewayModel, $invoiceModel);
    }
}
