using System.Collections.Concurrent;
using Renci.SshNet;
using YoutubeExplode;
using YoutubeExplode.Videos;
using Tesses.WebServer;
using static Creds;
using System.Diagnostics.CodeAnalysis;
using YoutubeExplode.Playlists;
using YoutubeExplode.Channels;
using System.Linq;
HttpClient http=new HttpClient();
YoutubeClient ytc=new YoutubeClient(http);
ConcurrentStack<VideoId> videoIds=new ConcurrentStack<VideoId>();
bool isRunning=true;

async Task AddVideo(VideoId id)
{
    if(!string.IsNullOrWhiteSpace(AlreadyExistsUrl))
    {
        try{
        if(await http.GetStringAsync($"{AlreadyExistsUrl}{id.Value}") == "true")
        {
            Console.WriteLine($"{AlreadyExistsUrl}{id.Value} returned true skipping: https://www.youtube.com/watch?v={id.Value}");
            return;
        }
        }catch(Exception)
        {

        }
    }

    using(SshClient client = new SshClient(IP,Port,Username,Password)){
    await client.ConnectAsync(default);
    using(var res=client.CreateCommand($"echo {DirectoryOnServer}/*-{id.Value}.mp4"))
    {
        await res.ExecuteAsync();
    
        if(res.Result == $"{DirectoryOnServer}/*-{id.Value}.mp4\n")
        {
             using(var res2=client.CreateCommand($"cd {DirectoryOnServer} && tytd {id.Value}"))
            {
                await res2.ExecuteAsync();
                Console.WriteLine($"Downloaded: https://www.youtube.com/watch?v={id.Value}");
                _=res2.Result;
            }         
        }
        else
        {
            Console.WriteLine($"Video already exists: https://www.youtube.com/watch?v={id.Value}");
        }

        
    }
    client.Disconnect();
    
        Task.Delay(10000);
    }
}

async Task AddItem(string url)
{
    VideoId? vId = VideoId.TryParse(url);
    PlaylistId? pId = PlaylistId.TryParse(url);
    ChannelId? cId = ChannelId.TryParse(url);
    UserName? username = UserName.TryParse(url); 
    ChannelSlug? slug = ChannelSlug.TryParse(url);  
    ChannelHandle? handle = ChannelHandle.TryParse(url);

    if(url.Length == 11 && vId.HasValue)    
    {
        videoIds.Push(vId.Value);
    }
    else if(pId.HasValue)
    {
        await foreach(var v in ytc.Playlists.GetVideosAsync(pId.Value))
        {
            videoIds.Push(v.Id);
        }
    }
    else if(vId.HasValue)
    {
        videoIds.Push(vId.Value);
    }
    else if(cId.HasValue)
    {
        await foreach(var c in ytc.Channels.GetUploadsAsync(cId.Value))
        {
            videoIds.Push(c.Id);
        }
    }
    else if(username.HasValue)
    {
        cId = (await ytc.Channels.GetByUserAsync(username.Value)).Id;
        await foreach(var c in ytc.Channels.GetUploadsAsync(cId.Value))
        {
            videoIds.Push(c.Id);
        }
    }
    else if(slug.HasValue)
    {
        cId = (await ytc.Channels.GetBySlugAsync(slug.Value)).Id;
        await foreach(var c in ytc.Channels.GetUploadsAsync(cId.Value))
        {
            videoIds.Push(c.Id);
        }
    }
    else if(handle.HasValue)
    {
        cId = (await ytc.Channels.GetByHandleAsync(handle.Value)).Id;
        await foreach(var c in ytc.Channels.GetUploadsAsync(cId.Value))
        {
            videoIds.Push(c.Id);
        }
    }
}

Task.Factory.StartNew(async()=>{
    while(isRunning)
    {
        if(videoIds.TryPop(out var res))
        {
            await AddVideo(res);
        }
    }
},TaskCreationOptions.LongRunning).Wait(0);

MountableServer msvr=new MountableServer(new StaticServer("wwwroot"));

msvr.Mount("/api/AddItemRes/1/",new TYTDServer(AddItem));
msvr.Mount("/api/AddItem/",new TYTDServer(AddItem));
msvr.Mount("/api/AddVideoRes/1/",new TYTDServer(videoIds));
msvr.Mount("/api/AddVideo/",new TYTDServer(videoIds));
RouteServer routeServer=new RouteServer(msvr);
routeServer.Add("/itemsInQueue",async(ctx)=>{
    await ctx.SendJsonAsync(videoIds.Count);
});
routeServer.Add("/queue.json", async(ctx)=>{
    List<string> items = new List<string>();
    foreach(var item in videoIds)
    {
        items.Add(item.Value);
    }
    await ctx.SendJsonAsync(items);
});
routeServer.Add("/add",async(ctx)=>{
    
   
    if(ctx.QueryParams.TryGetFirst("v",out var v) && ctx.QueryParams.TryGetFirst("mode",out var mode))
    {
        if(mode == "add")
            await AddItem(v);
        else
        {
            var id = VideoId.TryParse(v);
            if(id.HasValue)
            {
                using(SftpClient sftpClient=new SftpClient(IP,Port,Username,Password))
                {
                    await sftpClient.ConnectAsync(default);
                    await foreach(var item in sftpClient.ListDirectoryAsync(DirectoryOnServer,default))
                    {
                        if(item.Name.EndsWith($"-{id.Value.Value}.mp4"))
                        {
                            await ctx.WithFileName(item.Name,false).SendStreamAsync(sftpClient.OpenRead(item.FullName));
                            sftpClient.Disconnect();
                            return;
                        }
                    }
                    sftpClient.Disconnect();   
                }
                await ctx.SendRedirectAsync(GetRedirectUrl(id.Value));
                return;
            }
        }
    }
    await ctx.SendRedirectAsync("/");
});
routeServer.StartServer(3252);

public class TYTDServer : Server
{
    bool onlyVideos;
    ConcurrentStack<VideoId>? stk;
   
    Func<string,Task>? cb;
    public TYTDServer(ConcurrentStack<VideoId> stk)
    {
        onlyVideos=true;
        this.stk = stk;
    }

    public TYTDServer(Func<string,Task> cb)
    {
        onlyVideos=false;
        this.cb = cb;
    }
    public override async Task GetAsync(ServerContext ctx)
    {
       
        if(onlyVideos)
        {
            
            string url=ctx.UrlAndQuery.Substring(1);
            
            stk?.Push(url);
            await ctx.SendTextAsync("<script>window.history.back();</script>");
        }
        else
        {
            if(cb == null) {
                await ctx.SendTextAsync("<script>window.history.back();</script>");
      
                return;
            }
            string url=ctx.UrlAndQuery.Substring(1);
            
            await cb(url);
            await ctx.SendTextAsync("<script>window.history.back();</script>");

        }
    }
}