using System.Diagnostics;
using System.Runtime.CompilerServices;
using GraphicsManager.Enums;
using GraphicsManager.Interfaces;
using GraphicsManager.Objects;
using Luski.net.Interfaces;
using Luski.net.Structures.Main;
using Luski.net.Structures.Public;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common.Input;
using OpenTK.Windowing.GraphicsLibraryFramework;
using Label = GraphicsManager.Objects.Label;

namespace Luski.GUI.MainScreen.UI.PublicServers;

public class ChatMessage : UserControl
{
    private SocketMessage Msg { get; }
    private SocketChannel ch { get; }

    public PublicChat pc;

    private IRenderObject LastObject;
    public List<IRenderObject> MessageObjs = new();
    private Label FirstL;

    public readonly double HorPadding = 12.ScaleDouble(),
        VerticalPadding = 0.ScaleDouble();

    private static Dictionary<MainSocketRemoteUser, List<ChatMessage>> Messages = new();

    public static async Task<ChatMessage> MakeChatMessage(PublicChat p, SocketMessage message)
    {
        SocketUser auth = (SocketUser)(await message.GetAuthor(CancellationToken.None));
        ServerProfile prof = await message.GetProfile(CancellationToken.None);
        Color? c = await prof.GetColor();
        if (c is null) c = await auth.GetColor();
        Color4 c4 = new(c.R, c.G, c.B, c.A);
        return new ChatMessage(p, message, await message.GetParent(CancellationToken.None), prof, await prof.MakeRct(auth, new(40.ScaleInt())), c4);
    }


    private ChatMessage(PublicChat p, SocketMessage message, SocketChannel chan, ServerProfile Author, IRenderObject UserIcon, Color4 UserNameColor)
    {
        pc = p;
        Label label1;
        base.Size = new(723.5.ScaleInt(), 37.ScaleInt());
        ch = chan;
        base.BackgroundColor = new(40, 40, 40, 255);
        Msg = message;
        Anchor = ObjectAnchor.Left | ObjectAnchor.Right;
        
        DateTime time = chan.Epoch.AddMilliseconds(Msg.TimeStamp).ToLocalTime();
        
        string time_str;
        if (Globals.Settings.DayTime)
        {
            if (time.Date == DateTime.Now.ToLocalTime().Date)
            {
                time_str = $"Today at {time:HH:mm}";
            }
            else if (time.Date == DateTime.Now.ToLocalTime().AddDays(-1).Date)
            {
                time_str = $"Yesterday at {time:HH:mm}";
            }
            else
            {
                time_str = $"{time:M/dd/yyyy HH:mm}";
            }
        }
        else
        {
            if (time.Date == DateTime.Now.ToLocalTime().Date)
            {
                time_str = $"Today at {time.ToShortTimeString().Replace('\u202f', ' ')}";
            }
            else if (time.Date == DateTime.Now.ToLocalTime().AddDays(-1).Date)
            {
                time_str = $"Yesterday at {time.ToShortTimeString().Replace('\u202f', ' ')}";
            }
            else
            {
                time_str = $"{time:M/dd/yyyy h:mm tt}";
            }
        }
       
        UserIcon.Location = new(10.ScaleInt(), 2.ScaleInt(), 0);
        Controls.Add(UserIcon);
        Controls.Add(label1 = new Label(Globals.DefaultFont) { Color = UserNameColor, Text = Author.DisplayName });
        label1.Location = new(
            54.ScaleInt(),
            UserIcon.Location.Y,
            0);
        Label label2;
        LastObject = label1;
        FirstL = label1;
        Controls.Add(label2 = new Label(Globals.TopTimeFont) { Location = new(label1.Location.X + label1.Size.X + 8.ScaleInt(), (int)(label1.Location.Y + label1.Font.PixelHeight - Globals.TopTimeFont.PixelHeight), 0), Text = time_str});
        if (!string.IsNullOrWhiteSpace(Msg.Context))
        {
            Label l;
            Controls.Add(l = new Label(Globals.MessageFont) { Location = new(LastObject.Location.X, (int)(UserIcon.Location.Y + UserIcon.Size.Y - Globals.MessageFont.PixelHeight), 0), Text = message.Context});
            LastObject = l;
            LuskiContextMenu lcm = new();
            Label llllll = lcm.AddLabel("Copy Text");
            unsafe
            {
                llllll.Clicked += (o) =>
                {
                    GLFW.SetClipboardString(Globals.ms.WindowPtr, message.Context);
                    return Task.CompletedTask;
                };
            }
            l.ContextMenu = lcm;
            MessageObjs.Add(l);
        }
        Globals.Settings.DayTimeChanged += () => 
        { 
            if (Globals.Settings.DayTime)
            {
                if (time.Date == DateTime.Now.ToLocalTime().Date)
                {
                    time_str = $"Today at {time:HH:mm}";
                }
                else if (time.Date == DateTime.Now.ToLocalTime().AddDays(-1).Date)
                {
                    time_str = $"Yesterday at {time:HH:mm}";
                }
                else
                {
                    time_str = $"{time:M/dd/yyyy HH:mm}";
                }
            }
            else
            {
                if (time.Date == DateTime.Now.ToLocalTime().Date)
                {
                    time_str = $"Today at {time.ToShortTimeString().Replace('\u202f', ' ')}";
                }
                else if (time.Date == DateTime.Now.ToLocalTime().AddDays(-1).Date)
                {
                    time_str = $"Yesterday at {time.ToShortTimeString().Replace('\u202f', ' ')}";
                }
                else
                {
                    time_str = $"{time:M/dd/yyyy h:mm tt}";
                }
            }

            label2.Text = time_str;
            return Task.CompletedTask;
        };
        
        if (Msg.Files.Count > 0)
        {
            int row = 1;
            int files_on_row = 0;
            for (int i = 0; i < Msg.Files.Count; i++)
            {
                double lx = (HorPadding * files_on_row) + LastObject.Location.X + (333 * (files_on_row + 1));
                if (lx > base.Size.X)
                {
                    row++;
                    files_on_row = 0;
                    lx = (HorPadding * files_on_row) + LastObject.Location.X + (333 * (files_on_row + 1));
                }

                files_on_row++;
                IRenderObject cem = ContentEmbed.GetEmbed(this, Msg.Files[i], Msg.ChannelID).Result;
                cem.Location = new((int)(lx - 333), (int)(LastObject.Location.Y + 2 + LastObject.Size.Y + (HorPadding * row) + (66 * (row - 1))), 0);
                LastObject = cem;
                Controls.Add(cem);
            }
        }

        if (LastObject is Label ll) base.Size = new(base.Size.X, (int)(ll.Location.Y + ll.Size.Y + VerticalPadding));
        else base.Size = new(base.Size.X ,(int)(LastObject.Location.Y + LastObject.Size.Y + VerticalPadding));
    }
    

    public async Task AddMessage(SocketMessage msg)
    {
        BlockDraw = true;
        Label newLabel;
        if (!string.IsNullOrWhiteSpace(msg.Context))
        {
            newLabel = new(Globals.MessageFont)
            {
                Text = msg.Context,
                Tag = msg
            };
            if (LastObject is Label l)
            {
                newLabel.Location = new(FirstL.Location.X, (int)(l.Location.Y + l.Size.Y + VerticalPadding), 0);
            }
            else
            {
                newLabel.Location = new(FirstL.Location.X, Size.Y, 0);
            }
            bool result = Uri.TryCreate(newLabel.Text, UriKind.Absolute, out Uri? uriResult) 
                          && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
            if (result)
            {
                newLabel.HoverMouse = MouseCursor.Hand;
                newLabel.Color = Color4.Aqua;
                newLabel.Clicked += NewLabelOnClicked;
            }
            
            newLabel.MouseEnter += NewLabel_MouseEnter;
            newLabel.MouseLeave += NewLabel_MouseLeave;
            Controls.Add(newLabel);
            MessageObjs.Add(newLabel);
            LastObject = newLabel;
            LuskiContextMenu lcm = new();
            Label llllll = lcm.AddLabel("Copy Text");
            unsafe
            {
                llllll.Clicked += (o) =>
                {
                    GLFW.SetClipboardString(Globals.ms.WindowPtr, msg.Context);
                    return Task.CompletedTask;
                };
            }
            newLabel.ContextMenu = lcm;
        }
        

        if (msg.Files.Count > 0)
        {
            int row = 1;
            int filesonrow = 0;
            for (int i = 0; i < msg.Files.Count; i++)
            {
                double lx = (HorPadding * filesonrow) + LastObject.Location.X + (333 * (filesonrow + 1));
                if (lx > Size.X)
                {
                    row++;
                    filesonrow = 0;
                    lx = (HorPadding * filesonrow) + LastObject.Location.X + (333 * (filesonrow + 1));
                }

                filesonrow++;
                IRenderObject cem = await ContentEmbed.GetEmbed(this, msg.Files[i], msg.ChannelID);
                cem.Location = new((int)(lx - 333), (int)(LastObject.Location.Y + 2 + LastObject.Size.Y + (HorPadding * row) + (66 * (row - 1))), 0);
                LastObject = cem;
                Controls.Add(cem);
            }
        }
        if (LastObject is Label ll) Size = new(Size.X, (int)(ll.Location.Y + ll.Size.Y + VerticalPadding));
        else Size = new(Size.X ,(int)(LastObject.Location.Y + LastObject.Size.Y + VerticalPadding));
        BlockDraw = false;
        //if (Parent is not null) Globals.ms.pc.MessageFlow.ReportSizeUpdate(this);
        TryDraw();
    }

    private Task NewLabelOnClicked(IRenderObject arg)
    {
        try
        {
            Label m = (arg as Label)!;
            if (OperatingSystem.IsWindows())
                Process.Start(m.Text);
            else if (OperatingSystem.IsLinux())
            {
                if (m.Tag is string s) Process.Start("xdg-open", s);
                else Process.Start("xdg-open", m.Text);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
        return Task.CompletedTask;
    }

    readonly List<Label> Labels = new();
    private async Task NewLabel_MouseLeave(IRenderObject sender)
    {
        if (sender is not Label label) return;
        if (label.Tag is not SocketMessage Message) return;
        DateTime time = (await Message.GetParent(CancellationToken.None)).Epoch.AddMilliseconds(Message.TimeStamp).ToLocalTime();
        string b; 
        if (!Globals.Settings.DayTime) b = time.ToString("h:mm tt");
        else b = time.ToString("HH:mm");
        Globals.Settings.DayTimeChanged += () => 
        { 
            if (!Globals.Settings.DayTime) b = time.ToString("h:mm tt");
            else b = time.ToString("HH:mm"); 
            return Task.CompletedTask;
            
        };
        Label[] l = Labels.Where(s => s.Text == b).ToArray();
        if (l.Any())
        {
            Controls.Remove(l.First());
            Labels.Remove(l.First());
        }
        Window!.TryDraw();
    }
    
    private async Task NewLabel_MouseEnter(IRenderObject sender)
    {
        if (sender is not Label label) return;
        if (label.Tag is not SocketMessage Message) return;
        DateTime time = (await Message.GetParent(CancellationToken.None)).Epoch.AddMilliseconds(Message.TimeStamp).ToLocalTime();
        Label m;
        if (!Globals.Settings.DayTime) m = new( Globals.SmallTimeFont)
        {
            Text = time.ToString("h:mm tt"),
        };
        else m = new( Globals.SmallTimeFont)
        {
            Text = time.ToString("HH:mm"),
        };
        Globals.Settings.DayTimeChanged += () => 
        { 
            if (!Globals.Settings.DayTime) m.Text = time.ToString("h:mm tt");
            else m.Text = time.ToString("HH:mm");
            return Task.CompletedTask;
        };
        m.Location = new(
            label.Location.X - m.Size.X - 5.ScaleInt(),
            (int)(label.Location.Y + label.Font.PixelHeight - Globals.SmallTimeFont.PixelHeight),
            0);
        
        Controls.Add(m);
        Labels.Add(m);
        Window!.TryDraw();
    }
}