using File_Watcher.Helpers.EDA;
using File_Watcher.Models;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;

namespace File_Watcher.Helpers;

internal static partial class HelperEDADatabase
{

    private static Calendar? _Calendar;
    private static string? _EDADataCollectionPlansLastRun;

    [GeneratedRegex("[a-zA-Z0-9]{1,}")]
    private static partial Regex RegexAZ09();

    private static Stream ToStream(string @this)
    {
        MemoryStream? stream = new();
        StreamWriter? writer = new(stream);
        writer.Write(@this);
        writer.Flush();
        stream.Position = 0;
        return stream;
    }

    private static T? ParseXML<T>(string @this, bool throwExceptions) where T : class
    {
        object? result = null;
        try
        {
            Stream stream = ToStream(@this.Trim());
            XmlReader? reader = XmlReader.Create(stream, new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
            XmlSerializer xmlSerializer = new(typeof(T), typeof(T).GetNestedTypes());
            result = xmlSerializer.Deserialize(reader);
            stream.Dispose();
        }
        catch (Exception)
        {
            if (throwExceptions)
                throw;
        }
        return result as T;
    }

    private static void EvaluateRows(string workingDirectory, Dictionary<string, Dictionary<ModuleInstanceTypeName, List<List<object>>>> rows, CancellationToken cancellationToken)
    { // cSpell:disable
        // int moduleinstancetypename = 0;
        int unitname = 1;
        // int modulename = 2;
        int containername = 3;
        int configurationstate = 4;
        int configurationproductivestate = 5;
        // int containermodifiername = 6;
        int containermodifieddate = 7;
        int containerconfiguration = 8;
        int configurationmodifieddate = 9;
        // cSpell:enable
        string csv;
        string? xml;
        string json;
        string text;
        string html;
        Common common;
        string emptyAPC;
        string fileName;
        string? modifiedDate;
        string edaObjectFile;
        string goldDirectory;
        string replace = "$$$";
        string edaObjectDirectory;
        DateTime lastModifiedDate;
        string objectTypeDirectory;
        DateTime containerModifiedDate;
        PDSFConfiguration? configuration;
        DateTime configurationModifiedDate;
        JsonSerializerOptions jsonSerializerOptions = new() { WriteIndented = true };
        foreach (KeyValuePair<string, Dictionary<ModuleInstanceTypeName, List<List<object>>>> databaseElement in rows)
        {
            if (cancellationToken.IsCancellationRequested)
                break;
            emptyAPC = string.Concat(workingDirectory, @"\", databaseElement.Key, @"\EmptyAPC.xlsx");
            foreach (KeyValuePair<ModuleInstanceTypeName, List<List<object>>> tableNameElement in databaseElement.Value)
            {
                if (cancellationToken.IsCancellationRequested)
                    break;
                objectTypeDirectory = string.Concat(workingDirectory, @"\", databaseElement.Key, @"\", tableNameElement.Key);
                foreach (List<object> rowItem in tableNameElement.Value)
                { // cSpell:disable
                    if (cancellationToken.IsCancellationRequested)
                        break;
                    // foreach (var item in rowItem)
                    //     Print(item.ToString());
                    // if (!rowItem[containername].ToString().Contains("Plan_Pdsf_Health_Cp2Mg"))
                    //  if (!rowItem[unitname].ToString().Contains("HGCV1") || !rowItem[containername].ToString().Contains("Plan_Pdsf"))
                    //      continue;
                    modifiedDate = rowItem[containermodifieddate].ToString();
                    if (string.IsNullOrEmpty(modifiedDate))
                        continue;
                    containerModifiedDate = DateTime.Parse(modifiedDate);
                    modifiedDate = rowItem[configurationmodifieddate].ToString();
                    if (string.IsNullOrEmpty(modifiedDate))
                        continue;
                    configurationModifiedDate = DateTime.Parse(modifiedDate);
                    if (containerModifiedDate < configurationModifiedDate)
                        lastModifiedDate = configurationModifiedDate;
                    else
                        lastModifiedDate = containerModifiedDate;
                    goldDirectory = string.Concat(objectTypeDirectory, @"\", rowItem[unitname], @"\", rowItem[containername], @"\Gold");
                    if (!Directory.Exists(goldDirectory))
                        _ = Directory.CreateDirectory(goldDirectory);
                    edaObjectDirectory = string.Concat(objectTypeDirectory, @"\", rowItem[unitname], @"\", rowItem[containername], @"\", rowItem[configurationstate], @"\", rowItem[configurationproductivestate], @"\", lastModifiedDate.ToString("yyyy-MM-dd_"), new TimeSpan(lastModifiedDate.Ticks - new DateTime(lastModifiedDate.Year, lastModifiedDate.Month, lastModifiedDate.Day).Ticks).TotalSeconds);
                    if (!Directory.Exists(edaObjectDirectory))
                        _ = Directory.CreateDirectory(edaObjectDirectory);
                    edaObjectFile = string.Concat(edaObjectDirectory, @"\", rowItem[unitname], " ", rowItem[containername], " - ", replace, " - ", rowItem[configurationstate].ToString(), " ", rowItem[configurationproductivestate].ToString());
                    xml = rowItem[containerconfiguration].ToString();
                    if (string.IsNullOrEmpty(xml))
                        continue;
                    fileName = string.Concat(edaObjectFile.Replace(replace, "Raw"), ".xml");
                    File.WriteAllText(fileName, xml);
                    try
                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                    catch (Exception) { }
                    common = new Common(ModuleInstanceTypeName.Pdsf, rowItem[unitname], rowItem[containername], rowItem[configurationstate], rowItem[configurationproductivestate]);
                    configuration = ParseXML<PDSFConfiguration>(xml, throwExceptions: true);
                    if (configuration is null)
                        continue;
                    // cSpell:enable
                    common.Update(configuration);
                    json = JsonSerializer.Serialize(configuration, configuration.GetType(), jsonSerializerOptions);
                    if (common?.UnitName is null)
                        continue;
                    fileName = string.Concat(edaObjectFile.Replace(replace, "Partial"), ".csv");
                    File.WriteAllText(fileName, common.ParametersAsCsv);
                    try
                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                    catch (Exception) { }
                    fileName = string.Concat(edaObjectFile.Replace(replace, "Partial"), ".json");
                    File.WriteAllText(fileName, json);
                    text = DCP.GetText(edaObjectFile, common, json);
                    fileName = string.Concat(edaObjectFile.Replace(replace, "Partial"), ".txt");
                    File.WriteAllText(fileName, text);
                    try
                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                    catch (Exception) { }
                    html = DCP.GetEdaObjectToHtml(edaObjectFile, common);
                    fileName = string.Concat(edaObjectFile.Replace(replace, "Partial"), ".html");
                    File.WriteAllText(fileName, html);
                    try
                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                    catch (Exception) { }
                    xml = DCP.GetEdaObjectToDMSGridFormat(edaObjectFile, common, useAlias: false);
                    fileName = string.Concat(edaObjectFile.Replace(replace, "DMSGridFormat"), ".xml");
                    File.WriteAllText(fileName, xml);
                    try
                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                    catch (Exception) { }
                    xml = DCP.GetEdaObjectToDMSGridFormat(edaObjectFile, common, useAlias: true);
                    fileName = string.Concat(edaObjectFile.Replace(replace, "DMSGridFormat - Alias"), ".xml");
                    File.WriteAllText(fileName, xml);
                    try
                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                    catch (Exception) { }
                    csv = DCP.GetEdaObjectToAPCParameter(edaObjectFile, common);
                    fileName = string.Concat(edaObjectFile.Replace(replace, "APCParameter"), ".csv");
                    File.WriteAllText(fileName, csv);
                    try
                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                    catch (Exception) { }
                    csv = DCP.GetEdaObjectToAPCRunKeyNumber(edaObjectFile, common);
                    fileName = string.Concat(edaObjectFile.Replace(replace, "APCRunKeyNumber"), ".csv");
                    File.WriteAllText(fileName, csv);
                    try
                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                    catch (Exception) { }
                    fileName = string.Concat(edaObjectFile.Replace(replace, "APC"), ".xlsx");
                    if (File.Exists(emptyAPC) && !File.Exists(fileName))
                    {
                        File.Copy(emptyAPC, fileName);
                        try
                        { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                        catch (Exception) { }
                    }
                    if (common.StoragePaths.Length == 0)
                        continue;
                    foreach (string? storagePath in common.StoragePaths)
                    {
                        if (string.IsNullOrEmpty(storagePath) || !Directory.Exists(Path.GetPathRoot(storagePath)))
                            continue;
                        if (Directory.Exists(storagePath))
                            continue;
                        _ = Directory.CreateDirectory(storagePath);
                    }
                }
                try
                { Directory.SetLastWriteTime(objectTypeDirectory, DateTime.Now); }
                catch (Exception) { }
            }
        }
    }

    private static void DataCollectionPlans(AppSettings appSettings, ILogger<Worker> logger, Calendar calendar, CancellationToken cancellationToken)
    {
        int fieldCount;
        object @object;
        string? @string;
        List<object> row;
        StringBuilder sql = new();
        string objectTypeDirectory;
        Array objectTypes = Enum.GetValues(typeof(ModuleInstanceTypeName));
        Dictionary<string, Dictionary<ModuleInstanceTypeName, List<List<object>>>> rows = [];
        string decrypted = RijndaelEncryption.Decrypt(appSettings.EDADatabaseConfiguration.Password, appSettings.Company);
        string connectionString = $"Data Source={appSettings.EDADatabaseConfiguration.TNS}; User Id={appSettings.EDADatabaseConfiguration.UserName}; Password={decrypted};";
        rows.Add(appSettings.EDADatabaseConfiguration.Name, []);
        foreach (ModuleInstanceTypeName objectType in objectTypes)
        {
            if (cancellationToken.IsCancellationRequested)
                break;
            if (string.IsNullOrEmpty(_EDADataCollectionPlansLastRun))
            {
                objectTypeDirectory = Path.Combine(appSettings.EDADatabaseConfiguration.FileShare, appSettings.EDADatabaseConfiguration.Name, objectType.ToString());
                if (!Directory.Exists(objectTypeDirectory))
                    _ = Directory.CreateDirectory(objectTypeDirectory);
                else
                    _EDADataCollectionPlansLastRun = new DirectoryInfo(objectTypeDirectory).LastWriteTime.ToString(appSettings.EDADatabaseConfiguration.CSharpDateTimeFormat);
            }
            if (!rows[appSettings.EDADatabaseConfiguration.Name].ContainsKey(objectType))
                rows[appSettings.EDADatabaseConfiguration.Name].Add(objectType, []);
            using (Oracle.ManagedDataAccess.Client.OracleConnection oracleConnection = new(connectionString))
            {
                _ = sql.Clear();
                oracleConnection.Open(); // cSpell:disable
                _ = sql.Append(" select v.moduleinstancetypename, v.unitname, ").
                    Append("        v.modulename, v.containername, v.configurationstate, ").
                    Append("        v.configurationproductivestate, v.containermodifiername, ").
                    Append("        v.containermodifieddate, v.containerconfiguration, v.configurationmodifieddate ").
                    Append(" from veditconfiguration v ").
                    Append(" where v.moduleinstancetypename = '").Append(objectType.ToString()).Append("' ");
                // Append("   and v.containerversion in ('4.80', '4.111') ");
                if (!string.IsNullOrEmpty(_EDADataCollectionPlansLastRun))
                    _ = sql.Append("   and greatest(v.containermodifieddate, v.configurationmodifieddate) >= to_date('").Append(_EDADataCollectionPlansLastRun).Append("', '").Append(appSettings.EDADatabaseConfiguration.OracleDateTimeFormat).Append("') ");
                logger.LogDebug(sql.ToString());
                using Oracle.ManagedDataAccess.Client.OracleCommand oracleCommand = new(sql.ToString(), oracleConnection); // cSpell:enable
                using Oracle.ManagedDataAccess.Client.OracleDataReader oracleDataReader = oracleCommand.ExecuteReader();
                fieldCount = oracleDataReader.FieldCount;
                while (oracleDataReader.Read())
                {
                    if (cancellationToken.IsCancellationRequested)
                        break;
                    row = [];
                    for (int c = 0; c < fieldCount; c++)
                    {
                        @object = oracleDataReader.GetValue(c);
                        row.Add(@object);
                        if (@object is null)
                            continue;
                        @string = @object.ToString();
                        // if (@string.Length < 128)
                        //    _Logger.LogDebug(@string);
                    }
                    rows[appSettings.EDADatabaseConfiguration.Name][objectType].Add(row);
                }
            };
        }
        if (rows.Count != 0)
            EvaluateRows(appSettings.EDADatabaseConfiguration.FileShare, rows, cancellationToken);
    }

    internal static bool SaveDataCollectionPlans(AppSettings appSettings, ILogger<Worker> logger, CancellationToken cancellationToken)
    {
        _Calendar ??= new CultureInfo("en-US").Calendar;
        DataCollectionPlans(appSettings, logger, _Calendar, cancellationToken);
        return true;
    }

}