using AnotherReplayReader.ReplayFile;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace AnotherReplayReader
{
    public static class ReplayAutoSaver
    {
        private static int _errorMessageCount = 0;

        public static void SpawnAutoSaveReplaysTask(string replayFolderPath)
        {
            Task.Run(() => AutoSaveReplays(replayFolderPath));
        }

        private static async Task AutoSaveReplays(string replayFolderPath)
        {
            const string ourPrefix = "自动保存";

            // filename and last write time
            var previousFiles = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);
            // filename and file size
            var lastReplays = new Dictionary<string, long>(StringComparer.OrdinalIgnoreCase);

            while (true)
            {
                try
                {
                    var changed = (from fileName in Directory.GetFiles(replayFolderPath, "*.RA3Replay")
                                   let info = new FileInfo(fileName)
                                   where !info.Name.StartsWith(ourPrefix)
                                   where !previousFiles.ContainsKey(info.FullName) || previousFiles[info.FullName] != info.LastWriteTimeUtc
                                   select info).ToList();

                    foreach (var info in changed)
                    {
                        previousFiles[info.FullName] = info.LastWriteTimeUtc;
                    }

                    var replays = changed.Select(info =>
                    {
                        Debug.Instance.DebugMessage += $"正在尝试检测已更改的文件:{info.FullName}\r\n";
                        try
                        {
                            using var stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                            return new Replay(info.FullName, stream);
                        }
                        catch (Exception e)
                        {
                            Debug.Instance.DebugMessage += $"自动保存录像/检测录像更改时发生错误:{e}\r\n";
                            return null;
                        }
                    }).Where(replay => replay != null);

                    var newLastReplays = from replay in replays
                                         let threshold = Math.Abs((DateTime.UtcNow - replay.Date).TotalSeconds)
                                         let endDate = replay.Date.Add(replay.Length ?? TimeSpan.Zero)
                                         let endThreshold = Math.Abs((DateTime.UtcNow - endDate).TotalSeconds)
                                         where threshold < 40 || endThreshold < 40
                                         select replay;

                    var toBeChecked = newLastReplays.ToDictionary(replay => replay.Path, StringComparer.OrdinalIgnoreCase);
                    foreach (var savedLastReplay in lastReplays.Keys)
                    {
                        if (!toBeChecked.ContainsKey(savedLastReplay))
                        {
                            try
                            {
                                using var stream = File.Open(savedLastReplay, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                                toBeChecked.Add(savedLastReplay, new Replay(savedLastReplay, stream));
                            }
                            catch (Exception e)
                            {
                                Debug.Instance.DebugMessage += $"自动保存录像/检测录像更改时发生错误:{e}\r\n";
                            }
                        }
                    }

                    foreach (var kv in toBeChecked)
                    {
                        Debug.Instance.DebugMessage += $"正在检测录像更改:{kv.Key}\r\n";
                        var replay = kv.Value;
                        if (lastReplays.TryGetValue(kv.Key, out var fileSize))
                        {
                            if (fileSize == replay.Size)
                            {
                                // skip if size is not changed
                                Debug.Instance.DebugMessage += $"已跳过未更改的录像:{kv.Key}\r\n";
                                continue;
                            }
                        }
                        Debug.Instance.DebugMessage += $"将会自动保存已更改的录像:{kv.Key}\r\n";
                        lastReplays[kv.Key] = replay.Size;

                        var date = replay.Date;

                        var playerString = $"{replay.NumberOfPlayingPlayers}名玩家";
                        if (replay.NumberOfPlayingPlayers <= 2)
                        {
                            var playingPlayers = from player in replay.Players
                                                 let faction = ModData.GetFaction(replay.Mod, player.FactionId)
                                                 where faction.Kind != FactionKind.Observer
                                                 select $"{player.PlayerName}({faction.Name})";
                            playerString = playingPlayers.Aggregate(string.Empty, (x, y) => x + y);
                        }

                        var dateString = $"{date.Year}{date.Month:D2}{date.Day:D2}_{date.Hour:D2}{date.Minute:D2}{date.Second:D2}";
                        var destinationPath = Path.Combine(replayFolderPath, $"{ourPrefix}-{playerString}{dateString}.RA3Replay");
                        try
                        {
                            File.Copy(replay.Path, destinationPath, true);
                        }
                        catch (Exception e)
                        {
                            throw new Exception($"复制文件({replay.Path} -> {destinationPath})失败:{e.Message}", e);
                        }
                    }
                }
                catch (Exception e)
                {
                    var errorString = $"自动保存录像时出现错误:\r\n{e}\r\n";
                    Debug.Instance.DebugMessage += errorString;
                    if (Interlocked.Increment(ref _errorMessageCount) == 1)
                    {
                        _ = Application.Current.Dispatcher.InvokeAsync(() =>
                        {
                            try
                            {
                                MessageBox.Show(errorString);
                            }
                            finally
                            {
                                Interlocked.Decrement(ref _errorMessageCount);
                            }
                        });
                    }
                }

                await Task.Delay(10 * 1000).ConfigureAwait(false);
            }
        }
    }
}