@page "/{User}"
@using System.Text.RegularExpressions
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Shared.Schemas.Web
@using Microsoft.AspNetCore.Authorization
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Localization
@using Microsoft.Extensions.Localization
@attribute [Authorize]
@inject ApiService                     Api
@inject GlobalComponentSvc             GlobalComponentSvc;
@inject IJSRuntime                     Js;
@inject IStringLocalizer<Localization> Loc;
@inject MetadataService                MetadataService
@inject NavigationManager              Nav;

@if (_init)
{
    <HeadTitle Text="@(UserResponse.DisplayName ?? UserResponse.Username)"/>

    <div class="container">
        @if (UserResponse.Host != null)
        {
            <RemoteUserBanner OriginalPageUrl="@Profile?.Url"/>
        }

        @if (UserResponse.BannerUrl != null)
        {
            <img class="banner" src="@UserResponse.BannerUrl"
                 alt="@(UserResponse.BannerAlt ?? Loc["{0}'s banner", UserResponse.DisplayName ?? UserResponse.Username])"
                 title="@(UserResponse.BannerAlt ?? Loc["{0}'s banner", UserResponse.DisplayName ?? UserResponse.Username])"/>
        }

        <div class="profile-card">
            <div class="header">
                <div class="subheader">
                    <div>
                        <UserAvatar User="@UserResponse" Size="5em" Title="@true"/>
                    </div>
                    <div class="name-section">
                        <div class="name">
                            <UserDisplayName User="@UserResponse"/>
                        </div>
                        <div class="identifier">
                            @@@UserResponse.Username
                            @if (UserResponse.Host != null)
                            {
                                var host = $"@{UserResponse.Host}";
                                @host
                            }
                            @if (Profile != null && Profile.Relations.HasFlag(Relations.FollowedBy))
                            {
                                <span class="follows-badge">Follows you</span>
                            }
                        </div>
                    </div>
                </div>
                <FollowButton User="UserResponse" UserProfile="Profile"/>
                <button @ref="MenuButton" class="context-button" @onclick="ToggleMenu" @onclick:stopPropagation="true" aria-label="more">
                    <Icon Name="Icons.DotsThreeOutline" Size="1.3em"/>
                    <Menu @ref="ContextMenu">
                        @if (Profile != null && Profile.Relations.HasFlag(Relations.Self))
                        {
                            <MenuElement Icon="Icons.Pencil" OnSelect="EditProfile">
                                <Text>@Loc["Edit profile"]</Text>
                            </MenuElement>
                        }
                        else
                        {
                            <MenuElement Icon="Icons.Tooth" OnSelect="Bite">
                                <Text>@Loc["Bite"]</Text>
                            </MenuElement>
                        }
                        @if (UserResponse.Host != null)
                        {
                            <MenuElement Icon="Icons.ArrowsClockwise" OnSelect="RefetchUser">
                                <Text>@Loc["Refetch"]</Text>
                            </MenuElement>
                        }
                        <hr class="rule"/>
                        @if (UserResponse.Host != null && Profile?.Url != null)
                        {
                            <MenuElement Icon="Icons.ArrowSquareOut" OnSelect="OpenOriginal">
                                <Text>@Loc["Open original page"]</Text>
                            </MenuElement>
                        }
                        <MenuElement Icon="Icons.Share" OnSelect="CopyLink">
                            <Text>@Loc["Copy link"]</Text>
                        </MenuElement>
                        @if (UserResponse.Host != null && Profile?.Url != null)
                        {
                            <MenuElement Icon="Icons.ShareNetwork" OnSelect="CopyLinkRemote">
                                <Text>@Loc["Copy link (remote)"]</Text>
                            </MenuElement>
                        }
                        <MenuElement Icon="Icons.At" OnSelect="CopyUsername">
                            <Text>@Loc["Copy username"]</Text>
                        </MenuElement>
                        @if (Profile != null && !Profile.Relations.HasFlag(Relations.Self))
                        {
                            <hr class="rule"/>
                            @if (Profile != null && Profile.Relations.HasFlag(Relations.FollowedBy))
                            {
                                <MenuElement Icon="Icons.LinkBreak" OnSelect="RemoveFromFollowers" Danger>
                                    <Text>@Loc["Remove follower"]</Text>
                                </MenuElement>
                            }
                            @if (Profile != null && Profile.Relations.HasFlag(Relations.Muting))
                            {
                                <MenuElement Icon="Icons.Eye" OnSelect="Unmute">
                                    <Text>@Loc["Unmute"]</Text>
                                </MenuElement>
                            }
                            else
                            {
                                <MenuElement Icon="Icons.EyeSlash" OnSelect="Mute">
                                    <Text>@Loc["Mute"]</Text>
                                </MenuElement>
                                <MenuElement Icon="Icons.EyeSlash" OnSelect="TemporaryMute">
                                    <Text>@Loc["Temporary mute"]</Text>
                                </MenuElement>
                            }
                            @if (Profile != null && Profile.Relations.HasFlag(Relations.Blocking))
                            {
                                <MenuElement Icon="Icons.Prohibit" OnSelect="Unblock">
                                    <Text>@Loc["Unblock"]</Text>
                                </MenuElement>
                            }
                            else
                            {
                                <MenuElement Icon="Icons.Prohibit" OnSelect="Block">
                                    <Text>@Loc["Block"]</Text>
                                </MenuElement>
                            }
                        }
                        <ClosingBackdrop OnClose="ContextMenu.Close"></ClosingBackdrop>
                    </Menu>
                </button>
            </div>
            <ProfileInfo Emojis="@UserResponse.Emojis" User="UserResponse" UserProfile="Profile"/>
        </div>
        @if (UserNotes.Count > 0)
        {
            <div class="notes">
                @foreach (var note in UserNotes)
                {
                    <div class="note-container">
                        <Note NoteResponse="note" OpenNote="true"/>
                    </div>
                }
            </div>
            <ScrollEnd IntersectionChange="AddNotes" ManualLoad="AddNotes" Class="end"/>
        }
    </div>
}
@if (_loading)
{
    <HeadTitle Text="@Loc["Loading"]"/>

    <div class="loading"><LoadingSpinner Scale="2" /></div>
}
@if (_notFound)
{
    <HeadTitle Text="@Loc["User not found"]"/>

    <div>User does not exist!</div>
}
@if (_error)
{
    <HeadTitle Text="@Loc["Error"]"/>

    <div>Failure</div>
}

@code {
    [Parameter] public string?              User         { get; set; }
    [Parameter] public string?              Host         { get; set; }
    private            UserResponse         UserResponse { get; set; } = null!;
    private            UserProfileResponse? Profile      { get; set; }
    private            string?              MinId        { get; set; }
    private            List<NoteResponse>   UserNotes    { get; set; } = [];
    
    private ElementReference MenuButton  { get; set; }
    private Menu             ContextMenu { get; set; } = null!;

    private bool _loading = true;
    private bool _init;
    private bool _notFound;
    private bool _error;
    private bool _fetchLock;

    private async Task GetNotes(string? minId)
    {
        var pq    = new PaginationQuery { Limit = 10, MaxId = minId };
        var notes = await Api.Users.GetUserNotesAsync(UserResponse.Id, pq);
        if (notes is not null && notes.Count > 0)
        {
            MinId = notes.Last().Id;
            UserNotes.AddRange(notes);
            StateHasChanged();
        }
    }

    private async Task AddNotes()
    {
        if (_fetchLock == false)
        {
            _fetchLock = true;
            await GetNotes(MinId);
            _fetchLock = false;
        }
    }

    private async Task LoadProfile()
    {
        try
        {
            if (User is null)
            {
                _notFound = true;
            }
            else
            {
                var pattern      = "^@([^@]+)@?(.+)?$";
                var matches      = Regex.Match(User, pattern);
                var userResponse = await Api.Users.LookupUserAsync(matches.Groups[1].Value, matches.Groups[2].Value);
                if (userResponse is null)
                {
                    _notFound = true;
                }
                else
                {
                    UserResponse = userResponse;
                    Profile      = await Api.Users.GetUserProfileAsync(UserResponse.Id);
                    await GetNotes(null);
                    _init    = true;
                    _loading = false;
                }
            }
        }
        catch (ApiException)
        {
            _loading = false;
            _error   = true;
        }
    }

    protected override async Task OnInitializedAsync()
    {
        await LoadProfile();
    }

    protected override async Task OnParametersSetAsync()
    {
        _init     = false;
        _loading  = true;
        UserNotes = [];
        MinId     = null;
        Profile   = null;
        StateHasChanged();
        await LoadProfile();
    }
    
    private void ToggleMenu() => ContextMenu.Toggle(MenuButton);
    
    private void EditProfile() => Nav.NavigateTo("/settings/profile");

    private async Task Bite()
    {
        await Api.Users.BiteUserAsync(UserResponse.Id);
        await GlobalComponentSvc.NoticeDialog?.Display(Loc["Bit {0}", UserResponse.DisplayName ?? UserResponse.Username])!;
    }

    private async Task RemoveFromFollowersAction(bool confirm)
    {
        if (!confirm) return;

        var removed = await Api.Users.RemoveUserFromFollowersAsync(UserResponse.Id);
        if (!removed) return;

        await GlobalComponentSvc.NoticeDialog?.Display(Loc["Removed {0} from followers", UserResponse.DisplayName ?? UserResponse.Username])!;

        Profile!.Relations &= ~Relations.FollowedBy;
        StateHasChanged();
    }

    private void RemoveFromFollowers() =>
        GlobalComponentSvc.ConfirmDialog?.Confirm(new EventCallback<bool>(this, RemoveFromFollowersAction), Loc["Remove {0} from followers?", UserResponse.DisplayName ?? UserResponse.Username], buttonText: Loc["Remove follower"]);

    private void Mute() =>
        GlobalComponentSvc.ConfirmDialog?.Confirm(new EventCallback<bool>(this, MuteCallback), Loc["Mute {0}?", UserResponse.DisplayName ?? UserResponse.Username], buttonText: Loc["Mute"]);

    private async Task MuteCallback(bool confirm)
    {
        if (!confirm) return;

        try
        {
            await Api.Users.MuteUserAsync(UserResponse.Id, null);

            await GlobalComponentSvc.NoticeDialog?.Display(Loc["Muted {0}", UserResponse.DisplayName ?? UserResponse.Username])!;

            Profile!.Relations += (int)Relations.Muting;
            StateHasChanged();
        }
        catch (ApiException e)
        {
            await GlobalComponentSvc.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
        }
    }

    private void TemporaryMute() =>
        GlobalComponentSvc.SelectDialog?.Select(new EventCallback<object?>(this, TemporaryMuteCallback), Loc["Mute {0}?", UserResponse.DisplayName ?? UserResponse.Username], [(Loc["For 1 hour"], (object)DateTime.Now.AddHours(1)), (Loc["For {0} hours", 3], (object)DateTime.Now.AddHours(3)), (Loc["For {0} hours", 8], (object)DateTime.Now.AddHours(8)), (Loc["For 1 day"], (object)DateTime.Now.AddDays(1)), (Loc["For 1 week"], (object)DateTime.Now.AddDays(7))], Loc["Mute"]);

    private async Task TemporaryMuteCallback(object? value)
    {
        if (value is not DateTime expires) return;

        try
        {
            await Api.Users.MuteUserAsync(UserResponse.Id, expires);

            await GlobalComponentSvc.NoticeDialog?.Display(Loc["Muted {0}", UserResponse.DisplayName ?? UserResponse.Username])!;

            Profile!.Relations += (int)Relations.Muting;
            StateHasChanged();
        }
        catch (ApiException e)
        {
            await GlobalComponentSvc.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
        }
    }

    private void Unmute() =>
        GlobalComponentSvc.ConfirmDialog?.Confirm(new EventCallback<bool>(this, UnmuteCallback), Loc["Unmute {0}?", UserResponse.DisplayName ?? UserResponse.Username], buttonText: Loc["Unmute"]);

    private async Task UnmuteCallback(bool confirm)
    {
        if (!confirm) return;

        try
        {
            await Api.Users.UnmuteUserAsync(UserResponse.Id);

            await GlobalComponentSvc.NoticeDialog?.Display(Loc["Unmuted {0}", UserResponse.DisplayName ?? UserResponse.Username])!;

            Profile!.Relations -= (int)Relations.Muting;
            StateHasChanged();
        }
        catch (ApiException e)
        {
            await GlobalComponentSvc.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
        }
    }

    private void Block() =>
        GlobalComponentSvc.ConfirmDialog?.Confirm(new EventCallback<bool>(this, BlockCallback), Loc["Block {0}?", UserResponse.DisplayName ?? UserResponse.Username], buttonText: Loc["Block"]);

    private async Task BlockCallback(bool confirm)
    {
        if (!confirm) return;

        try
        {
            await Api.Users.BlockUserAsync(UserResponse.Id);

            await GlobalComponentSvc.NoticeDialog?.Display(Loc["Blocked {0}", UserResponse.DisplayName ?? UserResponse.Username])!;

            Profile!.Relations += (int)Relations.Blocking;
            StateHasChanged();
        }
        catch (ApiException e)
        {
            await GlobalComponentSvc.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
        }
    }

    private void Unblock() =>
        GlobalComponentSvc.ConfirmDialog?.Confirm(new EventCallback<bool>(this, UnblockCallback), Loc["Unblock {0}?", UserResponse.DisplayName ?? UserResponse.Username], buttonText: Loc["Unblock"]);

    private async Task UnblockCallback(bool confirm)
    {
        if (!confirm) return;

        try
        {
            await Api.Users.UnblockUserAsync(UserResponse.Id);

            await GlobalComponentSvc.NoticeDialog?.Display(Loc["Unblocked {0}", UserResponse.DisplayName ?? UserResponse.Username])!;

            Profile!.Relations -= (int)Relations.Blocking;
            StateHasChanged();
        }
        catch (ApiException e)
        {
            await GlobalComponentSvc.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
        }
    }

    private void OpenOriginal()
    {
        if (Profile?.Url != null)
            Js.InvokeVoidAsync("open", Profile.Url, "_blank");
    }
    
    private async Task CopyLink()
    {
        var instance = await MetadataService.Instance.Value;
        await Js.InvokeVoidAsync("navigator.clipboard.writeText", $"https://{instance.WebDomain}/@{UserResponse.Username}" + (UserResponse.Host != null ? $"@{UserResponse.Host}" : ""));
    }
    
    private void CopyLinkRemote()
    {
        if (Profile?.Url != null)
            Js.InvokeVoidAsync("navigator.clipboard.writeText", Profile.Url);
    }

    private void CopyUsername() => Js.InvokeVoidAsync("navigator.clipboard.writeText", $"@{UserResponse.Username}" + (UserResponse.Host != null ? $"@{UserResponse.Host}" : ""));

    private async Task RefetchUser() =>
        await GlobalComponentSvc.ConfirmDialog?.Confirm(new EventCallback<bool>(this, RefetchUserCallback), Loc["Refetch {0}'s profile?", UserResponse.DisplayName ?? UserResponse.Username])!;

    private async Task RefetchUserCallback(bool refetch)
    {
        if (!refetch) return;

        try
        {
            var res = await Api.Users.RefetchUserAsync(UserResponse.Id);
            if (res != null)
            {
                UserResponse = res;
                var newProfile = await Api.Users.GetUserProfileAsync(UserResponse.Id);
                if (newProfile != null)
                    Profile = newProfile;
                StateHasChanged();
                await GlobalComponentSvc.NoticeDialog?.Display(Loc["Successfully refetched {0}'s profile", UserResponse.DisplayName ?? UserResponse.Username])!;
            }
        }
        catch (ApiException e)
        {
            await GlobalComponentSvc.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
        }
    }
}
