<?php

class Bank extends AbstractBank {

    const DIR = __DIR__;
    const CLIENT_DIR = self::DIR . "/bank-client";
    const ADMIN_DIR = self::DIR . "/bank-admin";
    const HOME_DIR = self::DIR . "/bank-home";
    const GENERAL_DIR = self::DIR . "/general";
    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;
        };

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

        foreach([
            'currency' => 'USD',
            '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
        ]);

    }

}
