@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Abstractions.TagHelpers
@using BTCPayServer.Client
@using BTCPayServer.Common
@using BTCPayServer.Components.TruncateCenter
@using BTCPayServer.Plugins.Wabisabi
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using Microsoft.Extensions.Logging
@using NBitcoin
@using WalletWasabi.WabiSabi.Backend.Rounds
@using WalletWasabi.WabiSabi.Client
@using WalletWasabi.WabiSabi.Models
@model object
@inject IScopeProvider ScopeProvider
@inject WabisabiService WabisabiService;
@inject WalletProvider WalletProvider;
@inject WabisabiCoordinatorClientInstanceManager WabisabiCoordinatorClientInstanceManager

<script src="~/Resources/chart.js" type="text/javascript"> </script>

@{
    var available = true;
    @if (((dynamic) Model).CryptoCode != "BTC" || ((dynamic) Model).WalletEnabled is not true)
    {
        return;
    }

    var storeId = ScopeProvider.GetCurrentStoreId();
    Context.Items["cjlite"] = true;
}
@if (available)
{
    {
        var settings = await WabisabiService.GetWabisabiForStore(storeId);
        var enabledSettings = settings.Settings.Where(coordinatorSettings => coordinatorSettings.Enabled);
        var cjHistory = (await WabisabiService.GetCoinjoinHistory(storeId)).Take(5).ToList();

        @if (!enabledSettings.Any())
        {
            <div class="widget" permission="@Policies.CanModifyStoreSettings">
                <partial name="../WabisabiStore/UpdateWabisabiStoreSettings" model="@settings"/>
            </div>
        }
        else
        {
            <div class="widget store-wallet-balance">
                <header>
                    <h3>Recent Coinjoins</h3>
                    @if (cjHistory.Any())
                    {
                        <a asp-controller="WabisabiStore" asp-action="ListCoinjoins" asp-route-storeId="@storeId">View All</a>
                    }
                </header>
                @if (!cjHistory.Any())
                {
                    <p class="text-secondary mt-3 mb-0">
                        There are no recent transactions.
                    </p>
                }
                else
                {
                    <partial name="Wabisabi/CoinjoinHistoryTable" model="cjHistory"/>
                }
            </div>

            var wallet = (BTCPayWallet?) await WalletProvider.GetWalletAsync(storeId);

            var coins = await wallet.GetAllCoins();
            var privacy = wallet.GetPrivacyPercentage(coins, wallet.AnonScoreTarget);

            var privacyPercentage = Math.Round(privacy * 100);
            var data = new
            {
                privacyProgress = privacyPercentage,
                targetScore = wallet.AnonScoreTarget,
                coins = coins.Select(coin => new
                {
                    value = coin.Amount.ToDecimal(MoneyUnit.BTC),
                    score = coin.AnonymitySet,
                    isPrivate = coin.CoinColor(wallet) == AnonsetType.Green,
                    confirmed = coin.Confirmed,
                    id = coin.Outpoint.ToString(),
                    coinjoinInProgress = coin.CoinJoinInProgress
                }).OrderBy(coin => coin.isPrivate).ThenBy(coin => coin.score),
            };
            @if (coins.Any())
            {
                <script>
            
            document.addEventListener("DOMContentLoaded", function () {
        
function getColor(isPrivate, score, maxScore) {
    let normalizedScore = Math.min(Math.max(score, 0), maxScore) / maxScore;
    return isPrivate ? `rgb(81, 177, 62)` : `rgb(255, ${Math.floor(128 * normalizedScore)}, 0)`;
}

function prepareDatasets(data) {
    const coins = data.coins;
    const confirmedCoins = coins;
    const inProgressCoins = coins.filter(coin => coin.coinjoinInProgress);

    return [
        {
            id: "coins",
            label: "Coins",
            coins: confirmedCoins,
            data: confirmedCoins.map(coin => coin.value),
            backgroundColor: confirmedCoins.map(coin => getColor(coin.isPrivate, coin.score, data.targetScore)),
            borderColor: "transparent",
            borderWidth: 3
        },
        {
            id: "inprogresscoins",
            label: "In Progress Coins",
            data: inProgressCoins.map(coin => coin.value),
            backgroundColor: inProgressCoins.map(() => "rgb(56, 151, 37)"),
            alternativeBackgroundColor: inProgressCoins.map(() => "rgb(28, 113, 11)"),
            borderColor: "transparent",
            borderWidth: 3,
            coins: inProgressCoins
        }
    ];
}


            const data = @Json.Serialize(data);
const chartDataset = {
    labels: data.coins.map(coin => coin.id),
    datasets: prepareDatasets(data)
};




const config = {
    type: "doughnut",
    data: chartDataset,
    options: {
        cutout: "70%",
        plugins: {
            legend: { display: false },
            tooltip: {
                callbacks: {
                    labelColor: ()=>{ return undefined },
                    label: function (context) {
                        const coin = context.dataset.coins[context.dataIndex];
                        return [
                            `${coin.value.toFixed(8)} BTC`,
                            `${coin.score.toFixed(2)} anonset`,
                            `${coin.confirmed ? "confirmed" : "unconfirmed"}`,
                            coin.isPrivate ? "private" : "not private",
                            ...(coin.coinjoinInProgress ? ["mixing"] : []),
                            ...(!coin.isPrivate && coin.score >= data.targetScore? ["Coin too close to entry to deem private just yet"] : [])
                        ];
                       },
                     title: function (context) {
                         const coin = context[0].dataset.coins[context[0].dataIndex];
                         //truncated shoudl have first few chars and then ... and then last few chars
                         
                         const truncatedId = coin.id.substring(0, 8) + "..." + coin.id.substring(coin.id.length - 8);
                         return truncatedId;
                     },
                    footer: function (context) {}
                }
            }
        }
    },
    plugins: [{
        beforeDraw: function (chart) {
            let ctx = chart.ctx;
            ctx.save();
            let width = chart.width;
            let height = chart.height;
            ctx.textBaseline = "middle";
            ctx.fillStyle = getComputedStyle(document.body).getPropertyValue('color');

            function calculateFontSize(text, maxWidth, initialFontSize) {
                let fontSize = initialFontSize;
                ctx.font = fontSize + "em sans-serif";
                while (ctx.measureText(text).width > maxWidth && fontSize > 0) {
                    fontSize -= 0.1;
                    ctx.font = fontSize + "em sans-serif";
                }
                return fontSize;
            }

            function getTextHeight(text, fontSize) {
                ctx.font = fontSize + "em sans-serif";
                let metrics = ctx.measureText(text);
                return metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent +5;
            }

            function drawCenteredText(text, posY, maxWidth, fontSize) {
                ctx.font = fontSize + "em sans-serif";
                let textX = (width - ctx.measureText(text).width) / 2;
                ctx.fillText(text, textX, posY);
                return getTextHeight(text, fontSize);
            }

            let pfs = height / 114;
            let textY = height / 4;
            let maxWidth = width * 0.6;


            let lineTexts = [];
            const totalPrivateSum = data.coins.filter(coin => coin.isPrivate).length;
            const totalPrivateValueSum = data.coins.filter(coin => coin.isPrivate).reduce((sum, coin) => sum + coin.value, 0);
            if (totalPrivateSum > 0)
                lineTexts.push(`${totalPrivateSum} coins(${totalPrivateValueSum.toFixed(8)}BTC) private`);
            const totalNonPrivateSum = data.coins.filter(coin => !coin.isPrivate).length;
            const totalNonPrivateValueSum = data.coins.filter(coin => !coin.isPrivate).reduce((sum, coin) => sum + coin.value, 0);
            if (totalNonPrivateSum > 0)
                lineTexts.push(`${totalNonPrivateSum} coins(${totalNonPrivateValueSum.toFixed(8)}BTC) semi/not private`);
            const totalInProgressSum = data.coins.filter(coin => coin.coinjoinInProgress).length;
            const totalInProgressValueSum = data.coins.filter(coin => coin.coinjoinInProgress).reduce((sum, coin) => sum + coin.value, 0);
            if (totalInProgressSum > 0)
                lineTexts.push(`${totalInProgressSum} coins(${totalInProgressValueSum.toFixed(8)}BTC) mixing`);

             let commonFontSize = lineTexts.reduce(
                (size, text) => Math.min(size, calculateFontSize(text, maxWidth, pfs * 0.7)),
                pfs * 0.7
            );

            let totalTextHeight = getTextHeight(`${data.privacyProgress}%`, pfs)  +
                                  getTextHeight("Private",pfs) + 10 +
                                  lineTexts.reduce((totalHeight, text) => totalHeight + getTextHeight(text, commonFontSize), 0);

            // Adjust initial Y position for vertical centering
            textY = (height - totalTextHeight) / 2;

            // Draw the main text (privacy progress) and additional summary text
            textY += drawCenteredText(`${data.privacyProgress}%`, textY, maxWidth, pfs);
            textY += drawCenteredText("Private",textY, maxWidth, pfs) +10;
            lineTexts.forEach(text => {
                textY += drawCenteredText(text, textY, maxWidth, commonFontSize);
            });

            ctx.restore();
        }
    }]
};

const ctx = document.getElementById("cjchart").getContext("2d");
const myChart = new Chart(ctx, config);

function updateInProgressAnimation(chart) {
    chart.data.datasets.forEach(dataset => {
        if (dataset.id === "inprogresscoins") {
            dataset.backgroundColor = dataset.backgroundColor.map((c, i) => {
                const alt = dataset.alternativeBackgroundColor[i];
                const current = dataset.backgroundColor[i];
                dataset.alternativeBackgroundColor[i] = current;
                return alt;
            });
        }
    });
    chart.update();
    setTimeout(() => updateInProgressAnimation(chart), 1000);
}

updateInProgressAnimation(myChart);
                  
            });
            </script>
            }

            <div class="widget store-numbers">

                @if (wallet is { })
                {
                    @if (!((BTCPayKeyChain) wallet.KeyChain).KeysAvailable)
                    {
                        <div class="alert alert-danger d-flex align-items-center" role="alert">
                            <vc:icon symbol="warning"/>
                            <span class="ms-3">This wallet is either not a hot wallet, or enabled in yout store settings and will not be able to participate in coinjoins.</span>
                        </div>
                    }
                }
                <header>
                    <h4>Coinjoin stats</h4>
                    <div class="d-flex gap-1">
                        <a asp-controller="WabisabiStore" asp-action="ToggleActive" asp-route-storeId="@storeId" asp-route-returnUrl="@Context.Request.GetCurrentUrl()" class="fw-semibold">
                            @(settings.Active ? "Deactivate" : "Activate")
                        </a>
                        -
                        <a asp-controller="WabisabiStore" asp-action="UpdateWabisabiStoreSettings" asp-route-storeId="@storeId" class="fw-semibold" permission="@Policies.CanModifyStoreSettings">
                            Configure
                        </a>
                    </div>
                </header>
                <div class="w-100">

                    @if (coins.Any())
                    {
                        <div class="d-flex justify-content-center mb-4" style="max-height: 400px; ">
                            <canvas id="cjchart"></canvas>
                        </div>
                    }

                    <div class="modal modal-lg fade" id="coins" data-bs-keyboard="false" tabindex="-1">
                        <div class="modal-dialog">
                            <div class="modal-content">
                                <div class="modal-header">
                                    <h1 class="modal-title fs-5">Your coins</h1>
                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                </div>
                                <div class="modal-body">

                                    <table class="table">
                                        <thead>

                                        <tr>
                                            <th colspan="3">Coins</th>
                                        </tr>
                                        <tr>
                                            <th>
                                                Anonset
                                            </th>
                                            <th>
                                                Value
                                            </th>
                                            <th>
                                                Labels
                                            </th>
                                        </tr>
                                        </thead>

                                        @foreach (var coin in coins.OrderByDescending(coin => coin.AnonymitySet).ThenByDescending(coin => coin.Amount))
                                        {
                                            <tr>
                                                <td>
                                                    @coin.AnonymitySet.ToString("0.##")
                                                    @if (!coin.IsSufficientlyDistancedFromExternalKeys)
                                                    {
                                                        <span>(too close to entry)</span>
                                                    }
                                                </td>
                                                <td>
                                                    @coin.Amount.ToDecimal(MoneyUnit.BTC) BTC
                                                </td>
                                                <td>

                                                    @for (var index = 0; index < coin.HdPubKey.Labels.Count; index++)
                                                    {
                                                        var label = coin.HdPubKey.Labels.ElementAt(index);
                                                        <vc:truncate-center text="@label" classes="truncate-center-id"></vc:truncate-center>
                                                    }
                                                </td>
                                            </tr>
                                        }
                                    </table>
                                </div>
                                <div class="modal-footer">
                                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="list-group list-group-flush mb-3">
                        @if (enabledSettings.Count() > 1)
                        {
                            <h5 class="list-group-item-heading text-muted">Enabled coordinators</h5>
                        }
                        @{
                            foreach (var setting in enabledSettings)
                            {
                                if (!WabisabiCoordinatorClientInstanceManager.HostedServices.TryGetValue(setting.Coordinator, out var coordinator))
                                {
                                    continue;
                                }

                                RoundState currentRound = null;
                                CoinJoinTracker tracker = null;
                                if (coordinator.CoinJoinManager.TrackedCoinJoins?.TryGetValue(wallet.WalletId, out tracker) is true &&
                                    tracker?.CoinJoinClient?.CurrentRoundId is { } &&
                                    tracker?.CoinJoinClient?.RoundStatusUpdater?.RoundStates?.TryGetValue(tracker?.CoinJoinClient?.CurrentRoundId, out currentRound) is true)
                                {
                                }

                                var statusMsg = coordinator.WasabiCoordinatorStatusFetcher.Connected ? $"Connected to {(coordinator.Coordinator?.ToString() ?? "local")}" : $"Not connected to {(coordinator.Coordinator?.ToString() ?? "local")}";
                                <div class="list-group-item">
                                    <div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
                                        <div class="d-flex flex-wrap align-items-center justify-content-between gap-3" data-bs-toggle="tooltip" title="@statusMsg">
                                            <span class="btcpay-status btcpay-status--@(coordinator.WasabiCoordinatorStatusFetcher.Connected ? "enabled" : "disabled")"></span>
                                            <h6>@coordinator.CoordinatorDisplayName</h6>
                                        </div>
                                        @if (currentRound is not null)
                                        {
                                            <div class="timer cursor-pointer" data-bs-toggle="collapse" data-bs-target="#cj-@currentRound.Id">
                                                <span class="spinner-border spinner-border-sm" role="status">
                                                    <span class="visually-hidden"></span>
                                                </span>
                                                <span class="h6">Mixing</span>
                                                <vc:icon symbol="caret-down"/>
                                            </div>
                                        }
                                        else if (coordinator.WasabiCoordinatorStatusFetcher.Connected)
                                        {
                                            var roundParameters = coordinator.RoundStateUpdater.RoundStates.LastOrDefault(pair => pair.Value.BlameOf == uint256.Zero).Value?.CoinjoinState.Parameters;
                                            coordinator.CoinJoinManager.TrackedWallets.TryGetValue(wallet.WalletId, out var trackedWallet);
                                            if(trackedWallet is {} && setting.RoundWhenEnabled is not null && roundParameters is not null && !BTCPayWallet.IsRoundOk(roundParameters, setting))
                                            {
                                                <a asp-controller="WabisabiStore" asp-action="UpdateWabisabiStoreSettings" asp-route-storeId="@storeId" class="h6 text-danger">
                                                   New terms detected
                                                </a>
                                            }else if (trackedWallet is {})
                                            {
                                                <span class="h6">Idle</span>
                                            }
                                            else
                                            {
                                                <span class="h6">Inactive</span>
                                            }
                                        }
                                    </div>
                                    @{
                                        if (coordinator.CoinPrison is not null)
                                        {
                                            var bannedCoins = coins.Where(coin => coordinator.CoinPrison.IsBanned(coin.Outpoint)).ToArray();
                                            @if (bannedCoins.Any())
                                            {
                                                <div class="text-muted">@bannedCoins.Count() banned coins(for disrupting rounds)</div>
                                            }
                                        }


                                        if (currentRound is not null)
                                        {
                                            <div class="collapse table-responsive @(enabledSettings.Count() == 1 ? "show" : "") m-0 w-100" id="cj-@currentRound.Id">
                                                <table class="table w-100">
                                                    <tr>
                                                        <th scope="">Status</th>
                                                        <td class="text-truncate">@currentRound.Phase.ToString().ToSentenceCase()</td>
                                                    </tr>
                                                    <tr>
                                                        <th scope="">Round id</th>
                                                        <td class="text-truncate">
                                                            <vc:truncate-center text="@currentRound.Id.ToString()" classes="truncate-center-id"></vc:truncate-center>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <th scope="">Mining feerate</th>
                                                        <td >@currentRound.CoinjoinState.Parameters.MiningFeeRate.ToString()</td>
                                                    </tr>
                                                    <tr>
                                                        <th scope="">Coinjoin total inputs</th>
                                                        <td >@currentRound.CoinjoinState.Inputs.Count() inputs (@currentRound.CoinjoinState.Inputs.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC)</td>
                                                    </tr>
                                                    @if (!tracker.CoinJoinClient.CoinsToRegister.IsEmpty)
                                                    {
                                                        var statement = $"Registered {tracker.CoinJoinClient.CoinsInCriticalPhase.Count()} inputs ({tracker.CoinJoinClient.CoinsInCriticalPhase.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC))} BTC)";
                                                        if (tracker.CoinJoinClient.CoinsInCriticalPhase.Count() != tracker.CoinJoinClient.CoinsToRegister.Count())
                                                        {
                                                            statement += $" / {tracker.CoinJoinClient.CoinsToRegister.Count()} inputs ({tracker.CoinJoinClient.CoinsToRegister.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC))} BTC)";
                                                        }

                                                        var inputToolTip = tracker.CoinJoinClient.CoinsToRegister.Aggregate("", (current, coin) => current + $"{coin.Amount.ToDecimal(MoneyUnit.BTC)} BTC\n");
                                                        <tr data-bs-toggle="tooltip" title="@inputToolTip">
                                                            <th scope="">Your inputs</th>
                                                            <td class="">
                                                                <span class="w-100">@statement</span>
                                                                @if (tracker.BannedCoins.Any())
                                                                {
                                                                    <span class="w-100 text-danger">but got @tracker.BannedCoins.Count() inputs (@tracker.BannedCoins.Sum(coin => coin.Coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC) banned</span>
                                                                }
                                                            </td>
                                                        </tr>
                                                    }

                                                    @if (currentRound.Phase >= Phase.OutputRegistration)
                                                    {
                                                        <tr>
                                                            <th scope="">Coinjoin total outputs</th>
                                                            <td >@currentRound.CoinjoinState.Outputs.Count() outputs (@currentRound.CoinjoinState.Outputs.Sum(coin => coin.Value.ToDecimal(MoneyUnit.BTC)) BTC)</td>
                                                        </tr>
                                                        if (tracker.CoinJoinClient.OutputTxOuts is { } outputs)
                                                        {
                                                            var statement = $"{outputs.outputTxOuts.Count()} outputs ({outputs.outputTxOuts.Sum(coin => coin.Value.ToDecimal(MoneyUnit.BTC))} BTC {(outputs.batchedPayments.Any() ? $"{outputs.batchedPayments.Count()} batched payments" : "")}";
                                                            var outputToolTip = outputs.outputTxOuts.Aggregate("", (current, output) => current + $"{output.Value.ToDecimal(MoneyUnit.BTC)} BTC\n");


                                                            <tr data-bs-toggle="tooltip" title="@outputToolTip">
                                                                <th scope="">Your outputs</th>
                                                                <td >@statement</td>
                                                            </tr>
                                                        }
                                                    }
                                                </table>

                                            </div>
                                        }
                                    }
                                </div>
                            }
                        }
                    </div>

                    @if (coins.Any())
                    {
                        <button type="button" class="btn btn-text p-1" data-bs-toggle="modal" data-bs-target="#coins">
                            View coins
                        </button>
                    }
                    @if (wallet.LastLogs.Any())
                    {
                        <button type="button" class="btn btn-text p-1" data-bs-toggle="modal" data-bs-target="#logs">
                            Recent logs
                        </button>
                        <div class="modal modal-lg fade" id="logs" data-bs-keyboard="false" tabindex="-1">
                            <div class="modal-dialog">
                                <div class="modal-content">
                                    <div class="modal-header">
                                        <h3 class="mb-0">Coinjoin logs</h3>
                                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                    </div>
                                    <div class="modal-body  mt-0">
                                        <div class="table-responsive">
                                            
                                            <table class="table">
                                                <thead>
                                                <tr>
                                                    <th>Time</th>
                                                    <th>Coordinator</th>
                                                    <th>Message</th>
                                                </tr>
                                                </thead>
                                                <tbody>
                                                @foreach (var evt in wallet.LastLogs)
                                                {
                                                    string cssClass = evt.level <= (LogLevel) 2 ? "info" : evt.level == (LogLevel) 4 ? "warning" : "danger";
                                                    <tr class="text-@cssClass">
                                                        <td>
                                                            <small class="text-muted" data-timeago-unixms="@evt.time.ToUnixTimeMilliseconds()">@evt.time.ToTimeAgo()</small>
                                                        </td>
                                                        <td>
                                                            <pre>@evt.coordinator</pre>
                                                        </td>
                                                        <td>
                                                            <pre>@evt.message</pre>
                                                        </td>
                                                    </tr>
                                                }
                                                </tbody>
                                            </table>
                                        </div>
                                        <div class="modal-footer">
                                            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    }

                </div>
            </div>
        }
    }
}