namespace MinicsConsole.Helper;

public class ConfigUpdater(Hardwares hardwares, ConfigFiles configFiles, HardWareMonitor hardWareMonitor)
{
    public bool SetConfigFile(TemperatureConfig file, bool sendToMini8, out Dictionary<byte, Dictionary<byte, bool>> sendResult)
    {
        sendResult = [];
        if (Checker.IsNull(file, file.Mini8sConfig))
            return false;

        if (!sendToMini8)
            goto UpdateConfigInMemory;

        //Send to Mini8
        foreach (KeyValuePair<byte, Mini8Config> mini8 in file!.Mini8sConfig!)
        {
            sendResult[mini8.Key] = [];
            if (mini8.Value.ChannelConfig is null)
                continue;

            if (!hardWareMonitor.Mini8Communicators.TryGetValueNotNull(mini8.Key, out IMini8Communicator communicator))
                continue;

            foreach (KeyValuePair<byte, ChannelConfig> channel in mini8.Value.ChannelConfig)
            {
                //if channel is disabled or not in use, whether sending other Mini8Input needed to be confirmed
                if (channel.Value.Inhibit == Inhibit.Disable ||
                    channel.Value.ChannelMode == ChannelMode.UnUsed)
                {
                    communicator.EnableChannel(channel.Key, Inhibit.Disable);
                    continue;
                }

                Mini8Input input = new();
                channel.Value.Adapt(input);

                if (!communicator.SendMini8Data(channel.Key, input) ||
                    !communicator.EnableChannel(channel.Key, channel.Value.Inhibit))
                {
                    sendResult[mini8.Key][channel.Key] = false;
                    continue;
                }
                sendResult[mini8.Key][channel.Key] = true;
            }
        }

    //Update Memory Mini8 Channels
    UpdateConfigInMemory:
        foreach (KeyValuePair<byte, Mini8Config> mini8 in file!.Mini8sConfig!)
        {
            if (mini8.Value is null || mini8.Value.ChannelConfig is null)
                continue;

            if (!hardwares.Mini8Channels.TryGetValueNotNull(mini8.Key, out ConcurrentDictionary<byte, ChannelData> channels))
                continue;

            foreach (KeyValuePair<byte, ChannelConfig> item in mini8.Value.ChannelConfig)
            {
                if (!channels.TryGetValueNotNull(item.Key, out ChannelData channel))
                    continue;

                channels[item.Key] = item.Value.Adapt(channel);
            }
        }

        configFiles.CurrentConfigFile = file;
        this.UpdateConfigFile(file);

        return true;
    }

    public void UpdateConfigFile(TemperatureConfig config)
    {
        if (config.Mini8sConfig is null)
            return;

        foreach (KeyValuePair<byte, Mini8Config> item in config.Mini8sConfig)
        {
            if (!hardwares.Mini8Channels.TryGetValueNotNull(item.Key, out ConcurrentDictionary<byte, ChannelData> hardwareChannels))
                continue;
            if (item.Value.ChannelConfig is null)
                continue;

            foreach (KeyValuePair<byte, ChannelConfig> channelConfig in item.Value.ChannelConfig)
            {
                if (!hardwareChannels.TryGetValueNotNull(channelConfig.Key, out ChannelData hardwareChannel))
                    continue;
                channelConfig.Value.Adapt(hardwareChannel);
                //MapData(channelConfig.Value, hardwareChannel);
            }
        }
    }

    public bool ReadConfigFromMini8(out TemperatureConfig? temperatureConfig)
    {
        temperatureConfig = null;

        TemperatureConfig cache = new()
        {
            ConfigName = "FromMini8",
            EditTime = DateTime.Now,
            Editor = "Mini8",
            Description = "This data is read from Mini8",
            Mini8sConfig = []
        };

        if (hardWareMonitor.GetCurrentStatus() is not IEnumerable<(byte channelIndex, Dictionary<byte, Mini8Output>? realtimeData)> statusGroups)
            goto final;

        Parallel.ForEach(statusGroups, (t) =>
        {
            byte mini8Index = t.channelIndex;
            Dictionary<byte, Mini8Output>? data = t.realtimeData;

            if (data is null)
                return;

            Mini8Config config = new()
            {
                Index = mini8Index,
                ChannelConfig = []
            };
            hardwares.Mini8Channels.TryGetValue(mini8Index, out var channels);

            foreach (KeyValuePair<byte, Mini8Output> channelRealTime in data)
            {
                ChannelConfig channelConfig = new();
                channelRealTime.Value.Adapt(channelConfig);
                channelConfig.Index = channelRealTime.Key;
                config.ChannelConfig[channelRealTime.Key] = channelConfig;  

                if (channels is null)
                    continue;

                if (!channels.TryGetValue(channelRealTime.Key, out var hardwareChannel) || hardwareChannel is null)
                    continue;

                //Take ChannelMode from Hareware configruation
                channelConfig.ChannelMode = hardwareChannel.ChannelMode;
            }

            lock (cache.Mini8sConfig)
                cache.Mini8sConfig[mini8Index] = config;
        });


        if (hardWareMonitor.GetCurrentLimit() is not IDictionary<byte, Dictionary<byte, Mini8Limit>?> limitGroups)
            goto final;

        Parallel.ForEach(limitGroups, (t) =>
        {
            byte mini8Index = t.Key;
            Dictionary<byte, Mini8Limit>? data = t.Value;

            if (data is null)
                return;

            if (!cache.Mini8sConfig.TryGetValueNotNull(mini8Index, out Mini8Config mini8Config))
                return;

            if (mini8Config.ChannelConfig is null)
                return;


            foreach (KeyValuePair<byte, Mini8Limit> limitPair in data)
            {
                if (!mini8Config.ChannelConfig.TryGetValueNotNull(limitPair.Key, out ChannelConfig channelConfig))
                    continue;
                Mini8Limit limit = limitPair.Value;

                channelConfig.Caps = limit.Caps;
                channelConfig.Floor = limit.Floor;
                channelConfig.CapsWarning = limit.CapsWarning;
                channelConfig.FloorWarning = limit.FloorWarning;
            }
        }
        );

    final:
        temperatureConfig = cache;
        return true;
    }
}

