using FISLib.Connect;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Vinno.FIS.Sonopost.Common;
using Vinno.FIS.Sonopost.Features.Oled;
using Vinno.FIS.Sonopost.Helpers;
using Vinno.FIS.Sonopost.Managers.Interfaces;
using Vinno.IUS.Common.Log;

namespace Vinno.FIS.Sonopost.Managers
{
    internal class ExternalMemoryManager : SonopostManager, IExternalMemoryManager
    {
        private readonly IDeviceManager _deviceManager;
        private readonly ILoginManager _loginManager;
        private readonly IOledManager _oledManager;
        private readonly INetworkManager _networkManager;
        private const string SonopostInfoFile = "SonopostInfo.txt";
        private const string SonopostOperateFile = "SonopostOperater.conf";

        public ExternalMemoryManager()
        {
            _loginManager = AppManager.Instance.GetManager<ILoginManager>();
            _deviceManager = AppManager.Instance.GetManager<IDeviceManager>();
            _deviceManager.UDiskInserted += OnUDiskInserted;
            _oledManager = AppManager.Instance.GetManager<IOledManager>();
            _networkManager = AppManager.Instance.GetManager<INetworkManager>();
        }

        private void OnUDiskInserted(object sender, string letter)
        {
            try
            {
                if (!string.IsNullOrEmpty(letter))
                {
                    WriteSystemInfoToUDisk(letter);
                    ReadInfoFromUDisk(letter);
                }

                Logger.WriteLineInfo($"External Memory Manager UDisk Insert:Disk {letter}");
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"External Memory Manager UDisk insert Error:{ex}");
            }
        }

        private void WriteSystemInfoToUDisk(string driveLetter)
        {
            Task.Run(() =>
            {
                try
                {
                    var path = Path.Combine(driveLetter, SonopostInfoFile);
                    FileHelper.DeleteFile(path);
                    var list = new List<object>();
                    foreach (var item in _networkManager.GetAllNetworkInterfaceInfos())
                    {
                        var info = new
                        {
                            Name = item.Description,
                            Status = item.OperationalStatus.ToString(),
                            EnableDhcp = item.EnableDhcp.Value,
                            Guid = item.Guid,
                            MacAddress = item.MacAddress,
                            IPAddress = item.IpAddress.Value,
                            SubnetMask = item.SubnetMask.Value,
                            GateWay = item.GateWay.Value,
                            PreferredDNSServer = item.PreferredDNSServer.Value,
                            StandbyDNSServer = item.StandbyDNSServer.Value
                        };
                        list.Add(info);
                    }
                    File.AppendAllText(path, JsonHelper.ToJson(list, new JsonSerializerOptions
                    {
                        WriteIndented = true
                    }));
                }
                catch (Exception e)
                {
                    Logger.WriteLineError($"WriteSystemInfoToUDisk Error:{e}");
                }
            });
        }

        private void ReadInfoFromUDisk(string driveLetter)
        {
            Task.Run(() =>
            {
                try
                {
                    var path = Path.Combine(driveLetter, SonopostOperateFile);
                    if (!File.Exists(path))
                    {
                        return;
                    }
                    var operateContext = File.ReadAllText(path);

                    if (string.IsNullOrWhiteSpace(operateContext))
                    {
                        Logger.WriteLineWarn("Not context in Sonopost operate file!!");
                        return;
                    }

                    var info = JsonHelper.JsonToObj<UsbOperateInfo>(operateContext);

                    if (info?.NeedReset == true)
                    {
                        Reset(path, info);
                        return;
                    }
                    if (info?.NetworkInfo.NeedChangeNetwork == true)
                    {
                        ChangeNetworkInfo(info.NetworkInfo);
                    }
                    if (info?.UpdateInfo.NeedForceUpdate == true)
                    {
                        ForceUpdate(driveLetter, info.UpdateInfo);
                        return;
                    }
                    if (info?.NeedLog == true)
                    {
                        CopyLogToUSB(driveLetter);
                    }
                }
                catch (Exception ex)
                {
                    Logger.WriteLineInfo($"ReadInfoFromUDisk Error:{ex}");
                }
            });
        }

        private void Reset(string path, UsbOperateInfo info)
        {
            info.NeedReset = false;
            File.WriteAllText(path, JsonHelper.ToJson(info, new JsonSerializerOptions
            {
                WriteIndented = true
            }));//防止无限重置
            _oledManager.ShowStatus(OledMessage.Restore);
            SystemHelper.ResetSystem();
        }

        private void ChangeNetworkInfo(NetworkInfo network)
        {
            try
            {
                _oledManager.ShowStatus(OledMessage.ChangeNetwork);
                var success = _networkManager.SetNetworkInfo(network.MacAddress, network.EnableDHCP, network.IPAddress, network.SubnetMask, network.Gateway, network.PreferredDNSServer, network.StandbyDNSServer);
                var result = success ? "Success" : "Fail";
                Logger.WriteLineInfo($"Change Network {result}");
            }
            catch (Exception e)
            {
                Logger.WriteLineError($"Change Networkinfo error:{e}");
            }
            finally
            {
                if (_loginManager.DeviceStatus == DeviceStatus.Logon)
                {
                    _oledManager.ShowStatus(OledMessage.Logined);
                }
                else
                {
                    _oledManager.ShowStatus(OledMessage.Offline);
                }
            }
        }

        /// <summary>
        /// ignore version
        /// </summary>
        /// <param name="driveLetter"></param>
        /// <param name="info"></param>
        private void ForceUpdate(string driveLetter, UpdateInfo info)
        {
            var filePath = Path.Combine(driveLetter, info.FileName);
            if (!File.Exists(filePath))
            {
                Logger.WriteLineError("Can not find upgrade file!");
                return;
            }
            var upgradeFolder = Path.Combine(SonopostConstants.DataFolder, "UDiskUpgrade");
            DirectoryHelper.CreateDirectory(upgradeFolder);
            var destPath = Path.Combine(upgradeFolder, info.FileName);
            File.Copy(filePath, destPath, true);
            _oledManager.ShowStatus(OledMessage.Upgrading);
            UpgradeHelper.StartUpgrade(destPath, true);
        }

        private void CopyLogToUSB(string path)
        {
            try
            {
                _oledManager.ShowStatus(OledMessage.BeginLog);
                Logger.WriteLineInfo("Start Copy Log To Usb.");
                var tmplogPath = Path.Combine(SonopostConstants.DataFolder, "usbtmplog");
                var logZipPath = Path.Combine(SonopostConstants.DataFolder, "locallogs.zip");
                string[] logsType = { "Logs", "SmartLogs", "TRTCClientLogs", "TRTCLogs" };
                DirectoryHelper.DeleteDirectory(tmplogPath);
                DirectoryHelper.CreateDirectory(tmplogPath);
                foreach (var type in logsType)
                {
                    GetLogsFile(tmplogPath, type);
                }

                var localLogs = Directory.GetFiles(tmplogPath, "*.*", SearchOption.AllDirectories);
                if (!localLogs.Any())
                    return;

                CompressHelper.CompressFolder(tmplogPath, logZipPath);
                if (File.Exists(logZipPath))
                {
                    File.Move(logZipPath, Path.Combine(path, $"Sonopostlog_{DateTime.Now.ToString("yyyyMMddHHmmss")}.zip"));
                }
                Logger.WriteLineInfo("Finish Copy Log To Usb.");
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"CopyLog error:{ex}");
            }
            finally
            {
                if (_loginManager.DeviceStatus == DeviceStatus.Logon)
                {
                    _oledManager.ShowStatus(OledMessage.Logined);
                }
                else
                {
                    _oledManager.ShowStatus(OledMessage.Offline);
                }
            }
        }

        private void GetLogsFile(string path, string pathType)
        {
            string logsPath = Path.Combine(SonopostConstants.DataFolder, pathType);
            var exportLogFolder = Path.Combine(path, pathType);
            if (!Directory.Exists(logsPath))
            {
                Logger.WriteLineInfo($"No Exist Path:{logsPath}");
                return;
            }
            DirectoryInfo di = new DirectoryInfo(logsPath);
            var fileList = di.GetFiles("*.*", SearchOption.AllDirectories);
            foreach (var logFile in fileList)
            {
                if (DateTime.Now.AddDays(-1) <= logFile.CreationTime)
                {
                    string newFileName = logFile.FullName.Replace(logsPath, exportLogFolder);
                    if (!newFileName.EndsWith(".log"))
                        newFileName = newFileName + ".log";
                    string fileDir = Path.GetDirectoryName(newFileName);
                    DirectoryHelper.CreateDirectory(fileDir);
                    logFile.CopyTo(newFileName, true);
                }
            }
        }

        public override void DoDispose()
        {
            try
            {
                _deviceManager.UDiskInserted -= OnUDiskInserted;
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"ExternalMemoryManager DoDispose Error:{ex}");
            }
            base.DoDispose();
        }
    }
}