@using System.Text.RegularExpressions
@using Radzen.Blazor.Rendering
@using RadzenBlazorDemos.Models.GitHub
@using RadzenBlazorDemos.Services
@inject GitHubService GitHub

<RadzenStack Gap="1rem" Style="position: relative">
    <RadzenCard Visible=@fetchingData Style="z-index: 3; text-align: center; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, .5)">
        <RadzenCard Visible=@(error == null) class="rz-my-12 rz-mx-auto rz-p-12" Style="width: 80%">
            <RadzenText TextStyle="TextStyle.H6" class="rz-my-12">Fetching data from GitHub...</RadzenText>
            <RadzenText TextStyle="TextStyle.Subtitle1">Page @currentPage of @totalPages</RadzenText>
            <RadzenProgressBar Value=@currentPage Max=@totalPages ShowValue="false" Style="display: inline-block; width: 180px; margin-top: 16px" />
        </RadzenCard>
        <RadzenCard Visible=@(error != null) class="rz-my-12 rz-mx-auto rz-p-12" Style="width: 80%">
            <RadzenText TextStyle="TextStyle.H6" class="rz-my-8">An error has occurred: @error. Try reloading your browser.</RadzenText>
        </RadzenCard>
    </RadzenCard>
    <RadzenRow JustifyContent="JustifyContent.SpaceBetween" AlignItems="AlignItems.Center" class="rz-mb-4">
        <RadzenColumn Size="12">
            <RadzenText TextStyle="TextStyle.Subtitle1" class="rz-m-0">
                Sample dashboard that uses data from the <RadzenLink Text="ASP.NET GitHub repository" Path="https://github.com/dotnet/aspnetcore/issues" target="_blank"/>. Data is updated 24 hours.
            </RadzenText>
        </RadzenColumn>
    </RadzenRow>

    <RadzenRow Style="--rz-text-h2-line-height: 1; --rz-text-h2-font-weight: 200;">
        <RadzenColumn Size="12" SizeMD="12" SizeXL="2">
            <RadzenRow>
                <RadzenColumn Size="12" SizeMD="4" SizeXL="12">
                    <RadzenCard Variant="Variant.Outlined" Style="height: 100%;">
                        <RadzenStack JustifyContent="JustifyContent.SpaceBetween" Style="height: 100%;" >
                            <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Start" JustifyContent="JustifyContent.SpaceBetween">
                                <RadzenText TextStyle="TextStyle.H6">Progress</RadzenText>
                                <RadzenText TextStyle="TextStyle.H2" class="rz-color-info rz-m-0">@($"{Math.Round(closeRatioPercentage)}%")</RadzenText>
                            </RadzenStack>
                            <RadzenProgressBar ProgressBarStyle="ProgressBarStyle.Info" @bind-Value=@closeRatioPercentage ShowValue="false" Style="--rz-progressbar-background-color: var(--rz-info-lighter); --rz-progressbar-height: 1rem;"/>
                        </RadzenStack>
                    </RadzenCard>
                </RadzenColumn>
                <RadzenColumn Size="12" SizeMD="4" SizeXL="12">
                    <RadzenCard Variant="Variant.Outlined" Style="height: 100%;">
                        <RadzenStack Gap="0">
                            <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Start" JustifyContent="JustifyContent.SpaceBetween">
                                <RadzenText TextStyle="TextStyle.H6">Open Issues</RadzenText>
                                <RadzenText TextStyle="TextStyle.H2" class="rz-color-success rz-m-0">@openIssues?.Count()</RadzenText>
                            </RadzenStack>
                            <RadzenSparkline Style="width: 100%; height: 80px;">
                                <RadzenColumnSeries Data=@openIssuesByDate ValueProperty="Count" CategoryProperty="Week" Title="Issues" Fill="var(--rz-success)" />
                                <RadzenColumnOptions Margin="0" />
                                <RadzenCategoryAxis Padding="20" Visible="false" />
                            </RadzenSparkline>
                        </RadzenStack>
                    </RadzenCard>
                </RadzenColumn>
                <RadzenColumn Size="12" SizeMD="4" SizeXL="12">
                    <RadzenCard Variant="Variant.Outlined" Style="height: 100%;">
                        <RadzenStack Gap="0">
                            <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Start" JustifyContent="JustifyContent.SpaceBetween">
                                <RadzenText TextStyle="TextStyle.H6">Closed Issues</RadzenText>
                                <RadzenText TextStyle="TextStyle.H2" class="rz-color-danger rz-m-0">@closedIssues?.Count()</RadzenText>
                            </RadzenStack>
                            <RadzenSparkline Style="width: 100%; height: 80px;">
                                <RadzenColumnSeries Data=@closedIssuesByDate ValueProperty="Count" CategoryProperty="Week" Title="Issues" Fill="var(--rz-danger)" />
                                <RadzenColumnOptions Margin="0" Width="30" />
                                <RadzenCategoryAxis Padding="20" Visible="false" />
                            </RadzenSparkline>
                        </RadzenStack>
                    </RadzenCard>
                </RadzenColumn>
            </RadzenRow>
        </RadzenColumn>
        <RadzenColumn Size="12" SizeMD="6" SizeLG="6">
            <RadzenCard Variant="Variant.Outlined" Style="height: 100%;">
                <RadzenStack Style="height: 100%;" JustifyContent="JustifyContent.SpaceBetween">
                    <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Start" JustifyContent="JustifyContent.SpaceBetween">
                        <RadzenText TextStyle="TextStyle.H6">All Issues</RadzenText>
                        <RadzenText TextStyle="TextStyle.H2" class="rz-m-0">@issues?.Count()</RadzenText>
                    </RadzenStack>
                    <RadzenChart Style="width: 100%; height: 80%;">
                        <RadzenAreaSeries Data=@openIssuesByDate ValueProperty="Count" CategoryProperty="Week" Title="Open Issues" Smooth="true" Stroke="var(--rz-success)" Fill="var(--rz-success-lighter)">
                            <RadzenMarkers Visible="true" MarkerType="MarkerType.Circle" />
                        </RadzenAreaSeries>
                        <RadzenAreaSeries Data=@closedIssuesByDate ValueProperty="Count" CategoryProperty="Week" Title="Closed Issues" Smooth="true" Stroke="var(--rz-danger)" Fill="var(--rz-danger-lighter)">
                            <RadzenMarkers Visible="true" MarkerType="MarkerType.Circle" />
                        </RadzenAreaSeries>
                        <RadzenValueAxis>
                            <RadzenGridLines Visible="true" />
                        </RadzenValueAxis>
                        <RadzenCategoryAxis Padding="40" Formatter=@(value => ((DateTime)value).ToString("MMMM d")) />
                        <RadzenLegend Visible="false" />
                    </RadzenChart>
                </RadzenStack>
            </RadzenCard>
        </RadzenColumn>
        <RadzenColumn Size="12" SizeMD="6" SizeXL="4">
            <RadzenCard Variant="Variant.Outlined" Style="height: 100%; --rz-card-padding: 0;">
                <RadzenTabs Style="height: 100%; min-height: 330px">
                    <Tabs>
                        <RadzenTabsItem Text="Top Contributors">
                            <RadzenChart Style="width: 100%; height: 100%;">
                                <RadzenDonutSeries Data=@openByGroups CategoryProperty="Name" ValueProperty="Count" />
                            </RadzenChart>
                        </RadzenTabsItem>
                        <RadzenTabsItem Text="Top Labels">
                            <RadzenChart Style="width: 100%; height: 100%;">
                                <RadzenDonutSeries Data=@labelGroups CategoryProperty="Label" ValueProperty="Count" Fills=@labelColors />
                            </RadzenChart>
                        </RadzenTabsItem>
                        <RadzenTabsItem Text="Most Active Member">
                            <div class="rz-text-align-center">
                                <img Style="width: 120px; border-radius: 50%; margin: 20px 0;" src=@mostActiveMember?.AvatarUrl alt="Most active member avatar" />
                                <h4>
                                    @mostActiveMember?.Login
                                </h4>
                            </div>
                        </RadzenTabsItem>
                    </Tabs>
                </RadzenTabs>
            </RadzenCard>
        </RadzenColumn>
    </RadzenRow>

    <RadzenRow>
        <RadzenColumn Size="12">
            <RadzenDataGrid Data=@filteredIssues Style="height: 600px" AllowFiltering="true" FilterPopupRenderMode="PopupRenderMode.OnDemand" AllowSorting="true">
                <HeaderTemplate>
                    <RadzenDropDown Data=@issueStates @bind-Value="selectedState" Change=@FilterIssues>
                        <Template Context="issueState">
                            @Enum.GetName(typeof(IssueState), issueState) issues
                        </Template>
                    </RadzenDropDown>
                </HeaderTemplate>
                <Columns>
                    <RadzenDataGridColumn Property="@nameof(Issue.State)" Title="State" Width="120px">
                        <FilterTemplate>
                            <RadzenDropDown Style="width: 100%" Data=@issueStates @bind-Value="selectedState" Change=@FilterIssues>
                                <Template Context="issueState">
                                    @issueState.ToString()
                                </Template>
                            </RadzenDropDown>
                        </FilterTemplate>
                        <Template Context="issue">
                            @if (issue.State == IssueState.Open)
                            {
                                <RadzenBadge Text="Open" IsPill="true" Variant="Variant.Flat" Shade="Shade.Light" BadgeStyle="BadgeStyle.Success" Style="width: 60px;"/>
                            }
                            else
                            {
                                <RadzenBadge Text="Closed" IsPill="true" Variant="Variant.Flat" Shade="Shade.Light" BadgeStyle="BadgeStyle.Danger" Style="width: 60px;"/>
                            }
                        </Template>
                    </RadzenDataGridColumn>
                    <RadzenDataGridColumn Property="@nameof(Issue.Title)" Title="Title" Width="650px">
                        <Template Context="issue">
                            <RadzenLink Path=@issue.Url Text=@issue.Title Target="_blank" />
                        </Template>
                    </RadzenDataGridColumn>
                    <RadzenDataGridColumn Title="Labels" Sortable="false">
                        <FilterTemplate>
                            <RadzenDropDown Style="width: 100%" AllowClear="true" AllowFiltering="true" Multiple="true" Data=@labels @bind-Value="selectedLabels" Change=@FilterIssues>
                                <Template Context="label">
                                    @Regex.Replace(label, ":\\w+:", "")
                                </Template>
                            </RadzenDropDown>
                        </FilterTemplate>

                        <Template Context="issue">
                            @foreach(var label in issue.Labels)
                            {
                                <span class="rz-badge rz-badge-pill rz-me-1 rz-mb-1" Style="background-color: #@label.Color">@Regex.Replace(label.Name, ":\\w+:", "")</span>
                            }
                        </Template>
                    </RadzenDataGridColumn>
                    <RadzenDataGridColumn Title="User" Property="User.Login" FilterValue="@selectedUser?.Login">
                        <FilterTemplate>
                            <RadzenDropDown AllowClear="true" AllowFiltering="true" Data=@users TextProperty="Login" @bind-Value="selectedUser" Change=@FilterIssues>
                                <Template Context="user">
                                    <div Style="white-space: nowrap">
                                        <img Style="width: 24px; height: 24px; border-radius: 50%; margin-inline-end: 8px;" src=@user.AvatarUrl alt="User avatar"/>@user.Login
                                    </div>
                                </Template>
                            </RadzenDropDown>
                        </FilterTemplate>
                        <Template Context="issue">
                            <img Style="width: 32px; height: 32px; border-radius: 50%; margin-inline-end: 8px; border: 1px solid #cccccc;" src=@issue.User.AvatarUrl alt="User avatar" /><b>@issue.User.Login</b>
                        </Template>
                    </RadzenDataGridColumn>
                </Columns>
            </RadzenDataGrid>
        </RadzenColumn>
    </RadzenRow>
</RadzenStack>

<style>
    .rz-tabview-panel {
        height: 100%;
    }

    .rz-default,
    .rz-dark,
    .rz-standard,
    .rz-standard-dark,
    .rz-software,
    .rz-software-dark,
    .rz-humanistic,
    .rz-humanistic-dark {
        .rz-card:has(.rz-tabview) {
            --rz-card-border: none;
        }
    }
</style>

@code {
    IEnumerable<Issue> issues;
    IEnumerable<Issue> openIssues;
    IEnumerable<Issue> closedIssues;

    class IssueGroup
    {
        public int Count { get; set; }
        public DateTime Week { get; set; }
    }

    class LabelGroup
    {
        public int Count { get; set; }
        public string Label { get; set; }
        public string Color { get; set; }
    }

    class UserGroup
    {
        public int Count { get; set; }
        public string Name { get; set; }
    }

    IEnumerable<IssueGroup> openIssuesByDate;
    IEnumerable<IssueGroup> closedIssuesByDate;
    IEnumerable<LabelGroup> labelGroups;
    IEnumerable<UserGroup> openByGroups;
    IEnumerable<User> users;
    IEnumerable<Issue> filteredIssues;
    IEnumerable<string> labels;
    IEnumerable<string> selectedLabels;
    IEnumerable<IssueState> issueStates = Enum.GetValues(typeof(IssueState)).Cast<IssueState>();
    IEnumerable<string> labelColors;
    IssueState selectedState = IssueState.All;
    User mostActiveMember;
    User selectedUser;
    double closeRatio = 0;
    double closeRatioPercentage = 0;
    int currentPage = 0;
    int totalPages = 0;
    bool fetchingData = false;
    string error = null;

    class UserComparer : IEqualityComparer<User>
    {
        public bool Equals(User x, User y)
        {
            return x.Login.Equals(y.Login);
        }

        public int GetHashCode(User user)
        {
            return user.Login.GetHashCode();
        }
    }

    class LabelComparer : IEqualityComparer<Label>
    {
        public bool Equals(Label x, Label y)
        {
            return x.Name.Equals(y.Name);
        }

        public int GetHashCode(Label user)
        {
            return user.Name.GetHashCode();
        }
    }

    void FilterIssues()
    {
        filteredIssues = issues.OrderByDescending(issue => issue.CreatedAt);

        if (selectedUser != null)
        {
            filteredIssues = issues.Where(issue => issue.User.Login == selectedUser.Login);
        }

        if (selectedLabels != null)
        {
            foreach (var selectedLabel in selectedLabels)
            {
                filteredIssues = filteredIssues.Where(issue => issue.Labels.Any(label => label.Name == selectedLabel));
            }
        }

        if (selectedState != IssueState.All)
        {
            filteredIssues = filteredIssues.Where(issue => issue.State == selectedState);
        }
    }

    void OnProgress(FetchProgressEventArgs args)
    {
        currentPage = args.Current;
        totalPages = args.Total;

        StateHasChanged();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            GitHub.OnProgress += OnProgress;
            fetchingData = true;
            try
            {
                issues = await GitHub.GetIssues(DateTime.Now);
                filteredIssues = issues.OrderByDescending(issue => issue.CreatedAt);
                openIssues = issues.Where(issue => issue.State == IssueState.Open);
                closedIssues = issues.Where(issue => issue.State == IssueState.Closed);

                closeRatio = closedIssues.Count() / (double)issues.Count();
                closeRatioPercentage = closeRatio * 100;

                openIssuesByDate = openIssues.GroupBy(issue => issue.CreatedAt.EndOfWeek())
                        .Select(group => new IssueGroup
                        {
                            Week = group.Key,
                            Count = group.Count()
                        });

                closedIssuesByDate = closedIssues.GroupBy(issue => issue.ClosedAt.Value.EndOfWeek())
                        .Select(group => new IssueGroup
                        {
                            Week = group.Key,
                            Count = group.Count()
                        });

                labels = issues.SelectMany(issue => issue.Labels).Select(label => label.Name).Distinct();

                labelGroups = issues.SelectMany(issue => issue.Labels)
                                    .GroupBy(label => label, new LabelComparer())
                                    .Select(group => new LabelGroup { Label = Regex.Replace(group.Key.Name, ":\\w+:", ""), Color = $"#{group.Key.Color}", Count = group.Count() })
                                    .Where(group => group.Label != "area-blazor")
                                    .OrderByDescending(group => group.Count)
                                    .Take(5);

                labelColors = labelGroups.Select(label => label.Color);

                openByGroups = issues.GroupBy(issue => issue.User.Login)
                                    .Select(group => new UserGroup { Name = group.Key, Count = group.Count() })
                                    .OrderByDescending(group => group.Count)
                                    .Take(7);

                mostActiveMember = issues.SelectMany(issue => issue.Assignees)
                                    .GroupBy(assignee => assignee, new UserComparer())
                                    .Select(group => new { User = group.Key, Count = group.Count() })
                                    .OrderByDescending(group => group.Count)
                                    .Select(group => group.User)
                                    .FirstOrDefault();

                users = issues.Select(issue => issue.User)
                            .Distinct(new UserComparer());

                error = null;
                fetchingData = false;
            }
            catch (Exception ex)
            {
                error = ex.Message;
            }

            StateHasChanged();
        }
    }
}