using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
using RustService = AIStudio.Tools.RustService;
namespace AIStudio.Chat;
/// 
/// The UI component for a chat content block, i.e., for any IContent.
/// 
public partial class ContentBlockComponent : ComponentBase
{
    /// 
    /// The role of the chat content block.
    /// 
    [Parameter]
    public ChatRole Role { get; init; } = ChatRole.NONE;
    /// 
    /// The content.
    /// 
    [Parameter]
    public IContent Content { get; init; } = new ContentText();
    
    /// 
    /// The content type.
    /// 
    [Parameter]
    public ContentType Type { get; init; } = ContentType.NONE;
    
    /// 
    /// When was the content created?
    /// 
    [Parameter]
    public DateTimeOffset Time { get; init; }
    
    /// 
    /// Optional CSS classes.
    /// 
    [Parameter]
    public string Class { get; set; } = string.Empty;
    
    [Parameter]
    public bool IsLastContentBlock { get; set; } = false;
    [Parameter]
    public Func? RemoveBlockFunc { get; set; }
    
    [Parameter]
    public Func? RegenerateFunc { get; set; }
    
    [Parameter]
    public Func RegenerateEnabled { get; set; } = () => false;
    
    [Inject]
    private RustService RustService { get; init; } = null!;
    
    [Inject]
    private ISnackbar Snackbar { get; init; } = null!;
    
    [Inject]
    private SettingsManager SettingsManager { get; init; } = null!;
    
    [Inject]
    private IDialogService DialogService { get; init; } = null!;
    private bool HideContent { get; set; }
    #region Overrides of ComponentBase
    protected override async Task OnInitializedAsync()
    {
        // Register the streaming events:
        this.Content.StreamingDone = this.AfterStreaming;
        this.Content.StreamingEvent = () => this.InvokeAsync(this.StateHasChanged);
        
        await base.OnInitializedAsync();
    }
    /// 
    /// Gets called when the content stream ended.
    /// 
    private async Task AfterStreaming()
    {
        // Might be called from a different thread, so we need to invoke the UI thread:
        await this.InvokeAsync(async () =>
        {
            //
            // Issue we try to solve: When the content changes during streaming,
            // Blazor might fail to see all changes made to the render tree.
            // This happens mostly when Markdown code blocks are streamed.
            //
            
            // Hide the content for a short time:
            this.HideContent = true;
            
            // Let Blazor update the UI, i.e., to see the render tree diff:
            this.StateHasChanged();
            
            // Show the content again:
            this.HideContent = false;
            
            // Let Blazor update the UI, i.e., to see the render tree diff:
            this.StateHasChanged();
            
            // Inform the chat that the streaming is done:
            await MessageBus.INSTANCE.SendMessage(this, Event.CHAT_STREAMING_DONE);
        });
    }
    #endregion
    /// 
    /// Copy this block's content to the clipboard.
    /// 
    private async Task CopyToClipboard()
    {
        switch (this.Type)
        {
            case ContentType.TEXT:
                var textContent = (ContentText) this.Content;
                await this.RustService.CopyText2Clipboard(this.Snackbar, textContent.Text);
                break;
            
            default:
                this.Snackbar.Add("Cannot copy this content type to clipboard!", Severity.Error, config =>
                {
                    config.Icon = Icons.Material.Filled.ContentCopy;
                    config.IconSize = Size.Large;
                    config.IconColor = Color.Error;
                });
                break;
        }
    }
    
    private string CardClasses => $"my-2 rounded-lg {this.Class}";
    private CodeBlockTheme CodeColorPalette => this.SettingsManager.IsDarkMode ? CodeBlockTheme.Dark : CodeBlockTheme.Default;
    
    private async Task RemoveBlock()
    {
        if (this.RemoveBlockFunc is null)
            return;
        
        var remove = await this.DialogService.ShowMessageBox(
            "Remove Message",
            "Do you really want to remove this message?",
            "Yes, remove it",
            "No, keep it");
        
        if (remove.HasValue && remove.Value)
            await this.RemoveBlockFunc(this.Content);
    }
    
    private async Task RegenerateBlock()
    {
        if (this.RegenerateFunc is null)
            return;
        
        if(this.Role is not ChatRole.AI)
            return;
        
        var regenerate = await this.DialogService.ShowMessageBox(
            "Regenerate Message",
            "Do you really want to regenerate this message?",
            "Yes, regenerate it",
            "No, keep it");
        
        if (regenerate.HasValue && regenerate.Value)
            await this.RegenerateFunc(this.Content);
    }
}