using Adaptation.Eaf.Management.ConfigurationData.CellAutomation;
using Adaptation.Ifx.Eaf.EquipmentConnector.File.Configuration;
using Adaptation.Shared;
using Adaptation.Shared.Duplicator;
using Adaptation.Shared.Methods;
using Infineon.Monitoring.MonA;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.Json;
using System.Threading;

namespace Adaptation.FileHandlers.Dummy;

public class FileRead : Shared.FileRead, IFileRead
{

    private readonly Timer _Timer;
    private int _LastDummyRunIndex;
    private readonly string[] _CellNames;

    public FileRead(ISMTP smtp, Dictionary<string, string> fileParameter, string cellInstanceName, int? connectionCount, string cellInstanceConnectionName, FileConnectorConfiguration fileConnectorConfiguration, string equipmentTypeName, string parameterizedModelObjectDefinitionType, IList<ModelObjectParameterDefinition> modelObjectParameters, string equipmentDictionaryName, Dictionary<string, List<long>> dummyRuns, Dictionary<long, List<string>> staticRuns, bool useCyclicalForDescription, bool isEAFHosted) :
        base(new Description(), false, smtp, fileParameter, cellInstanceName, connectionCount, cellInstanceConnectionName, fileConnectorConfiguration, equipmentTypeName, parameterizedModelObjectDefinitionType, modelObjectParameters, equipmentDictionaryName, dummyRuns, staticRuns, useCyclicalForDescription, isEAFHosted: connectionCount is null)
    {
        _MinFileLength = 10;
        _NullData = string.Empty;
        _Logistics = new(this);
        if (_FileParameter is null)
            throw new Exception(cellInstanceConnectionName);
        if (_ModelObjectParameterDefinitions is null)
            throw new Exception(cellInstanceConnectionName);
        if (!_IsDuplicator)
            throw new Exception(cellInstanceConnectionName);
        _LastDummyRunIndex = -1;
        List<string> cellNames = new();
        _Timer = new Timer(Callback, null, Timeout.Infinite, Timeout.Infinite);
        ModelObjectParameterDefinition[] cellInstanceCollection = GetProperties(cellInstanceConnectionName, modelObjectParameters, "CellInstance.", ".Alias");
        foreach (ModelObjectParameterDefinition modelObjectParameterDefinition in cellInstanceCollection)
            cellNames.Add(modelObjectParameterDefinition.Name.Split('.')[1]);
        _CellNames = cellNames.ToArray();
        if (Debugger.IsAttached || fileConnectorConfiguration.PreProcessingMode == FileConnectorConfiguration.PreProcessingModeEnum.Process)
            Callback(null);
        else
        {
            TimeSpan timeSpan = new(DateTime.Now.AddSeconds(_FileConnectorConfiguration.FileScanningIntervalInSeconds.Value).Ticks - DateTime.Now.Ticks);
            _ = _Timer.Change((long)timeSpan.TotalMilliseconds, Timeout.Infinite);
        }
    }

    void IFileRead.Move(Tuple<string, Test[], JsonElement[], List<FileInfo>> extractResults, Exception exception) => Move(extractResults);

    void IFileRead.WaitForThread() => WaitForThread(thread: null, threadExceptions: null);

    string IFileRead.GetEventDescription()
    {
        string result = _Description.GetEventDescription();
        return result;
    }

    List<string> IFileRead.GetHeaderNames()
    {
        List<string> results = _Description.GetHeaderNames();
        return results;
    }

    string[] IFileRead.Move(Tuple<string, Test[], JsonElement[], List<FileInfo>> extractResults, string to, string from, string resolvedFileLocation, Exception exception)
    {
        string[] results = Move(extractResults, to, from, resolvedFileLocation, exception);
        return results;
    }

    JsonProperty[] IFileRead.GetDefault()
    {
        JsonProperty[] results = _Description.GetDefault(this, _Logistics);
        return results;
    }

    Dictionary<string, string> IFileRead.GetDisplayNamesJsonElement()
    {
        Dictionary<string, string> results = _Description.GetDisplayNamesJsonElement(this);
        return results;
    }

    List<IDescription> IFileRead.GetDescriptions(IFileRead fileRead, List<Test> tests, IProcessData processData)
    {
        List<IDescription> results = _Description.GetDescriptions(fileRead, _Logistics, tests, processData);
        return results;
    }

    Tuple<string, Test[], JsonElement[], List<FileInfo>> IFileRead.GetExtractResult(string reportFullPath, string eventName) => throw new Exception(string.Concat("See ", nameof(CallbackFileExists)));

    Tuple<string, Test[], JsonElement[], List<FileInfo>> IFileRead.ReExtract() => throw new Exception(string.Concat("See ", nameof(CallbackFileExists)));

    private void CallbackInProcessCleared(string sourceArchiveFile, string traceDummyFile, string targetFileLocation, string monARessource, string inProcessDirectory, long sequence, bool warning)
    {
        const string site = "sjc";
        string stateName = string.Concat("Dummy_", _EventName);
        const string monInURL = "http://moninhttp.sjc.infineon.com/input/text";
        MonIn monIn = MonIn.GetInstance(monInURL);
        try
        {
            if (warning)
            {
                File.AppendAllLines(traceDummyFile, new string[] { site, monARessource, stateName, State.Warning.ToString() });
                _ = monIn.SendStatus(site, monARessource, stateName, State.Warning);
                for (int i = 1; i < 12; i++)
                    Thread.Sleep(500);
            }
            ZipFile.ExtractToDirectory(sourceArchiveFile, inProcessDirectory);
            string[] files = Directory.GetFiles(inProcessDirectory, "*", SearchOption.TopDirectoryOnly);
            if (files.Length > 250)
                throw new Exception("Safety net!");
            foreach (string file in files)
                File.SetLastWriteTime(file, new DateTime(sequence));
            if (!_FileConnectorConfiguration.IncludeSubDirectories.Value)
            {
                foreach (string file in files)
                    File.Move(file, Path.Combine(targetFileLocation, Path.GetFileName(file)));
            }
            else
            {
                string[] directories = Directory.GetDirectories(inProcessDirectory, "*", SearchOption.AllDirectories);
                foreach (string directory in directories)
                    _ = Directory.CreateDirectory(string.Concat(targetFileLocation, directory.Substring(inProcessDirectory.Length)));
                foreach (string file in files)
                    File.Move(file, string.Concat(targetFileLocation, file.Substring(inProcessDirectory.Length)));
            }
            File.AppendAllLines(traceDummyFile, new string[] { site, monARessource, stateName, State.Ok.ToString() });
            _ = monIn.SendStatus(site, monARessource, stateName, State.Ok);
        }
        catch (Exception exception)
        {
            string subject = string.Concat("Exception:", _CellInstanceConnectionName);
            string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace);
            try
            {
                _SMTP.SendHighPriorityEmailMessage(subject, body);
                File.WriteAllText(".email", body);
            }
            catch (Exception) { }
            File.AppendAllLines(traceDummyFile, new string[] { site, monARessource, stateName, State.Critical.ToString(), exception.Message, exception.StackTrace });
            _ = monIn.SendStatus(site, monARessource, stateName, State.Critical);
        }
    }

    private void CallbackFileExists(string sourceArchiveFile, string traceDummyFile, string targetFileLocation, string monARessource, long sequence)
    {
        string[] files;
        bool warning = false;
        if (!_DummyRuns.ContainsKey(monARessource))
            _DummyRuns.Add(monARessource, new List<long>());
        if (!_DummyRuns[monARessource].Contains(sequence))
            _DummyRuns[monARessource].Add(sequence);
        File.AppendAllLines(traceDummyFile, new string[] { sourceArchiveFile });
        string inProcessDirectory = Path.Combine("_ProgressPath", "Dummy In-Process", sequence.ToString());
        if (!Directory.Exists(inProcessDirectory))
            _ = Directory.CreateDirectory(inProcessDirectory);
        files = Directory.GetFiles(inProcessDirectory, "*", SearchOption.AllDirectories);
        if (files.Length != 0)
        {
            if (files.Length > 250)
                throw new Exception("Safety net!");
            try
            {
                foreach (string file in files)
                    File.Delete(file);
            }
            catch (Exception) { }
        }
        if (_FileConnectorConfiguration.IncludeSubDirectories.Value)
            files = Directory.GetFiles(targetFileLocation, "*", SearchOption.AllDirectories);
        else
            files = Directory.GetFiles(targetFileLocation, "*", SearchOption.TopDirectoryOnly);
        foreach (string file in files)
        {
            if (new FileInfo(file).LastWriteTime.Ticks == sequence)
            {
                warning = true;
                break;
            }
        }
        CallbackInProcessCleared(sourceArchiveFile, traceDummyFile, targetFileLocation, monARessource, inProcessDirectory, sequence, warning);
    }

    private string GetCellName(string pathSegment)
    {
        string result = string.Empty;
        foreach (string cellName in _CellNames)
        {
            if (pathSegment.ToLower().Contains(cellName.ToLower()))
            {
                result = cellName;
                break;
            }
        }
        if (string.IsNullOrEmpty(result))
        {
            int count;
            List<(string CellName, int Count)> cellNames = new();
            foreach (string cellName in _CellNames)
            {
                count = 0;
                foreach (char @char in cellName.ToLower())
                    count += pathSegment.Length - pathSegment.ToLower().Replace(@char.ToString(), string.Empty).Length;
                cellNames.Add(new(cellName, count));
            }
            result = (from l in cellNames orderby l.CellName.Length, l.Count descending select l.CellName).First();
        }
        return result;
    }

    private void Callback(object state)
    {
        try
        {
            string sourceParentDirectory;
            string targetParentDirectory;
            DateTime dateTime = DateTime.Now;
            if (!string.IsNullOrEmpty(Path.GetFileName(_FileConnectorConfiguration.SourceFileLocation)))
                sourceParentDirectory = Path.GetDirectoryName(_FileConnectorConfiguration.SourceFileLocation);
            else
                sourceParentDirectory = GetParentParent(_FileConnectorConfiguration.SourceFileLocation);
            if (!string.IsNullOrEmpty(Path.GetFileName(_FileConnectorConfiguration.TargetFileLocation)))
                targetParentDirectory = Path.GetDirectoryName(_FileConnectorConfiguration.TargetFileLocation);
            else
                targetParentDirectory = GetParentParent(_FileConnectorConfiguration.TargetFileLocation);
            if (sourceParentDirectory != targetParentDirectory)
                throw new Exception("Target and source must have the same parent for Si Dummy FileConnectorConfiguration!");
            bool check = dateTime.Hour > 7 && dateTime.Hour < 18 && dateTime.DayOfWeek != DayOfWeek.Sunday && dateTime.DayOfWeek != DayOfWeek.Saturday;
            if (!_IsEAFHosted || check)
            {
                string monARessource;
                string sourceFileFilter;
                string sourceArchiveFile;
                string weekOfYear = _Calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
                string traceDummyDirectory = Path.Combine(Path.GetPathRoot(_TracePath), "TracesDummy", _CellInstanceName, "Source", $"{dateTime:yyyy}___Week_{weekOfYear}");
                if (!Directory.Exists(traceDummyDirectory))
                    _ = Directory.CreateDirectory(traceDummyDirectory);
                string traceDummyFile = Path.Combine(traceDummyDirectory, $"{dateTime.Ticks} - {_CellInstanceName}.txt");
                File.AppendAllText(traceDummyFile, string.Empty);
                for (int i = 0; i < _FileConnectorConfiguration.SourceFileFilters.Count; i++)
                {
                    _LastDummyRunIndex += 1;
                    if (_LastDummyRunIndex >= _FileConnectorConfiguration.SourceFileFilters.Count)
                        _LastDummyRunIndex = 0;
                    sourceFileFilter = _FileConnectorConfiguration.SourceFileFilters[_LastDummyRunIndex];
                    sourceArchiveFile = Path.Combine(_FileConnectorConfiguration.SourceFileLocation, sourceFileFilter);
                    if (File.Exists(sourceArchiveFile))
                    {
                        if (!long.TryParse(Path.GetFileNameWithoutExtension(sourceArchiveFile).Replace("x", string.Empty), out long sequence))
                            throw new Exception("Invalid file name for source archive file!");
                        monARessource = GetCellName(sourceArchiveFile);
                        if (string.IsNullOrEmpty(monARessource))
                            throw new Exception("Could not determine which cell archive file is associated with!");
                        if (_IsEAFHosted)
                            CallbackFileExists(sourceArchiveFile, traceDummyFile, _FileConnectorConfiguration.TargetFileLocation, monARessource, sequence);
                        break;
                    }
                }
            }
        }
        catch (Exception exception)
        {
            string subject = string.Concat("Exception:", _CellInstanceConnectionName);
            string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace);
            try
            {
                _SMTP.SendHighPriorityEmailMessage(subject, body);
                File.WriteAllText(".email", body);
            }
            catch (Exception) { }
        }
        try
        {
            TimeSpan timeSpan = new(DateTime.Now.AddSeconds(_FileConnectorConfiguration.FileScanningIntervalInSeconds.Value).Ticks - DateTime.Now.Ticks);
            _ = _Timer.Change((long)timeSpan.TotalMilliseconds, Timeout.Infinite);
        }
        catch (Exception exception)
        {
            string subject = string.Concat("Exception:", _CellInstanceConnectionName);
            string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace);
            try
            {
                _SMTP.SendHighPriorityEmailMessage(subject, body);
                File.WriteAllText(".email", body);
            }
            catch (Exception) { }
        }
    }

}