//******************************************************************************************************
//  ProcessParameters.cs - Gbtc
//
//  Copyright © 2026, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
//  file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://opensource.org/licenses/MIT
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  01/15/2026 - J. Ritchie Carroll
//       Generated original version of source code.
//
//******************************************************************************************************


using Gemstone.Data;
using Gemstone.Data.Model;

namespace DNP3ConfigLoader;

/// <summary>
/// Represents processing parameters for DNP3 configuration.
/// </summary>
public class ProcessParameters
{
    /// <summary>
    /// Device acronym template format string.
    /// </summary>
    public const string DeviceAcronymTemplate = "{0}-DNP3";

    public ProcessParameters(
        Settings settings,
        AdoDataConnection connection,
        Guid nodeID,
        int historianID,
        int protocolID,
        Dictionary<string, int> signalTypeIDs)
    {
        Connection = connection;
        Settings = settings;
        NodeID = nodeID;
        HistorianID = historianID;
        ProtocolID = protocolID;
        SignalTypeIDs = signalTypeIDs;

        CompanyAcronym = Common.GetCleanIdentifier(settings.CompanyAcronym);

        // Create reusable TableOperations instances
        MeasurementTable = new TableOperations<Measurement>(connection);
        DeviceTable = new TableOperations<Device>(connection);
        CustomFilterAdapterTable = new TableOperations<CustomFilterAdapter>(connection);
        CustomActionAdapterTable = new TableOperations<CustomActionAdapter>(connection);

        // Ensure output directory exists and ends with separator
        if (!Directory.Exists(settings.OutputDirectory))
            Directory.CreateDirectory(settings.OutputDirectory);

        OutputDirectory = settings.OutputDirectory.EndsWith('/') || settings.OutputDirectory.EndsWith('\\') ?
            settings.OutputDirectory :
            settings.OutputDirectory + Path.DirectorySeparatorChar;

        // Ensure tag map directory exists and ends with separator
        if (!Directory.Exists(settings.TagMapDirectory))
            Directory.CreateDirectory(settings.TagMapDirectory);

        TagMapDirectory = settings.TagMapDirectory.EndsWith('/') || settings.TagMapDirectory.EndsWith('\\') ?
            settings.TagMapDirectory :
            settings.TagMapDirectory + Path.DirectorySeparatorChar;

        string templateDirectory = settings.TemplateDirectory.EndsWith('/') || settings.TemplateDirectory.EndsWith('\\') ?
            settings.TemplateDirectory :
            settings.TemplateDirectory + Path.DirectorySeparatorChar;

        // Load XML templates into strings
        DeviceTemplate = File.ReadAllText($"{templateDirectory}Device.xml");
        IndexMapTemplate = File.ReadAllText($"{templateDirectory}IndexMap.xml");
        MappingTemplate = File.ReadAllText($"{templateDirectory}Mapping.xml");
    }

    /// <summary>
    /// Gets the database connection.
    /// </summary>
    public AdoDataConnection Connection { get; }

    /// <summary>
    /// Gets the application settings.
    /// </summary>
    public Settings Settings { get; }

    /// <summary>
    /// Gets the node ID.
    /// </summary>
    public Guid NodeID { get; }

    /// <summary>
    /// Gets the historian ID for measurements.
    /// </summary>
    public int HistorianID { get; }

    /// <summary>
    /// Gets the DNP3 protocol ID.
    /// </summary>
    public int ProtocolID { get; }

    /// <summary>
    /// Gets the signal type ID lookup dictionary.
    /// </summary>
    public Dictionary<string, int> SignalTypeIDs { get; }

    /// <summary>
    /// Gets the cleaned company acronym.
    /// </summary>
    public string CompanyAcronym { get; }

    /// <summary>
    /// Gets the output directory path (with trailing separator).
    /// </summary>
    public string OutputDirectory { get; }

    /// <summary>
    /// Gets the tag map directory path (with trailing separator).
    /// </summary>
    public string TagMapDirectory { get; }

    /// <summary>
    /// Gets the Device.xml template content.
    /// </summary>
    public string DeviceTemplate { get; }

    /// <summary>
    /// Gets the IndexMap.xml template content.
    /// </summary>
    public string IndexMapTemplate { get; }

    /// <summary>
    /// Gets the Mapping.xml template content.
    /// </summary>
    public string MappingTemplate { get; }

    /// <summary>
    /// Gets the reusable TableOperations for Measurement records.
    /// </summary>
    public TableOperations<Measurement> MeasurementTable { get; }

    /// <summary>
    /// Gets the reusable TableOperations for Device records.
    /// </summary>
    public TableOperations<Device> DeviceTable { get; }

    /// <summary>
    /// Gets the reusable TableOperations for CustomFilterAdapter records.
    /// </summary>
    public TableOperations<CustomFilterAdapter> CustomFilterAdapterTable { get; }

    /// <summary>
    /// Gets the reusable TableOperations for CustomActionAdapter records.
    /// </summary>
    public TableOperations<CustomActionAdapter> CustomActionAdapterTable { get; }

    /// <summary>
    /// Gets the set of singleton adapter names that have already been verified/created
    /// during this run, avoiding redundant database queries.
    /// </summary>
    public HashSet<string> VerifiedAdapterNames { get; } = new(StringComparer.OrdinalIgnoreCase);

    /// <summary>
    /// Gets the list of processed device acronyms (for adapter initialization).
    /// </summary>
    public HashSet<string> ProcessedAcronyms { get; } = [];

    /// <summary>
    /// Gets the per-device measurement cache keyed by SignalReference.
    /// Preloaded once per device to avoid individual database round-trips
    /// during validation runs as an optimization for large configurations.
    /// </summary>
    public Dictionary<string, Measurement> MeasurementCache { get; private set; } = new(StringComparer.OrdinalIgnoreCase);

    /// <summary>
    /// Bulk-loads all measurements for the specified device into the in-memory cache.
    /// </summary>
    public void LoadMeasurementCache(int deviceID)
    {
        MeasurementCache = MeasurementTable
            .QueryRecordsWhere("DeviceID = {0}", deviceID)
            .Where(measurement => measurement is not null && !string.IsNullOrEmpty(measurement.SignalReference))
            .ToDictionary(measurement => measurement!.SignalReference, StringComparer.OrdinalIgnoreCase)!;

        Console.WriteLine($"      Loaded {MeasurementCache.Count:N0} existing measurements into cache for device ID {deviceID}");
    }

    /// <summary>
    /// Clears the per-device measurement cache.
    /// </summary>
    public void ClearMeasurementCache()
    {
        MeasurementCache.Clear();
    }
}
