<?php

class Bank extends AbstractBank
{
    public const DIR = __DIR__;
    public const CLIENT_DIR = self::DIR . "/bank-client";
    public const ADMIN_DIR = self::DIR . "/bank-admin";
    public const HOME_DIR = self::DIR . "/bank-home";
    public const GENERAL_DIR = self::DIR . "/general";
    public const LIB_DIR = self::DIR . "/library";

    # Convert Array To HTML Options

    public static function array_to_options(array $array, ?string $selected = null, bool $print = true)
    {
        $options = [];
        foreach($array as $key => $label) {
            $key = htmlentities($key, ENT_QUOTES);
            $label = ucfirst($label);
            $select = ($selected == $key) ? "selected" : null;
            $option = "<option value='{$key}' {$select}>{$label}</option>";
            $options[] = $option;
        };
        $result = implode("\n", $options);
        return print_r($result, !$print);
    }


    # Get user by account number

    public static function getBankUser($account_number)
    {
        $SQL = "
            SELECT %{prefix}_users.*
            FROM %{prefix}_users
            INNER JOIN %{prefix}_usermeta
            ON %{prefix}_users.id = %{prefix}_usermeta._ref
            WHERE
                %{prefix}_usermeta._key = 'bank:number'
                AND %{prefix}_usermeta._value REGEXP '^\"?{$account_number}\"?$'
        ";
        $SQL = Core::replace_var($SQL, ['prefix' => DB_TABLE_PREFIX]);
        $result = Uss::$global['mysqli']->query($SQL);
        return $result->fetch_assoc();
    }

    # GEt user Balance

    public static function getUserBalance(int $userid, string $currency  = 'USD')
    {

        if(strlen($currency) !== 3) {
            throw new Exception(__METHOD__ . ": Invalid Currency Code supplied in (# argument 4)");
        };

        $_key = "bank:balance";

        if($currency != 'USD') {
            $_key .= ("." . strtoupper($currency));
        };

        $balance = Uss::$global['usermeta']->get($_key, $userid);

        return (float)($balance ?? 0);

    }

    # Update User Balance

    public static function updateUserBalance(int $userid, ?float $amount, string $currency = 'USD', $exact = false)
    {

        if(strlen($currency) !== 3) {
            throw new Exception(__METHOD__ . ": Invalid Currency Code supplied in (# argument 4)");
        }

        $_key = "bank:balance";

        if($currency != 'USD') {
            $_key .= ("." . strtoupper($currency));
        };

        $balance = Uss::$global['usermeta']->get($_key, $userid);

        if(empty($balance)) {
            $balance = 0;
        };

        $new_balance = ($exact ? $amount : ($balance + $amount));

        $updated = Uss::$global['usermeta']->set($_key, $new_balance, $userid);

        return $updated;

    }

    # Get Currency Data By Code

    public static function getCurrencies(?string $code, ?string $key = null)
    {

        if(empty($code)) {
            $code = 'USD';
        }

        $currencies = Uss::$global['options']->get('bank:currencies');
        $default = strtoupper(Uss::$global['options']->get('bank:currencies.default'));
        $code = strtoupper($code);

        if(!in_array($code, array_column($currencies, 'code'))) {
            $code = $default;
        }

        foreach($currencies as $data) {

            if($data['code'] != $code) {
                continue;
            }

            if(is_null($key)) {
                return $data;
            } elseif(array_key_exists($key, $data)) {
                return $data[$key];
            }

        };

    }


    # Get Countries By Code;

    public static function getCountries($iso_2 = null, bool $heavy = false)
    {

        if(is_string($iso_2)) {
            $iso_2 = [$iso_2];
        } elseif(!is_null($iso_2) && !is_array($iso_2)) {
            throw new \Exception("Parameter 1 must be a string, an array or null");
        };

        $countries = array();
        $list = json_decode(file_get_contents(ASSETS_DIR . "/JSON/countries.min.json"), true);

        foreach($list as $data) {
            $key = $data['iso_2'];
            if(is_null($iso_2)) {
                $countries[ $key ] = $data;
            } else {
                foreach($iso_2 as $value) {
                    if(empty($value) || !is_string($value)) {
                        continue;
                    }
                    if(strtoupper(trim($value)) == $key) {
                        $countries[ $key ] = $data;
                    }
                };
            };
        };

        if(!$heavy) {
            $countries = array_combine(array_keys($countries), array_column($countries, "name"));
        };

        return $countries;

    }


    # Display Success, Info, Warning or Error Modal Message

    public static function log(string $type, string $message)
    {

        # Create special Icons;
        $icons = [
            'error' => "<i class='bi bi-exclamation-triangle text-danger me-2'></i>",
            'success' => "<i class='bi bi-check-circle text-success me-2'></i>",
            'info' => "<i class='bi bi-info-circle text-info me-2'></i>",
            'warning' => "<i class='bi bi-info-triangle text-warning me-2'></i>"
        ];

        $icon = $icons[$type];
        $message = trim($message);

        $content = "<div class='px-3 text-center'>
            <div class='fs-35px mb-2'>{$icon}</div>
            <div class=''>{$message}</div>
        </div>";

        return !Uss::console('@alert', $content); // returns true

    }


    # Generate New Unique Account Number

    public static function newBankNumber(int $userid)
    {

        $bankCode = 104;

        do {
            $userNumber = $bankCode . $userid . mt_rand(1000000, 9999999);
            $userNumber = substr($userNumber, 0, 10);
            $SQL = SQuery::select(DB_TABLE_PREFIX . "_usermeta", "_key = 'bank:number' AND _value = '{$userNumber}'");
            $result = Uss::$global['mysqli']->query($SQL);
        } while($result->num_rows);

        return $userNumber;

    }


    # Get User URL Directing To Admin Panel

    public static function userURL(string $code)
    {
        $href = Core::url(ROOT_DIR . "/" . UADMIN_ROUTE . "/users/{$code}");
        return $href;
    }


    # Process A New Transfer

    public static function transfer(array $detail, bool $notify = true)
    {

        if(!array_key_exists('default', $detail) || !is_array($detail['default'])) {
            throw new Exception("Missing default transaction information");
        };

        if(!array_key_exists('debit', $detail) && !array_key_exists('credit', $detail)) {
            throw new Exception("Transaction requires at least one credit or debit information");
        };

        $transaction = array();

        // Transfer Information

        $transfers = $detail['default'];

        if(!isset($transfers['amount'])) {
            throw new Exception("Default transaction detail requires an amount");
        };

        $transfers['tx_ref'] = (uniqid() . Core::keygen(15));

        if(empty($transfers['tx_region'])) {
            $transfers['tx_region'] = 'local';
        }
        if(empty($transfers['tx_charge'])) {
            $transfers['tx_charge'] = 0;
        }

        $transaction[] = array( 'transfers' => $transfers );

        # Credit Information

        if(!empty($detail['credit'])) {
            self::transferRefillUser($detail['credit'], 'credit', $transfers['amount'], $transfers['tx_charge'], $transfers['tx_ref']);
            $transaction[] = array( 'transfer_meta' => $detail['credit'] );
        };

        # Debit Information

        if(!empty($detail['debit'])) {
            self::transferRefillUser($detail['debit'], 'debit', $transfers['amount'], $transfers['tx_charge'], $transfers['tx_ref']);
            $transaction[] = array( 'transfer_meta' => $detail['debit'] );
        };

        # Process Transfer;

        try {

            foreach($transaction as $dataset) {

                foreach($dataset  as $tablename => $data) {

                    $SQL = SQuery::insert(DB_TABLE_PREFIX . "_{$tablename}", $data);

                    $insert = Uss::$global['mysqli']->query($SQL);

                    if(!$insert) {
                        throw new Exception("Transaction Failed");
                    }

                };

            }

            if($notify) {
                self::transferAlert($transfers, $detail['debit'] ?? null, 'debit');
                self::transferAlert($transfers, $detail['credit'] ?? null, 'credit');
            };

            return $transfers['tx_ref'];

        } catch(Exception $e) {

            $SQL = SQuery::delete(DB_TABLE_PREFIX . "_transfers", "tx_ref = '{$transfers['tx_ref']}'");

            $remove = Uss::$global['mysqli']->query($SQL);

            return false;

        };

    }

    # Send Email

    public static function mailto(string $client, string $subject, string $body)
    {

        $template = require Udash::VIEW_DIR . "/MAIL/template.php";
        $content = Core::replace_var($template, [
            'content' => $body
        ]);

        $PHPMailer = Udash::PHPMailer(true);

        if($_SERVER['SERVER_NAME'] === 'localhost') {
            $PHPMailer->isSMTP();
            $PHPMailer->SMTPAuth = false;
            $PHPMailer->Host = 'localhost'; // MailHog SMTP Server Address
            $PHPMailer->Port = 1025; // MailHog SMTP Server Port
        };

        $PHPMailer->addAddress($client);
        $PHPMailer->Subject = ucwords($subject);
        $PHPMailer->Body = $content;

        return $PHPMailer->send();

    }

    # Get a transaction detail

    public static function getTransaction($key, $column = 'id')
    {
        $data = [];
        $data['transfer'] = Udash::fetch_assoc(DB_TABLE_PREFIX . "_transfers", $key, $column);
        if(!$data['transfer']) {
            $data['sender'] = $data['receiver'] = null;
        } else {
            $SQL = SQuery::select(DB_TABLE_PREFIX . "_transfer_meta", "tx_ref = '{$data['transfer']['tx_ref']}' AND tx_type = '%s'");
            $data['sender'] = Uss::$global['mysqli']->query(sprintf($SQL, 'debit'))->fetch_assoc();
            $data['receiver'] = Uss::$global['mysqli']->query(sprintf($SQL, 'credit'))->fetch_assoc();
        };
        return $data['transfer'] ? $data : null;
    }

    # Preprocessor For New Transaction

    private static function transferRefillUser(&$data, string $type, $amount, $charge, $ref)
    {
        if(!empty($data['userid'])) {
            $bankNumber = Uss::$global['usermeta']->get('bank:number', $data['userid']);
            $data['account_number'] = $bankNumber;
            unset($data['userid']);
        };
        $data['tx_type'] = $type;
        $amount = (float)$amount;
        $charge = $amount * (float)$charge * 0.01;
        if($type == 'credit') {
            $data['amount'] = $amount;
        } else {
            $data['amount'] = $amount + $charge;
        }
        $data['tx_ref'] = $ref;
    }

    private static function transferAlert(array $transfer, ?array $data, string $type)
    {

        if(!$data) {
            return;
        };

        $user = Bank::getBankUser($data['account_number'] ?? '');
        if(!$user) {
            return;
        };

        $userinfo = Uss::$global['usermeta']->get('bank:info', $user['id']);
        if(!$userinfo || empty($userinfo['bio'])) {
            return;
        } else {
            $bio = $userinfo['bio'];
        }

        foreach([
            'currency' => $transfer['currency'],
            'tx_reason' => '',
            'time' => (new DateTime())->format('d/m/Y')
        ] as $key => $value) {
            if(empty($transfer[$key])) {
                $transfer[ $key ] = $value;
            };
        };

        $amount = number_format($transfer['amount'], 2);
        $way = $type == 'debit' ? 'from' : 'to';
        $partialAcc = substr($data['account_number'], 0, 3) . "***" . substr($data['account_number'], -3);
        $balance = self::getUserBalance($user['id'], $transfer['currency']);
        $totalBal = number_format($balance, 2);
        $availBal = number_format($balance - (float)$data['amount'], 2);

        $message = "
Hi {$bio['firstname']},  
This is to notify you that an amount of {$amount} {$transfer['currency']} has been {$type}ed {$way} your account.  
Acc: {$partialAcc}  
Desc: {$transfer['tx_reason']}  
Time: {$transfer['time']}  
Total Bal: {$totalBal} {$transfer['currency']}  
Avail Bal: {$availBal} {$transfer['currency']}
        ";

        $receipt = Core::url(ROOT_DIR . "/" . UDASH_ROUTE . "/receipt/{$transfer['tx_ref']}");

        return Udash::notify([
            'model' => 'bank:transfer',
            'userid' => $user['id'],
            'message' => $message,
            'redirect' => $receipt
        ]);

    }

}
