#include "..\script_component.hpp"

/*
 * File: fnc_initStore.sqf
 * Author: IDSolutions
 * Date: 2025-12-17
 * Last Update: 2026-04-02
 * Public: No
 *
 * Description:
 *     Initializes the bank store for managing player bank accounts.
 *     Bank account truth lives in the extension hot cache; SQF handles
 *     session state, Arma-facing validation, and client messaging.
 */

#pragma hemtt ignore_variables ["_self"]
GVAR(BankBaseStore) = compileFinal createHashMapFromArray [
    ["#base", EGVAR(common,BaseStore)],
    ["#type", "BankBaseStore"],
    ["#create", compileFinal {
        ["INFO", "Bank Store Initialized!"] call EFUNC(common,log);
    }],
    ["normalizeAccount", compileFinal {
        params [["_uid", "", [""]], ["_account", createHashMap, [createHashMap]], ["_playerName", "", [""]]];

        if (_uid isEqualTo "" || { !(_account isEqualType createHashMap) }) exitWith { createHashMap };

        private _finalAccount = GVAR(BankModel) call ["migrate", [+_account]];
        if ((_finalAccount getOrDefault ["uid", ""]) isEqualTo "") then {
            _finalAccount set ["uid", _uid];
        };
        if ((_finalAccount getOrDefault ["name", ""]) isEqualTo "" && { _playerName isNotEqualTo "" }) then {
            _finalAccount set ["name", _playerName];
        };

        _finalAccount
    }],
    ["callHotBank", compileFinal {
        params [["_function", "", [""]], ["_arguments", [], [[]]]];

        private _envelope = _self call ["callHotBankEnvelope", [_function, _arguments]];
        _envelope getOrDefault ["data", createHashMap]
    }],
    ["callHotBankEnvelope", compileFinal {
        params [["_function", "", [""]], ["_arguments", [], [[]]]];

        private _envelope = createHashMapFromArray [["data", createHashMap], ["error", ""]];

        if (_function isEqualTo "") exitWith { _envelope };

        [_function, _arguments] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
        if !(_isSuccess) exitWith {
            _envelope set ["error", format ["Bank backend call '%1' failed.", _function]];
            _envelope
        };
        if !(_result isEqualType "") exitWith {
            _envelope set ["error", format ["Bank backend call '%1' returned an invalid response.", _function]];
            _envelope
        };
        if ((_result find "Error:") == 0) exitWith {
            ["ERROR", format ["Bank extension call '%1' failed: %2", _function, _result]] call EFUNC(common,log);
            _envelope set ["error", _result select [7]];
            _envelope
        };

        private _data = fromJSON _result;
        if !(_data isEqualType createHashMap) exitWith {
            _envelope set ["error", format ["Bank backend call '%1' returned unreadable JSON.", _function]];
            _envelope
        };

        _envelope set ["data", _data];
        _envelope
    }],
    ["loadHotBank", compileFinal {
        params [["_uid", "", [""]], ["_initialize", false, [false]], ["_playerName", "", [""]]];

        if (_uid isEqualTo "") exitWith { createHashMap };

        private _command = ["bank:hot:get", "bank:hot:init"] select _initialize;
        private _account = _self call ["callHotBank", [_command, [_uid]]];
        if (_account isEqualTo createHashMap) exitWith { _account };

        _self call ["normalizeAccount", [_uid, _account, _playerName]]
    }],
    ["finalizeMutation", compileFinal {
        params [
            ["_uid", "", [""]],
            ["_result", createHashMap, [createHashMap]],
            ["_save", false, [false]]
        ];

        if (_uid isEqualTo "" || { _result isEqualTo createHashMap }) exitWith { createHashMap };

        private _account = _result getOrDefault ["account", createHashMap];
        private _patch = _result getOrDefault ["patch", createHashMap];

        if !(_patch isEqualType createHashMap) then {
            _patch = createHashMap;
        };

        if (_save && { _account isNotEqualTo createHashMap }) then {
            private _savedAccount = _self call ["callHotBank", ["bank:hot:save", [_uid]]];
            if (_savedAccount isEqualTo createHashMap) exitWith { createHashMap };
            _account = _savedAccount;
        };

        if (_account isNotEqualTo createHashMap) then {
            _self call ["normalizeAccount", [_uid, _account, ""]];
        };

        _patch
    }],
    ["runMutation", compileFinal {
        params [
            ["_uid", "", [""]],
            ["_command", "", [""]],
            ["_arguments", [], [[]]],
            ["_save", false, [false]],
            ["_notification", "", [""]]
        ];

        if (_uid isEqualTo "" || { _command isEqualTo "" }) exitWith { false };

        private _envelope = _self call ["callHotBankEnvelope", [_command, _arguments]];
        private _result = _envelope getOrDefault ["data", createHashMap];
        private _finalPatch = _self call ["finalizeMutation", [_uid, _result, _save]];
        if (_finalPatch isEqualTo createHashMap) exitWith {
            private _message = _envelope getOrDefault ["error", "Bank operation failed."];
            if (_message isNotEqualTo "") then {
                GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _message]];
            };
            false
        };

        GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalPatch]];
        if (_notification isNotEqualTo "") then {
            GVAR(BankMessenger) call ["sendNotification", [_uid, "info", "Bank", _notification]];
        };

        true
    }],
    ["chargeCheckout", compileFinal {
        params [["_uid", "", [""]], ["_source", "cash", [""]], ["_amount", 0, [0]], ["_commit", false, [false]]];

        private _result = createHashMapFromArray [["success", false], ["message", "Unable to process bank payment."], ["patch", createHashMap]];
        if (_uid isEqualTo "") exitWith { _result };

        private _checkoutContext = GVAR(BankPayloadBuilder) call ["buildCheckoutContext", [_source, _commit]];
        private _envelope = _self call [
            "callHotBankEnvelope",
            [
                "bank:hot:charge_checkout",
                [_uid, str _amount, toJSON _checkoutContext]
            ]
        ];
        private _mutationResult = _envelope getOrDefault ["data", createHashMap];
        private _patch = _self call ["finalizeMutation", [_uid, _mutationResult, false]];
        if (_patch isEqualTo createHashMap) exitWith {
            _result set ["message", _envelope getOrDefault ["error", "Bank checkout payment failed."]];
            _result
        };

        _result set ["success", true];
        _result set ["message", ""];
        _result set ["patch", _patch];
        _result
    }],
    ["repayCreditLine", compileFinal {
        params [["_uid", "", [""]], ["_amount", 0, [0]]];

        if (_uid isEqualTo "" || { _amount <= 0 }) exitWith {
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", "Enter a valid repayment amount."]];
            false
        };

        private _originalAccount = _self call ["loadHotBank", [_uid, false, ""]];
        if (_originalAccount isEqualTo createHashMap) then {
            _originalAccount = _self call ["loadHotBank", [_uid, true, ""]];
        };
        if (_originalAccount isEqualTo createHashMap) exitWith {
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", "Bank account could not be loaded."]];
            false
        };

        private _checkoutContext = GVAR(BankPayloadBuilder) call ["buildCheckoutContext", ["bank", false]];
        private _previewEnvelope = _self call [
            "callHotBankEnvelope",
            [
                "bank:hot:charge_checkout",
                [_uid, str _amount, toJSON _checkoutContext]
            ]
        ];
        private _previewResult = _previewEnvelope getOrDefault ["data", createHashMap];
        private _bankPatch = _self call ["finalizeMutation", [_uid, _previewResult, false]];
        if (_bankPatch isEqualTo createHashMap) exitWith {
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _previewEnvelope getOrDefault ["error", "Credit repayment could not be funded from the bank account."]]];
            false
        };

        private _nextAccount = _previewResult getOrDefault ["account", createHashMap];
        if (_nextAccount isEqualTo createHashMap) exitWith {
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", "Bank repayment preview returned an invalid account state."]];
            false
        };

        private _overrideEnvelope = _self call [
            "callHotBankEnvelope",
            ["bank:hot:override", [_uid, _self call ["toJSON", [_nextAccount]]]]
        ];
        if ((_overrideEnvelope getOrDefault ["data", createHashMap]) isEqualTo createHashMap) exitWith {
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _overrideEnvelope getOrDefault ["error", "Credit repayment could not reserve bank funds."]]];
            false
        };

        private _orgResult = EGVAR(org,OrgStore) call ["repayCreditLine", [_uid, _amount]];
        if !(_orgResult getOrDefault ["success", false]) exitWith {
            private _rollbackEnvelope = _self call [
                "callHotBankEnvelope",
                ["bank:hot:override", [_uid, _self call ["toJSON", [_originalAccount]]]]
            ];
            if ((_rollbackEnvelope getOrDefault ["data", createHashMap]) isEqualTo createHashMap) then {
                ["ERROR", format ["Failed to roll back bank state for %1 after org credit repayment failure.", _uid]] call EFUNC(common,log);
            };

            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _orgResult getOrDefault ["message", "Credit repayment failed."]]];
            false
        };

        private _persistenceFailures = [];
        private _savedBank = _self call ["save", [_uid]];
        if (_savedBank isEqualTo createHashMap) then {
            _persistenceFailures pushBack "bank";
        };

        private _orgPersistenceMessage = _orgResult getOrDefault ["persistenceMessage", ""];
        if !(_orgResult getOrDefault ["persisted", false]) then {
            _persistenceFailures pushBack "organization";
        };

        GVAR(BankMessenger) call ["sendAccountSync", [_uid, _bankPatch]];
        GVAR(BankMessenger) call ["sendNotification", [_uid, "info", "Bank", _orgResult getOrDefault ["message", format ["Repaid $%1 toward the organization credit line.", [_amount] call EFUNC(common,formatNumber)]]]];

        private _orgPatch = _orgResult getOrDefault ["patch", createHashMap];
        if (_orgPatch isNotEqualTo createHashMap) then {
            private _memberUids = +(_orgResult getOrDefault ["memberUids", []]);
            if (isNil QEGVAR(common,EventBus)) then {
                {
                    private _memberPlayer = [_x] call EFUNC(common,getPlayer);
                    if (_memberPlayer isNotEqualTo objNull) then {
                        [CRPC(org,responseSyncOrg), [_orgPatch], _memberPlayer] call CFUNC(targetEvent);
                    };
                } forEach _memberUids;
            } else {
                EGVAR(common,EventBus) call ["emit", [
                    "org.sync.requested",
                    createHashMapFromArray [
                        ["memberUids", _memberUids],
                        ["patch", +_orgPatch]
                    ],
                    createHashMapFromArray [["source", "bank"]]
                ]];
            };
        };

        if (_persistenceFailures isNotEqualTo []) then {
            private _warning = format [
                "Credit repayment posted, but durable save failed for: %1.",
                _persistenceFailures joinString ", "
            ];
            if (_orgPersistenceMessage isNotEqualTo "") then {
                _warning = format ["%1 %2", _warning, _orgPersistenceMessage];
            };

            ["ERROR", format ["Credit repayment for %1 completed with persistence failures: %2", _uid, _persistenceFailures joinString ", "]] call EFUNC(common,log);
            GVAR(BankMessenger) call ["sendAlert", [_uid, "warning", _warning]];
        };

        _self call ["hydrateSession", [_uid, "", false]];
        true
    }],
    ["deposit", compileFinal {
        params [["_uid", "", [""]], ["_amount", 0, [0]]];

        _self call [
            "runMutation",
            [
                _uid,
                "bank:hot:deposit",
                [_uid, str _amount, toJSON (GVAR(BankPayloadBuilder) call ["buildOperationContext", [_uid]])],
                false,
                format ["Deposited $%1", [_amount] call EFUNC(common,formatNumber)]
            ]
        ]
    }],
    ["hydrateSession", compileFinal {
        params [["_uid", "", [""]], ["_mode", "", [""]], ["_resetAuthorization", false, [false]]];

        private _payload = GVAR(BankPayloadBuilder) call ["buildHydratePayload", [_uid, _mode, _resetAuthorization]];
        if (_payload isEqualTo createHashMap) exitWith { false };

        private _player = [_uid] call EFUNC(common,getPlayer);
        if (isNull _player) exitWith { false };

        [CRPC(bank,responseHydrateBank), [_payload], _player] call CFUNC(targetEvent);
        true
    }],
    ["init", compileFinal {
        params [["_uid", "", [""]]];

        if (_uid isEqualTo "") exitWith { createHashMap };

        private _player = [_uid] call EFUNC(common,getPlayer);
        private _playerName = if (isNull _player) then { "Unknown" } else { name _player };
        ["bank:exists", [_uid]] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
        if !(_isSuccess) exitWith {
            ["ERROR", format ["Failed to check if bank account %1 exists in backend.", _uid]] call EFUNC(common,log);
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", "Bank backend is unavailable right now."]];
            createHashMap
        };
        if !(_result isEqualType "") exitWith {
            ["ERROR", format ["Bank exists check for %1 returned an invalid response.", _uid]] call EFUNC(common,log);
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", "Bank backend returned an invalid response."]];
            createHashMap
        };
        if ((_result find "Error:") == 0) exitWith {
            ["ERROR", format ["Bank exists check for %1 failed: %2", _uid, _result]] call EFUNC(common,log);
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _result select [7]]];
            createHashMap
        };

        private _finalAccount = createHashMap;
        if (_result isEqualTo "true") then {
            _finalAccount = _self call ["loadHotBank", [_uid, true, _playerName]];
            ["INFO", format ["Found bank account for %1", _uid]] call EFUNC(common,log);
        } else {
            _finalAccount = GVAR(BankModel) call ["fromPlayer", [_player]];
            _finalAccount set ["uid", _uid];
            if ((_finalAccount getOrDefault ["name", ""]) isEqualTo "") then {
                _finalAccount set ["name", _playerName];
            };

            private _json = _self call ["toJSON", [_finalAccount]];
            ["bank:create", [_uid, _json]] call EFUNC(extension,extCall) params ["_createResult", "_createSuccess"];
            if (!_createSuccess) exitWith {
                ["ERROR", format ["Failed to create bank account %1 in backend.", _uid]] call EFUNC(common,log);
                GVAR(BankMessenger) call ["sendAlert", [_uid, "error", "Failed to create bank account in backend."]];
                createHashMap
            };
            if !(_createResult isEqualType "") exitWith {
                ["ERROR", format ["Bank create for %1 returned an invalid response.", _uid]] call EFUNC(common,log);
                GVAR(BankMessenger) call ["sendAlert", [_uid, "error", "Bank backend returned an invalid create response."]];
                createHashMap
            };
            if ((_createResult find "Error:") == 0) exitWith {
                ["ERROR", format ["Bank create for %1 failed: %2", _uid, _createResult]] call EFUNC(common,log);
                GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _createResult select [7]]];
                createHashMap
            };

            _finalAccount = _self call ["loadHotBank", [_uid, true, _playerName]];
            ["INFO", format ["Created new bank account for %1", _uid]] call EFUNC(common,log);
        };

        if (_finalAccount isEqualTo createHashMap) exitWith {
            ["ERROR", format ["Failed to initialize bank hot state for %1.", _uid]] call EFUNC(common,log);
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", "Bank account hot state could not be initialized."]];
            createHashMap
        };

        _finalAccount = _self call ["normalizeAccount", [_uid, _finalAccount, _playerName]];
        GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalAccount, CRPC(bank,responseInitBank)]];
        _finalAccount
    }],
    ["get", compileFinal {
        params [["_uid", "", [""]], ["_field", "", [""]]];

        private _account = _self call ["loadHotBank", [_uid, false, ""]];
        if (_account isEqualTo createHashMap) then {
            _account = _self call ["loadHotBank", [_uid, true, ""]];
        };

        if (_field isEqualTo "") exitWith { _account };
        _account getOrDefault [_field, nil]
    }],
    ["set", compileFinal {
        params [["_uid", "", [""]], ["_field", "", [""]], ["_value", nil, [[], "", 0, false, createHashMap]], ["_sync", false, [false]]];

        if (_uid isEqualTo "" || { _field isEqualTo "" }) exitWith { createHashMap };

        _self call ["mset", [_uid, createHashMapFromArray [[_field, _value]], _sync]]
    }],
    ["mset", compileFinal {
        params [["_uid", "", [""]], ["_fieldValuePairs", createHashMap, [createHashMap]], ["_sync", false, [false]]];

        if (_uid isEqualTo "" || { !(_fieldValuePairs isEqualType createHashMap) }) exitWith { createHashMap };

        private _result = _self call ["callHotBank", ["bank:hot:patch", [_uid, toJSON _fieldValuePairs]]];
        _self call ["finalizeMutation", [_uid, _result, _sync]]
    }],
    ["save", compileFinal {
        params [["_uid", "", [""]]];
        if (_uid isEqualTo "") exitWith { createHashMap };

        private _envelope = _self call ["callHotBankEnvelope", ["bank:hot:save", [_uid]]];
        private _account = _envelope getOrDefault ["data", createHashMap];
        if (_account isEqualTo createHashMap) exitWith {
            private _message = _envelope getOrDefault ["error", "Bank save failed."];
            ["ERROR", format ["Failed to save bank account %1: %2", _uid, _message]] call EFUNC(common,log);
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _message]];
            createHashMap
        };

        _self call ["normalizeAccount", [_uid, _account, ""]]
    }],
    ["transfer", compileFinal {
        params [["_uid", "", [""]], ["_target", "", [""]], ["_amount", 0, [0]], ["_context", createHashMap, [createHashMap]]];

        private _transferContext = GVAR(BankPayloadBuilder) call ["buildTransferContext", [_uid, _context getOrDefault ["sourceField", "bank"]]];
        private _envelope = _self call [
            "callHotBankEnvelope",
            [
                "bank:hot:transfer",
                [_uid, _target, str _amount, toJSON _transferContext]
            ]
        ];
        private _result = _envelope getOrDefault ["data", createHashMap];
        if (_result isEqualTo createHashMap) exitWith {
            private _message = _envelope getOrDefault ["error", "Bank transfer failed."];
            if (_message isNotEqualTo "") then {
                GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _message]];
            };
            false
        };

        private _sourceAccount = _result getOrDefault ["sourceAccount", createHashMap];
        private _targetAccount = _result getOrDefault ["targetAccount", createHashMap];
        private _finalSourcePatch = _result getOrDefault ["sourcePatch", createHashMap];
        private _finalTargetPatch = _result getOrDefault ["targetPatch", createHashMap];

        if (
            _finalSourcePatch isEqualTo createHashMap
            || { _finalTargetPatch isEqualTo createHashMap }
        ) exitWith {
            private _message = _envelope getOrDefault ["error", "Bank transfer failed."];
            if (_message isNotEqualTo "") then {
                GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _message]];
            };
            false
        };

        if (_sourceAccount isEqualType createHashMap && { _sourceAccount isNotEqualTo createHashMap }) then {
            _self call ["normalizeAccount", [_uid, _sourceAccount, ""]];
        };
        if (_targetAccount isEqualType createHashMap && { _targetAccount isNotEqualTo createHashMap }) then {
            _self call ["normalizeAccount", [_target, _targetAccount, ""]];
        };

        GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalSourcePatch]];
        GVAR(BankMessenger) call ["sendAccountSync", [_target, _finalTargetPatch]];

        private _contextTargetAccount = _context getOrDefault ["targetAccount", createHashMap];
        private _contextAccount = _context getOrDefault ["account", createHashMap];
        private _targetPlayer = [_target] call EFUNC(common,getPlayer);
        private _targetName = if (isNull _targetPlayer) then { _contextTargetAccount getOrDefault ["name", "Recipient"] } else { name _targetPlayer };
        private _player = [_uid] call EFUNC(common,getPlayer);
        private _playerName = if (isNull _player) then { _contextAccount getOrDefault ["name", "Unknown"] } else { name _player };

        GVAR(BankMessenger) call ["sendNotification", [_uid, "info", "Bank", format ["Transferred $%1 to %2", [_amount] call EFUNC(common,formatNumber), _targetName]]];
        GVAR(BankMessenger) call ["sendNotification", [_target, "info", "Bank", format ["Received $%1 from %2", [_amount] call EFUNC(common,formatNumber), _playerName]]];
        true
    }],
    ["validatePin", compileFinal {
        params [["_uid", "", [""]], ["_pin", "", [""]]];

        if (_uid isEqualTo "") exitWith { false };

        private _enteredPin = _pin;
        if !(_enteredPin isEqualType "") then {
            _enteredPin = str _enteredPin;
        };

        private _envelope = _self call [
            "callHotBankEnvelope",
            [
                "bank:hot:validate_pin",
                [_uid, _enteredPin, toJSON (GVAR(BankPayloadBuilder) call ["buildOperationContext", [_uid, "atm"]])]
            ]
        ];

        private _message = _envelope getOrDefault ["error", ""];
        if (_message isNotEqualTo "") then {
            GVAR(BankMessenger) call ["sendAlert", [_uid, "error", _message]];
            false
        } else {
            true
        }
    }],
    ["withdraw", compileFinal {
        params [["_uid", "", [""]], ["_amount", 0, [0]]];

        _self call [
            "runMutation",
            [
                _uid,
                "bank:hot:withdraw",
                [_uid, str _amount, toJSON (GVAR(BankPayloadBuilder) call ["buildOperationContext", [_uid]])],
                false,
                format ["Withdrew $%1", [_amount] call EFUNC(common,formatNumber)]
            ]
        ]
    }],
    ["depositEarnings", compileFinal {
        params [["_uid", "", [""]], ["_amount", 0, [0]]];

        _self call [
            "runMutation",
            [
                _uid,
                "bank:hot:deposit_earnings",
                [_uid, str _amount, toJSON (GVAR(BankPayloadBuilder) call ["buildOperationContext", [_uid]])],
                false,
                format ["Deposited $%1 from earnings", [_amount] call EFUNC(common,formatNumber)]
            ]
        ]
    }]
];

GVAR(BankStore) = createHashMapObject [GVAR(BankBaseStore)];
GVAR(BankStore)
