module analytics.balances;

import handy_http_primitives;
import std.datetime;
import std.stdio;
import std.path;
import std.file;
import slf4d;
import asdf;

import profile.data;
import profile.model;
import account.data;
import account.model;
import account.service;
import analytics.util;

struct BalanceSnapshot {
    long balance;
    string timestamp;
}

struct AccountBalanceTimeSeries {
    ulong accountId;
    string currencyCode;
    BalanceSnapshot[] balanceTimeSeries;
}

struct TotalBalanceTimeSeries {
    string currencyCode;
    BalanceSnapshot[] balanceTimeSeries;
}

struct BalanceTimeSeriesAnalytics {
    AccountBalanceTimeSeries[] accounts;
    TotalBalanceTimeSeries[] totals;
}

void computeAccountBalanceTimeSeries(Profile profile, ProfileRepository profileRepo) {
    ProfileDataSource ds = profileRepo.getDataSource(profile);
    Account[] accounts = ds.getAccountRepository().findAll();

    // Initialize the data structure that'll store the analytics info.
    BalanceTimeSeriesAnalytics data;
    foreach (account; accounts) {
        data.accounts ~= AccountBalanceTimeSeries(
            account.id,
            account.currency.code.idup,
            []
        );
    }

    foreach (timestamp; generateTimeSeriesTimestamps(days(1), 365)) {
        // Compute the balance of each account at this timestamp.
        foreach (idx, account; accounts) {
            auto balance = getBalance(ds, account.id, timestamp);
            if (!balance.isNull) {
                data.accounts[idx].balanceTimeSeries ~= BalanceSnapshot(
                    balance.value,
                    timestamp.toISOExtString()
                );
            }
        }

        // Compute total balances for this timestamp.
        auto totalBalances = getTotalBalanceForAllAccounts(ds, timestamp);
        foreach (bal; totalBalances) {
            // Assign the balance to one of our running totals.
            bool currencyFound = false;
            foreach (ref currencyTotal; data.totals) {
                if (currencyTotal.currencyCode == bal.currency.code) {
                    currencyTotal.balanceTimeSeries ~= BalanceSnapshot(
                        bal.balance,
                        timestamp.toISOExtString()
                    );
                    currencyFound = true;
                    break;
                }
            }
            if (!currencyFound) {
                data.totals ~= TotalBalanceTimeSeries(
                    bal.currency.code.idup,
                    [BalanceSnapshot(bal.balance, timestamp.toISOExtString())]
                );
            }
        }
    }

    ds.doTransaction(() {
        ds.getPropertiesRepository().deleteAllByPrefix("analytics");
        ds.getPropertiesRepository().setProperty(
            "analytics.balanceTimeSeries",
            serializeToJsonPretty(data)
        );
    });
    infoF!"Computed account balance analytics for user %s, profile %s."(
        profile.username,
        profile.name
    );
}