using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RedundancyFinder;
using Spectre.Console;
using Spectre.Console.Cli;
namespace RedundancyFinderCLI
{
    internal sealed class FinderCommand : Command<FinderCommand.Settings>
    {
        public sealed class Settings : CommandSettings
        {
            [Description("Paths to search.")]
            [CommandArgument(0, "<searchPaths>")]
            public string[] SearchPaths { get; init; }

            [Description("File extensions to search for. Comma separated.")]
            [CommandOption("-e|--extensions")]
            [DefaultValue(".jpg,.webp,.raw,.pdf,.xsl,.xslx,.doc,.docx,.txt,.jpeg,.mov,.mp4,.mp3,.wav,.bmp,.gif,.png,.cu,.mid,.msb ,.mov,.avi,.wmv,.flv,.m4v,.bak ,.cpr ,.xml,.psd")]
            public string? Extensions { get; init; }

            [Description("Show all information.")]
            [CommandOption("-v|--verbose")]
            [DefaultValue(false)]
            public bool Verbose { get; init; }

            [Description("Output path.")]
            [CommandOption("-o|--output")]
            [DefaultValue("redundancies.json")]
            public string? OutputPath { get; init; }
        }

        CancellationTokenSource cancellation = new CancellationTokenSource();
        Task task = null;
        Finder finder = new Finder();
        Settings settings = null;
        public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
        {
            finder.cancellation = cancellation.Token;
            this.settings = settings;
            var extensions = settings.Extensions.Split(",", StringSplitOptions.RemoveEmptyEntries);

            
            int hashingTasks = 0;
            int hashingTasksFinished = 0;

            // Register the ProcessExit event to save redundancies on exit
            AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
            {
                End();
            };

            // Register the CancelKeyPress event to handle Ctrl+C
            Console.CancelKeyPress += (sender, e) =>
            {
                cancellation.Cancel(); // Cancel the ongoing tasks
                e.Cancel = true; // Prevent the process from terminating immediately

                Global.WriteLine("[yellow]Ctrl+C detected. Saving redundancies before exiting...[/]");

                End();
                
            };

            // Load existing redundancies if the output file exists
            if (File.Exists(settings.OutputPath))
            {
                try
                {
                    var existingData = File.ReadAllText(settings.OutputPath);
                    var existingRedundancies = JsonConvert.DeserializeObject<Dictionary<string, Redundancy>>(existingData);

                    if (existingRedundancies != null)
                    {
                        foreach (var entry in existingRedundancies)
                        {
                            finder.Redundancies[entry.Key] = entry.Value;
                        }

                        Global.WriteLine($"[yellow]Resumed from existing output file: [/]'{settings.OutputPath}'");
                        Global.WriteLine($"[yellow]Loaded {finder.Redundancies.Count} redundancies from the file.[/]");
                    }
                }
                catch (Exception ex)
                {
                    Global.WriteLine($"[red]Failed to load existing output file: {Markup.Escape(ex.Message)}[/]");
                }
            }

            finder.FileError += (sender, e) =>
            {
                if (e.Exception is UnauthorizedAccessException)
                {
                    if (settings.Verbose)
                    {
                        Global.WriteLine($"[red]Access denied to file: [/]{e.Path}. Skipping. Error: [red]{Markup.Escape(e.Exception.Message)}[/]");
                    }
                }
                else
                {
                    if (settings.Verbose)
                    {
                        Global.WriteLine($"[red] Error processing file:\n[/]Path:{e.Path}\n[red]{Markup.Escape(e.Exception.Message)}[/]");
                    }
                }
            };

            finder.DirectoryError += (sender, e) =>
            {
                if (e.Exception is UnauthorizedAccessException)
                {
                    if (settings.Verbose)
                    {
                        Global.WriteLine($"[red]Access denied to directory: [/]{e.Path}. Skipping. Error: [red]{Markup.Escape(e.Exception.Message)}[/]");
                    }
                }
                else
                {
                    if (settings.Verbose)
                    {
                        Global.WriteLine($"[red] Error processing directory:\n[/]Path:{e.Path}\n[red]{Markup.Escape(e.Exception.Message)}[/]");
                    }
                }
            };

            finder.ProcessingFile += (sender, e) =>
            {
                if (settings.Verbose)
                {
                    Global.WriteLine($"[green]Processing file: [/]{e.Path}");
                }
            };

            try
            {

                finder.TaskStarted += (sender, e) =>
                {
                    hashingTasks++;
                    if (settings.Verbose)
                    {
                        Global.WriteLine($"[green]Task started: [/]{e}");
                    }
                };

                finder.FileFound += (sender, e) =>
                {
                    if (settings.Verbose)
                    {
                        Global.WriteLine($"[green]File found: [/]{e.FilePath} [darkgreen]{Global.GetSizeFormat((ulong)e.Size)}[/]");
                    }
                    hashingTasksFinished++;
                };

                task = Task.Run(() =>
                {
                    finder.FindRedundancies(settings.SearchPaths, extensions);
                }, cancellation.Token);
                try
                {

                    task.Wait(cancellation.Token);

                }
                catch (Exception)
                {

                }
                finally
                {

                    End();
                }
            }
            catch (Exception e)
            {
                Global.WriteLine($"[red] Error:\n[/]{e.Message}");
            }
            return 0;
        }

        private void End()
        {
            SaveRedundancies(finder, settings.OutputPath);
            ulong totalSize = finder.Redundancies.Select(x => (ulong)x.Value.FileSize).Aggregate((a, b) => a + b);
            string sizeFormat = Global.GetSizeFormat(totalSize);
            Global.WriteLine($"Total Size: [green]{sizeFormat}[/]");
            Environment.Exit(0); // Exit the application gracefully            
            
        }

        private void SaveRedundancies(Finder finder, string outputPath)
        {
            try
            {
                // Check if path is relative or absolute
                if (!Path.IsPathRooted(outputPath))
                {
                    outputPath = Path.Combine(Directory.GetCurrentDirectory(), outputPath);
                }

                string json = null;
    //            AnsiConsole.Status()
    //.Start(Global.Format($"[yellow]Writing to [/]'{outputPath}' [yellow] This may take a long time.[/]"), ctx =>
    //{
    //    ctx.Spinner(Spinner.Known.Clock);
    //    ctx.SpinnerStyle = Style.Parse("yellow bold");
    //    Thread.Sleep(1000);
        
    //    lock (finder.Redundancies)
    //    {
            
    //    }

    //});
                json = JsonConvert.SerializeObject(finder.Redundancies, Formatting.Indented);
                File.WriteAllText(outputPath, json);

                Global.WriteLine($"[yellow]Wrote [/]{finder.Redundancies.Count}[yellow] redundancies to [/]'{outputPath}'");
            }
            catch (Exception ex)
            {
                Global.WriteLine($"[red]Failed to save redundancies: {ex.Message}[/]");
            }
        }

    }
}
