using MinicsConsole.Helper.RawDataFilter;

namespace MinicsConsole.Helper;

public class HardWareMonitor(HardwareAddress address, Hardwares hardwares, Mini8DataDispatcher dataDispatcher, BasicInfo basicInfo, ILog log) : IOrmProvider
{
    public ConcurrentDictionary<byte, IMini8Communicator> Mini8Communicators { get; } = [];

    public ConcurrentDictionary<byte, Mini8Provider> Mini8Providers { get; } = [];

    private ConcurrentDictionary<byte, IORM> ORMs { get; } = [];

    public bool ParallelCreateConnections()
    {
        if (address is null || address.Mini8sAddress is null)
            return false;

        Parallel.ForEach(address.Mini8sAddress.Keys, mini8 => CreateConnection(mini8, false));
        return true;
    }

    public bool ParallelStartDataCollecion()
    {
        Parallel.ForEach(Mini8Communicators.Values, comm => comm.StartDataCollector());
        return true;
    }

    public bool CloseConnections()
    {
        Parallel.ForEach(Mini8Communicators.Values, t => t?.Close());
        Mini8Providers.Values.Foreach(t => ((IDisposable)t)?.Dispose());
        ORMs.Values.Foreach(t => t?.Dispose());

        Mini8Communicators.Clear();
        Mini8Providers.Clear();
        ORMs.Clear();
        return true;
    }

    public void FullyReset()
    {
        CloseConnections();
        ParallelCreateConnections();
        ParallelStartDataCollecion();
    }

    public bool CreateConnection(byte mini8, bool startCollection)
    {
        string info = string.Empty;
        if (string.IsNullOrEmpty(basicInfo.DBConnectionString))
        {
            info = "DB Connection String Empty";
            goto @return;
        }

        if (!address.Mini8sAddress.TryGetValueNotNull(mini8, out Mini8Address mini8Address))
        {
            info = "Mini8 Address not Exist";
            goto @return;
        }

        if (string.IsNullOrEmpty(mini8Address.Address) || mini8Address.Port == 0)
        {
            info = "Mini8 Address Empty or Port not correct";
            goto @return;
        }

        if (!address.Mini8ChannelsAddress.TryGetValueNotNull(mini8, out var channelAddress))
        {
            info = "Channel Address Not Exist";
            goto @return;
        }

        if (!hardwares.Mini8Channels.TryGetValueNotNull(mini8, out var channels))
        {
            info = "Channels Not Exist";
            goto @return;
        }

        if (!this.ORMs.TryGetValue(mini8, out IORM? orm) || orm is null)
        {
            orm = new SqlSugarCustom();
            if (!orm.Initialize(this))
            {
                info = "ORM Initialize failed";
                goto @return;
            }

            if (!orm.Open(basicInfo.DBConnectionString, DbType.PostgreSQL))
            {
                info = $"{basicInfo.DBConnectionString} DB Connect failed";
                goto @return;
            }

            if (!channels.Values.TryForeach(channel => orm.CreateTable<DBFormat>($"Mini8-{mini8}-{channel.ChannelIndex}")))
            {
                info = "DB Create DBFormat Table Failed";
                goto @return;
            }

            if (!orm.CreateTable<DBWarning>($"Error-Mini8-{mini8}"))
            {
                info = "DB Create DBWarning Table Failed";
                goto @return;
            }

            this.ORMs[mini8] = orm;
        }

        if (!this.Mini8Providers.TryGetValue(mini8, out Mini8Provider? mini8Provider) || mini8Provider is null)
        {
            mini8Provider ??= new(mini8, log, basicInfo, channels, dataDispatcher);
            mini8Provider.ORM = orm;
            Mini8Providers[mini8] = mini8Provider;
        }

        if (this.Mini8Communicators.TryGetValue(mini8, out IMini8Communicator? mini8Communicator))
            mini8Communicator?.Close();

        IMini8Communicator sender = new Mini8CommunicatorModbus.Mini8Communcator_Modbus_FluentModbus();
        sender.Initialize(mini8Provider, mini8Address, channelAddress);

        if (!sender.Open())
        {
            info = $"Unable to connect to {mini8Address.Address}:{mini8Address.Port}";
            goto @return;
        }
        mini8Provider.Mini8Communicator = sender;


        if (startCollection)
            sender.StartDataCollector();

        this.Mini8Communicators[mini8] = sender;

        log.Info($"Mini8 {mini8} Connected - {mini8Address.Address}:{mini8Address.Port} ");
        return true;

    @return:
        log.Error($" Connect to Mini8 {mini8} Failed  - {info}");
        return false;
    }

    public IEnumerable<(byte channelIndex, Dictionary<byte, Mini8Output>? realtimeData)> GetCurrentStatus()
    {
        List<(byte channelIndex, Dictionary<byte, Mini8Output>? realtimeData)> list = [];
        foreach (KeyValuePair<byte, IMini8Communicator> communicator in this.Mini8Communicators)
        {
            communicator.Value.GetRealtimeDataFromDevice(out Dictionary<byte, Mini8Output>? output);
            list.Add((communicator.Key, output));
        }
        return list;
    }

    public IDictionary<byte, Dictionary<byte, Mini8Limit>?> GetCurrentLimit()
    {
        Dictionary<byte, Dictionary<byte, Mini8Limit>?> list = [];
        foreach (KeyValuePair<byte, IMini8Communicator> communicator in this.Mini8Communicators)
        {
            for (int i = 0; i < 10; i++)
            {
                if (!communicator.Value.GetRealtimeLimit(out Dictionary<byte, Mini8Limit>? output) || output is null)
                {
                    Thread.Sleep(200);
                    continue;
                }

                list[communicator.Key] = output;
                break;
            }
        }
        return list;
    }

    void IOrmProvider.Log(string logInfo, DateTime dateTime, ORM.LogLevel logLevel)
    {
        if (logLevel == ORM.LogLevel.Error)
            log.Error($"DataBase {logLevel}-{dateTime:hh:mm:ss} {logInfo}");
    }
}