<?php

abstract class AbstractBank {

    public static $globalOptions = [

        'gender' => [
            "male" => "Male",
            "female" => "Female",
            "others" => "Others"
        ],

        'religion' => [
            "" => "-- Please select one --",
            "islam" => "islam",
            "christianity" => "christianity",
            "others" => "others"
        ],

        'relationship' => [
            "" => "- Choose One --",
            "mother" => "Mother",
            "father" => "Father",
            "brother" => "Brother",
            "sister" => "Sister",
            "spouse" => "Spouse",
            "friend" => "Friend",
            "son" => "Son",
            "daughter" => "Daughter",
            "stepfather" => "Stepfather",
            "stepmother " => "Stepmother ",
            "stepson" => "Stepson",
            "stepdaughter" => "Stepdaughter"
            
        ]

    ];


    ### Initialize The Platform 

    public static function init() {
        self::setupDB();
        self::globalTags();
        self::config( Uss::$global['options'] );
        self::configureUser();
        self::doTransfers();
        self::doExchange();
        self::makeAdmin();
        self::cleanDatabase();
        //self::secure();
    }

    ### Bank Ready

    public static function ready( callable $content ) {

        $user = Uss::$global['user'];

        Uss::tag('pin.error', 'Please enter your account pin to proceed');
        Uss::tag('pin.color', 'info');

        if( $user ) {

            $bankinfo = Uss::$global['usermeta']->get('bank:info', $user['id']);

            $account = Uss::$global['usermeta']->get('bank:account', $user['id']);
            $verified = !empty($account['verified']);
            
            $pin = $bankinfo['security']['pin'];

            if( isset($_POST['bank-user-pin-001']) ) {
                $_SESSION['user:pin'] = $_POST['bank-user-pin-001'];
                Uss::tag('pin.error', 'Please enter the correct pin');
                Uss::tag('pin.color', 'danger');
            };

            $pinned = ($_SESSION['user:pin'] ?? null) == $pin;

        } else {

            $pinned = $verified = false;

            if( isset($_SESSION['user:pin']) ) {
                unset($_SESSION['user:pin']);
            };

        };
        
        Home::headerFooter( $pinned && $verified );
        
        if( $user ) {
            
            call_user_func(function() use($pinned, $user, $verified) {

                if( in_array(Uss::query(1), ['signup', 'reset']) ) return;
                
                $authorized = $verified && $pinned;

                Udash::config('auth', $authorized);

                if( !$authorized ) {

                    Udash::config('auth-page', function() use($verified, $pinned) {
                        $signout = Core::url( ROOT_DIR . "/" . Udash::config('page:signout') );
                        if( !$verified ) {
                            require_once static::CLIENT_DIR . "/__control/unverified.php";
                        } else {
                            require_once static::CLIENT_DIR . "/__control/pin.php";
                        };
                    });

                };

            });

        };

        $content();

    }

    ### Set Up Missing User Meta Data

    public static function setUserMeta( ?int $userid ) {

        if( !$userid ) return;

        $metaset = array();
        $usermeta = Uss::$global['usermeta'];

        if( !$usermeta->get('bank:info', $userid) ) {
            $basicinfo = self::getBasicInfo();
            foreach( $basicinfo as $key => $data ) {
                foreach( $data as $name => $value ) {
                    $basicinfo[$key][$name] = null;
                }
            };
            $metaset['bank:info'] = $basicinfo;
        }

        if( is_null($usermeta->get('bank:balance', $userid)) ) {
            $metaset['bank:balance'] = 0;
        };

        if( is_null($usermeta->get('bank:number', $userid)) ) {
            $metaset['bank:number'] = static::newBankNumber( $userid );
        };

        if( is_null($usermeta->get('bank:otp', $userid )) ) {
            $metaset['bank:otp'] = [
                "email" => 0,
                "transfer_code" => 0
            ];  
        };

        if( is_null($usermeta->get('bank:account', $userid)) ) {
            $metaset['bank:account'] = [
                'verified' => 0,
                'can-transfer' => 0
            ];
        };

        if( is_null($usermeta->get('bank:extra', $userid)) ) {
            $metaset['bank:extra'] = [];
        };

        foreach( $metaset as $key => $value ) {
            $usermeta->set($key, $value, $userid);
        };

    }


    ### Reset Option Tags

    public static function resetOptionTags() {
        
        foreach( ['icon', 'title', 'tagline', 'description'] as $key ) {
            Uss::tag( "@site:{$key}", Uss::$global[$key] );
        };

        # Short Cut to Options Variables
        foreach( Uss::$global['options']->all() as $data ) {
            foreach( $data as $key => $value ) {
                if( !is_scalar($value) ) continue;
                if( $key == 'site:icon' ) {
                    $value = Core::url( MOD_DIR . "/{$value}" );
                };
                Uss::tag( "@{$key}", $value );
            }
        };

    }

    ### Print Meta Form: PSR
    
    public static function printMetaForm( ?array $bankinfo = [], $class = 'p-4 border rounded-2', $print = true ) {

        $formdata = '';

        if( is_null($bankinfo) ) {
            $bankinfo = [];
        };
        
        foreach( self::getBasicInfo() as $category => $subset ) {

            $title = $category;

            if( $title == 'nok' ) {
                $title = 'Next Of Kin';
            };

            $container = "<div class='mb-4 text-capitalize'>
                <h4 class='mb-3 display-6'>{$title}</h4>
                <fieldset>";

            foreach( $subset as $key => $datatype ) {

                $name = "bank[$category][$key]";
                $value = null;

                if( !empty($bankinfo) ) {
                    $value = $bankinfo[$category] ?? [];
                    $value = $value[$key] ?? null;
                };

                if( !is_array($datatype) ) {
                    
                    if( $datatype != 'textarea') {
                        $input = "<input type='{$datatype}' name='{$name}' class='form-control' value='{$value}'>";
                    } else {
                        $input = "<textarea class='form-control' name='{$name}'>{$value}</textarea>";
                    };

                } else {
                    
                    $input = "<select name='{$name}' class='form-select' value='{$value}'>";
                        $input .= $datatype[0];
                    $input .= "</select>";

                };

                $container .= "<div class='mb-3'>
                    <label class='form-label'>{$key}</label>
                    {$input}
                </div>";

            };

            $container .= "</fieldset>
                </div>";

            $formdata .= $container;
            
        };

        $result = "<div class='mb-4 {$class}'>{$formdata}</div>";

        return print_r($result, !$print);

    }


    ### Automatically Create Database Table
    
    protected static function setupDB() {

        $database = array(

            "transfers" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_transfers(
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    amount FLOAT NOT NULL DEFAULT 0,
                    currency VARCHAR(3) NOT NULL DEFAULT 'USD',
                    tx_ref VARCHAR(50) NOT NULL UNIQUE,
                    tx_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(),
                    tx_reason VARCHAR(1000),
                    tx_region VARCHAR(20) NOT NULL DEFAULT 'local',
                    tx_charge FLOAT NOT NULL DEFAULT 0 COMMENT '%',
                    tx_status VARCHAR(20) NOT NULL DEFAULT 'success',
                    bank_country VARCHAR(3),
                    bank_name VARCHAR(255),
                    bank_account VARCHAR(20),
                    bank_holder VARCHAR(255),
                    identifier VARCHAR(100),
                    identifier_code VARCHAR(100),
                    sender_name VARCHAR(255)
                );
            ",

            "transfer_meta" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_transfer_meta(
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    tx_ref VARCHAR(50) NOT NULL,
                    FOREIGN KEY(tx_ref) REFERENCES %{prefix}_transfers(tx_ref) ON DELETE CASCADE,
                    account_number VARCHAR(20),
                    tx_type VARCHAR(20) NOT NULL COMMENT 'debit|credit',
                    amount FLOAT NOT NULL,
                    status TINYINT NOT NULL DEFAULT 0
                );
            ",

            "beneficiaries" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_beneficiaries(
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    userid INT NOT NULL,
                    FOREIGN KEY(userid) REFERENCES %{prefix}_users(id) ON DELETE CASCADE,
                    bank_account VARCHAR(20) NOT NULL,
                    bank_region VARCHAR(100) NOT NULL,
                    bank_info TEXT
                );
            ",

            "cheques" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_cheques(
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    userid INT NOT NULL,
                    image VARCHAR(255) NOT NULL,
                    amount FLOAT DEFAULT 0,
                    date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    status VARCHAR(20) NOT NULL DEFAULT 'pending'
                );
            ",

            "payment" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_payment_methods(
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    medium VARCHAR(20) NOT NULL DEFAULT 'crypto' COMMENT 'crypto,bank,other',
                    network VARCHAR(100) COMMENT 'ETH, BTC',
                    name VARCHAR(255) NOT NULL,
                    icon VARCHAR(100),
                    detail TEXT,
                    date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    status TINYINT NOT NULL DEFAULT 1
                )
            ",

            "deposit" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_deposits(
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    tx_ref VARCHAR(40) NOT NULL UNIQUE,
                    userid INT NOT NULL,
                    usd_amount FLOAT,
                    network VARCHAR(255),
                    rate FLOAT DEFAULT 0,
                    detail TEXT,
                    txid VARCHAR(255),
                    status VARCHAR(20) NOT NULL DEFAULT 'pending',
                    date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    paid TINYINT NOT NULL DEFAULT 0
                )
            ",

            "cards" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_cards(
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    userid INT NOT NULL,
                    FOREIGN KEY (userid) REFERENCES %{prefix}_users(id) ON DELETE CASCADE,
                    card_type VARCHAR(10) NOT NULL DEFAULT 'debit',
                    vendor VARCHAR(20) NOT NULL DEFAULT 'mastercard',
                    cvv VARCHAR(3) NOT NULL,
                    card_number VARCHAR(16) NOT NULL,
                    expiry VARCHAR(6) NOT NULL,
                    status VARCHAR(10) NOT NULL DEFAULT 'pending',
                    date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
                )
            ",

            "loan" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_loans(
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    userid INT NOT NULL,
                    date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    amount FLOAT,
                    duration VARCHAR(20) NOT NULL,
                    loan_type VARCHAR(50) NOT NULL,
                    reason TEXT,
                    loan_id VARCHAR(20) NOT NULL,
                    status VARCHAR(20) NOT NULL DEFAULT 'pending',
                    funded TINYINT NOT NULL DEFAULT 0
                )
            ",

            "exchange" => "
                CREATE TABLE IF NOT EXISTS %{prefix}_exchanges (
                    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
                    userid INT NOT NULL,
                    paid_amount FLOAT COMMENT 'user paid',
                    paid_currency VARCHAR(4) COMMENT 'user paid',
                    usd_value FLOAT COMMENT 'USD equivalance',
                    usd_fee FLOAT COMMENT 'cost in USD',
                    expected_amount FLOAT COMMENT 'user will receive',
                    expected_currency VARCHAR(4) COMMENT 'user will receiver',
                    date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    status VARCHAR(20) NOT NULL DEFAULT 'pending',
                    funded TINYINT NOT NULL DEFAULT 0
                )   
            "

        );

        self::processDatabase( $database );

    }


    ### Generate Database Tables

    private static function processDatabase( $database ) {

        foreach( $database as $key => $SQL ) {

            try {

                $SQL = Core::replace_var($SQL, ["prefix" => DB_TABLE_PREFIX]);

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

                if( !$insert ) {
                    throw new Exception( "Unable to create relevant bank tables. Error: " . Uss::$global['mysqli']->error );
                };

            } catch(mysqli_sql_exception $e) {
                
                echo "Error in table `{$key}`: {$e->getMessage()}";

            };
            
        };

    }

    
    ### Protected Initial Configuration

    protected static function config( Pairs $options ) {

        # Currency config:

        call_user_func(function() use($options) {

            if( $options->get('bank:currencies') ) return;

            $default = array(
                array(
                    "code" => "USD",
                    "name" => "US Dollar",
                    "symbol" => "$",
                    "rate" => 1
                )
            );

            $options->set("bank:currencies", $default);
            $options->set("bank:currencies.default", $default[0]['code']);

        });

        # Currencies Tag;

        call_user_func(function() use($options) {
            
            $currencySet = array();

            foreach( $options->get("bank:currencies") as $data ) {
                $code = $data['code'];
                $currencySet[ $code ] = $data['name'];
            };

            Uss::tag('currency_options', static::array_to_options($currencySet, null, false));

        });

        # Configure account types

        call_user_func(function() use($options) {

            if( is_null($options->get("bank:accounts")) ) {

                $options->set("bank:accounts", [
                    "savings account",
                    "current account",
                    "fixed account"
                ]);

            };

            $accounts = $options->get("bank:accounts");
            $accounts = array_combine( $accounts, $accounts );

            Uss::tag('account_options', static::array_to_options($accounts, null, false));

        });

        # Other settings;

        call_user_func(function() use($options) {

            $regularConfig = [

                'bank:transfer.charge' => '10',

                "bank:transfer.codes" => array(
                    array( "imf" => null ),
                    array( "cot" => 2349 ),
                    array( "tax" => null )
                ),

                'site:phone' => '08123456789',
                'site:address' => '6391 Elgin St. Celina, 10299',
                'site:exchange-fee' => 0

            ];

            foreach( $regularConfig as $key => $value ) {
                if( is_null($options->get($key)) ) {
                    $options->set($key, $value);
                };
            }

        });

    }


    ### Configure Globally Used Tags
    
    protected static function globalTags() {

        Uss::tag('countries', Bank::array_to_options( Bank::getCountries(), null, false ));

        Uss::tag('gender_options', Bank::array_to_options(self::$globalOptions['gender'], null, false));   

        Uss::tag('religion_options', Bank::array_to_options(self::$globalOptions['religion'], null, false));

        Uss::tag('relationship_options', Bank::array_to_options(self::$globalOptions['relationship'], null, false));

        return true;

    }


    ### Configure User
    
    protected static function ConfigureUser() {

        $user = Uss::$global['user'];

        if( !$user ) return;

        $userinfo = Uss::$global['usermeta']->get('bank:info', $user['id']);
        $bankNo = Uss::$global['usermeta']->get('bank:number', $user['id']);
        $currency = Bank::getCurrencies( $userinfo['system']['currency'] );
        $bankBal = static::getUserBalance( $user['id'], $currency['code'] );
        
        Uss::tag('user.bank_number', $bankNo);
        Uss::tag('user.currency', $currency['symbol']);
        Uss::tag('user.currency.code', $currency['code']);
        Uss::tag('user.balance', number_format($bankBal ?? 0, 2));
        Uss::tag('user.firstname', $userinfo['bio']['firstname']);
        Uss::tag('user.lastname', $userinfo['bio']['lastname']);

    }

    
    ### Get Basic User Info

    private static function getBasicInfo() {
        return [
            "security" => [
                "pin" => 'number'
            ],
            "bio" => [
                "firstname" => 'text',
                "lastname" => 'text',
                "phone" => "number",
                "birthdate" => "date",
                "gender" => ['%{gender_options}'],
                "religion" => ['%{religion_options}']
            ],
            "address" => [
                "location" => 'text',
                "state" => 'text',
                "city" => 'text',
                "country" => ['%{countries}'],
                "zipcode" => 'number'
            ],
            "nok" => [
                "firstname" => 'text',
                "lastname" => 'text',
                "relationship" => ['%{relationship_options}'],
                "address" => 'text'
            ],
            "system" => [
                "currency" => ['%{currency_options}'],
                "account" => ['%{account_options}']
            ]
        ];
    }

    # Do financial Transactions

    private static function doTransfers() {

        $SQL = "
            SELECT 
                %{prefix}_transfer_meta.*,
                %{prefix}_transfers.currency
            FROM %{prefix}_transfer_meta
            INNER JOIN %{prefix}_transfers
            ON %{prefix}_transfer_meta.tx_ref = %{prefix}_transfers.tx_ref
            WHERE 
                %{prefix}_transfer_meta.status = 0
                AND %{prefix}_transfers.tx_status IN('success', 'approved')
        ";

        $SQL = Core::replace_var( $SQL, ['prefix' => DB_TABLE_PREFIX] );
        $result = Uss::$global['mysqli']->query( $SQL );

        if( $result->num_rows ) {

            while( $data = $result->fetch_assoc() ) {

                $user = static::getBankUser( $data['account_number'] );

                if( !$user ) {
                    self::markAsPaid( $data );
                    continue;
                };

                if( $data['tx_type'] == 'debit' ) {
                    $data['amount'] = "-{$data['amount']}";
                };

                $updatedBalance = static::updateUserBalance( $user['id'], $data['amount'], $data['currency'] );

                if( $updatedBalance ) {

                    self::markAsPaid( $data );

                    try {
                        $status = static::mailto( $user['email'], "{$data['tx_type']} Transaction Notification", self::buildTransferMessage($data, $user['id']) );
                    } catch(Exception $e) {
                        //var_dump( $e );
                    };

                }

            };

        };

    }

    private static function buildTransferMessage( array $txMeta, int $userid ) {

        $site = array(
            "title" => Uss::$global['options']->get('site:title') ?? Uss::$global['title'],
            "url" => Core::url( ROOT_DIR ),
            "phone" => Uss::$global['options']->get('site:phone'),
            "email" => Uss::$global['options']->get('email:admin')
        );

        $message = "
            <img src='https://cdn.pixabay.com/photo/2020/09/02/03/26/iphone-5537230_1280.jpg' style='max-width: 100%; width: 100%;'> <br/><br/>
            
            This is to inform you that a transaction has occurred on your account with {$site['title']} with details below: <br/><br/>";

        $bankinfo = Uss::$global['usermeta']->get('bank:info', $userid);
        $bio = $bankinfo['bio'];
        $currency = static::getCurrencies( $bankinfo['system']['currency'] );
        $balance = static::getUserBalance($userid, $currency['code']);

        $data = array();
        $data[] = array( 'label' => 'Account Name', 'value' => "{$bio['firstname']} {$bio['lastname']}" );
        $data[] = array( 'label' => "Transaction Type", "value" => ucwords("{$txMeta['tx_type']} alert") );
        $data[] = array( "label" => "Transaction Amount", "value" => number_format($txMeta['amount'], 2) );
        $data[] = array( "label" => "Transaction Currency", "value" => $currency['name'] );
        $data[] = array( "label" => "Account Number", "value" => $txMeta['account_number'] );
        $data[] = array( "label" => "Transaction Ref", "value" => $txMeta['tx_ref'] );
        $data[] = array( "label" => "DateTime", "value" => date('d-M-Y H:i') );

        $data[] = array( "label" => "Available Balance", "value" => number_format($balance, 2) );
        
        $tab = new DOMTable('tx');
        $tab->data( $data );
        $tab->columns(['label', 'value']);

        $thead = $tab->table->getElementsByTagName('thead')->item(0);
        $thead->parentNode->removeChild($thead);

        $message .= $tab->prepare();

        $message .= "<br/><br/>
            To view your full account statement, log in to {$site['title']} at {$site['url']} <br/><br/>

            For additional information about this transaction or any of our services, please contact our Customer Fulfillment Centre : {$site['phone']}, {$site['email']}. <br/><br/>

            Your account information is private. Please do not disclose your login credentials or card details to anyone. Avoid clicking on suspicious links in emails or text messages. If in doubt, kindly contact support team: {$site['phone']}, {$site['email']}
        ";

        return $message;

    }

    private static function markAsPaid( array $data ) {
        $SQL = SQuery::update( DB_TABLE_PREFIX . "_transfer_meta", ['status' => 1], "id = {$data['id']}" );
        $update = Uss::$global['mysqli']->query( $SQL );
    }


    # Process Exchanges

    private static function doExchange() {
        $SQL = SQuery::select( DB_TABLE_PREFIX . "_exchanges", "status = 'approved' AND funded = 0" );
        $result = Uss::$global['mysqli']->query( $SQL );
        if( !$result->num_rows ) return;
        while( $data = $result->fetch_assoc() ) {
            $funded = static::updateUserBalance( $data['userid'], $data['expected_amount'], $data['expected_currency'] );
            if( $funded ) {
                $data['funded'] = 1;
                $SQL = SQuery::update( DB_TABLE_PREFIX . "_exchanges", $data, "id = {$data['id']}" );
                $update = Uss::$global['mysqli']->query( $SQL );
            };
        };
    }

    # Create first admin

    private static function makeAdmin() {
        if( !empty(Roles::get_assigned_users( 'super-admin' )) ) return;
        $SQL = SQuery::select( DB_TABLE_PREFIX . "_users" );
        $user = Uss::$global['mysqli']->query( $SQL )->fetch_assoc();
        if( !$user ) return;
        Roles::user( $user['id'] )::assign( 'super-admin' );
    }

    # Clean Database

    private static function cleanDatabase() {
        $SQL = Core::replace_var("
            DELETE FROM `%{prefix}_transfer_meta`
            WHERE `tx_ref` IN (
                SELECT `%{prefix}_transfer_meta`.`tx_ref`
                FROM `%{prefix}_transfer_meta`
                LEFT JOIN `%{prefix}_transfers` ON `%{prefix}_transfer_meta`.`tx_ref` = `%{prefix}_transfers`.`tx_ref`
                WHERE `%{prefix}_transfers`.`tx_ref` IS NULL
            );
        ", ['prefix' => DB_TABLE_PREFIX]);
        $result = Uss::$global['mysqli']->query( $SQL );
        return $result;
    }

};