#region Using Statements

using System;
using System.IO;

#endregion

namespace OneDriveArchiver
{
    public static class IoHelper
    {
        #region Constructor

        static IoHelper()
        {
            ProcessedFiles = 0;
        }
        

        #endregion
        #region Properties
        public static int ProcessedFiles
        {
            get;
            set;
        }
        #endregion
        /// <summary>
        ///     Archives a one drive file. It takes the file creation date and month,
        ///     creates the necessary folder structure (if necessary) i.e. Year\Month and
        ///     moves the file to the (new) target folder. The Sync engine of OneDrive then
        ///     will sync the changes being made back to the cloud. Base Path is the path where
        ///     the file was found. So the subfolder will start in the same directory.
        /// </summary>
        /// <param name="appName"></param>
        /// <param name="filename">Filename</param>
        private static void ArchiveOneDriveFile(string appName, string targetPath, string filename)
        {
            try
            {
                if (string.IsNullOrEmpty(filename)) throw new ArgumentException("filename cannot be null.");

                // do a check if the file is empty...
                var fi = new FileInfo(filename);
                if (fi.Length == 0)
                {
                    Console.WriteLine($"OneDriveArchiver: Deleting file {filename} as it has zero bytes content.");
                    File.Delete(filename);
                    return;
                }

                var dateModified = File.GetLastWriteTime(filename);
                var fileMonth = dateModified.Month.ToString("D2");
                var fileYear = dateModified.Year.ToString();
                var fileNameWithOutPath = Path.GetFileName(filename);

                // check if we have to create either year or month folder...
                // start with parent year folder...
                var fullYearFolderPath = Path.Combine(targetPath ?? throw new InvalidOperationException(), fileYear);
                var fullMonthFolderPath = Path.Combine(Path.Combine(targetPath, fileYear), fileMonth);
                
                // create the directories if not existing...
                if (!Directory.Exists(fullYearFolderPath)) Directory.CreateDirectory(fullYearFolderPath);
                if (!Directory.Exists(fullMonthFolderPath)) Directory.CreateDirectory(fullMonthFolderPath);
                
                var newFullFileName = Path.Combine(fullMonthFolderPath, fileNameWithOutPath);
                
                // check if the file at the designated new archive location exists. If so, remove the existing file
                // and replace it with the new copy.
                if (File.Exists(newFullFileName))
                {
                    Console.WriteLine($"{appName}: archive file exists at designated folder. Removing the old file for copying the new one instead.");
                    File.Delete(newFullFileName);
                }
                
                // then move the file...
                File.Move(filename, newFullFileName);
                Console.WriteLine($"Moved onedrive file to {newFullFileName} successfully.");
            }
            catch (Exception ex)
            {
                Console.WriteLine(
                    $"Exception while backing up OneDrive file. Message = {ex.Message}, Stacktrace = {ex.StackTrace}");
            }
        }

        /// <summary>
        /// ProcessFilesForDirectory implementation. Processes all contained directory recursively and
        /// archives the files to the archive directory.
        /// </summary>
        /// <param name="currentDirectoryWithFiles">Current directory for processing</param>
        /// <param name="appName">Name of application</param>
        /// <param name="targetPath">Target archiving path to store files</param>
        public static void ProcessFilesForDirectory(string currentDirectoryWithFiles, string appName, string targetPath)
        {
            var containedDirectories = Directory.GetDirectories(currentDirectoryWithFiles);
            foreach (var containedDirectory in containedDirectories)
            {
                ProcessFilesForDirectory(containedDirectory, appName, targetPath);
            }
            
            // process the files in the directory (if all directories have been traversed...
            var filesToProcess = Directory.GetFiles(currentDirectoryWithFiles);
            if (filesToProcess.Length > 1)
            {
                foreach (var item in filesToProcess)
                {
                    Console.WriteLine($"{appName}: Processing file {item}...");

                    // skip ourselves...
                    if (item.ToLower().Contains("onedrivearchiver"))
                    {
                        Console.WriteLine(
                            $"{appName}: Skipping processing of my own executable. Proceeding...");
                        continue;
                    }
                            
                    // we do not archive desktop.ini either...
                    if (item.ToLower().Contains("desktop.ini"))
                    {
                        Console.WriteLine($"{appName}: skipping desktop.ini file...");
                        continue;
                    }

                    // archive the file
                    IoHelper.ArchiveOneDriveFile(appName, targetPath,item);

                    // increment number of processed files
                    ProcessedFiles += 1;

                    Console.WriteLine($"{appName}: Processing for file {item} completed successfully.");
                }
            }
            else
            {
                // no files found...
                Console.WriteLine($"{appName}: No files found to process.");
            }
        }
    }
}