@using AliasVault.WorkerStatus.Database
@inherits MainBase

<button @onclick="SmtpClick"
        class="@GetSmtpButtonClasses() mx-3 inline-flex items-center justify-center rounded-xl px-8 py-2 text-white"
        disabled="@(!IsHeartbeatValid())"
        title="@GetButtonTooltip()">
    <span>SmtpService</span>
    @if (SmtpPending)
    {
    <svg class="animate-spin ml-2 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
        <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
        <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
    </svg>
    }
</button>

@code {
    private List<WorkerServiceStatus> ServiceStatus = [];
    private bool InitInProgress;
    private bool SmtpStatus;
    private bool SmtpPending;
    private DateTime LastHeartbeat;

    /// <summary>
    /// The interval in milliseconds for refreshing the service status.
    /// </summary>
    private readonly int AutoRefreshInterval = 5000;

    /// <summary>
    /// CancellationTokenSource for the timer.
    /// </summary>
    private CancellationTokenSource? _timerCancellationTokenSource;

    /// <inheritdoc />
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
            _timerCancellationTokenSource = new CancellationTokenSource();
            _ = RunPeriodicRefreshAsync(_timerCancellationTokenSource.Token);
        }
    }

    /// <summary>
    /// Refreshes the service status periodically while waiting for specified amount of ms in between.
    /// </summary>
    private async Task RunPeriodicRefreshAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            await InitPage();
            await Task.Delay(AutoRefreshInterval, cancellationToken);
        }
    }

    /// <inheritdoc />
    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);

        if (disposing)
        {
            _timerCancellationTokenSource?.Cancel();
            _timerCancellationTokenSource?.Dispose();
        }
    }

    /// <summary>
    /// Gets the CSS classes for the SMTP button based on its current state.
    /// </summary>
    /// <returns>A string containing the CSS classes for the button.</returns>
    private string GetSmtpButtonClasses()
    {
        string buttonClass = "cursor-pointer ";

        if (!IsHeartbeatValid())
        {
            buttonClass += "bg-gray-600";
        }
        else if (SmtpStatus)
        {
            buttonClass += "bg-green-600";
        }
        else
        {
            buttonClass += "bg-red-600";
        }

        return buttonClass;
    }

    /// <summary>
    /// Gets the tooltip text for the SMTP button.
    /// </summary>
    /// <returns>A string containing the tooltip text.</returns>
    private string GetButtonTooltip()
    {
        return IsHeartbeatValid() ? "" : "Heartbeat offline";
    }

    /// <summary>
    /// Checks if the heartbeat is valid (within the last 5 minutes).
    /// </summary>
    /// <returns>True if the heartbeat is valid, false otherwise.</returns>
    private bool IsHeartbeatValid()
    {
        return DateTime.Now <= LastHeartbeat.AddMinutes(5);
    }

    /// <summary>
    /// Handles the click event for the SMTP button.
    /// </summary>
    private async void SmtpClick()
    {
        if (!IsHeartbeatValid())
        {
            return;
        }

        SmtpPending = true;
        StateHasChanged();

        SmtpStatus = !SmtpStatus;
        await UpdateSmtpStatus(SmtpStatus);

        SmtpPending = false;
        StateHasChanged();
    }

    /// <summary>
    /// Initializes the page by fetching service statuses and updating the SMTP status.
    /// </summary>
    private async Task InitPage()
    {
        if (InitInProgress || SmtpPending)
        {
            return;
        }

        try
        {
            InitInProgress = true;
            var dbContext = await DbContextFactory.CreateDbContextAsync();
            ServiceStatus = await dbContext.WorkerServiceStatuses.ToListAsync();

            var smtpEntry = ServiceStatus.Find(x => x.ServiceName == "AliasVault.SmtpService");
            if (smtpEntry != null)
            {
                LastHeartbeat = smtpEntry.Heartbeat;
                SmtpStatus = IsHeartbeatValid() && smtpEntry.CurrentStatus == "Started";
            }

            await InvokeAsync(StateHasChanged);
        }
        finally
        {
            InitInProgress = false;
        }
    }

    /// <summary>
    /// Update the service statuses.
    /// </summary>
    public async Task<bool> UpdateServiceStatus(string serviceName, bool newStatus)
    {
        // Refresh the DbContext to ensure we get the latest data.
        var dbContext = await DbContextFactory.CreateDbContextAsync();
        var entry = await dbContext.WorkerServiceStatuses.Where(x => x.ServiceName == serviceName).FirstOrDefaultAsync();
        if (entry != null)
        {
            string newDesiredStatus = newStatus ? "Started" : "Stopped";
            entry.DesiredStatus = newDesiredStatus;
            await dbContext.SaveChangesAsync();

            // Wait for service to have updated its status.
            var timeout = DateTime.Now.AddSeconds(30);
            while (true)
            {
                if (DateTime.Now > timeout)
                {
                    // Timeout
                    return false;
                }

                dbContext = await DbContextFactory.CreateDbContextAsync();
                var check = await dbContext.WorkerServiceStatuses.Where(x => x.ServiceName == serviceName).FirstAsync();
                if (check.CurrentStatus == newDesiredStatus)
                {
                    // Done
                    return true;
                }
                await Task.Delay(1000);
            }
        }

        return false;
    }

    /// <summary>
    /// Update the SMTP service status.
    /// </summary>
    public async Task<bool> UpdateSmtpStatus(bool newStatus)
    {
        return await UpdateServiceStatus("AliasVault.SmtpService", newStatus);
    }
}
