// Copyright © Selmo Technology GmbH. All Rights Reserved

using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Novus.Connectivity.Common;
using SelmoStudio.Hmi.Common.SelmoProject.ProcessValue;
using SelmoStudio.Hmi.Server.Pagination;
using SelmoStudio.Hmi.Server.ProcessValue.Services;
using SelmoStudio.Hmi.Server.SelmoHmi.ProjectInformation.Service;
using SelmoStudio.Hmi.Server.SelmoHmi.Sequence.Model;
using SelmoStudio.Hmi.Server.SelmoHmi.Sequence.Model.Dto;
using SelmoStudio.Hmi.Server.SelmoHmi.Sequence.Model.Ids;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Component;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Component.Ids;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Component.Types;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Mappers;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Model;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Service;

namespace SelmoStudio.Hmi.Server.SelmoHmi.Sequence.Components
{
    public partial class SequenceSelectionComponent : IBaseProcessValueComponent, IAsyncDisposable
    {
        private DotNetObjectReference<SequenceSelectionComponent> _viewportReference;
        private Lazy<Task<IJSObjectReference>> _resizeCallbackRef;
        private bool _isDisposed;

        [Inject]
        private ILogger<SequenceSelectionComponent> _logger { get; set; }

        [Inject]
        private IProcessValueInfoService _processValueInfoService { get; set; }

        [Inject]
        private IProjectInformationService _projectInformationService { get; set; }

        [Inject]
        private IBrowserService _browserService { get; set; }

        [Inject]
        private IJSRuntime _jsRuntime { get; set; }

        public IEnumerable<ProjectItemCollectionSequence> _sequencesParameter { get; set; }

        [Parameter]
        public string Id { get; set; }

        [Parameter]
        public EventCallback<bool> StateChanged { get; set; }

        [Parameter]
        public EventCallback OnCurrentSequenceChanged { get; set; }

        [Parameter]
        public Action<bool> OnSequenceSelected { get; set; }

        [Parameter]
        public bool ShowOverview { get; set; }

        #region Init/Lifecycle

        protected override async Task OnInitializedAsync()
        {
            _logger.LogInformation("Sequence Selection Loading");
            _projectInformationService.SequenceUpdatedAsync += SequenceUpdatedAsync;

            InitializePagination();

            await base.OnInitializedAsync();
        }

        protected override async Task OnParametersSetAsync()
        {
            if (_projectInformationService.Sequences == _sequencesParameter)
            {
                return;
            }

            await SetVisibleSequences();

            _sequencesParameter = _projectInformationService.Sequences;
            _logger.LogInformation("Sequence Selection changed");
            await base.OnParametersSetAsync();
        }

        private bool _isReady;

        public bool IsRendered
        {
            get
            {
                return _isReady;
            }
        }

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                _viewportReference = DotNetObjectReference.Create(this);
                _resizeCallbackRef = new(() => _jsRuntime.InvokeAsync<IJSObjectReference>(
                         "registerViewportChangeCallback", _viewportReference).AsTask());
            }
            base.OnAfterRender(firstRender);
            _isReady = true;
        }

        #endregion Init/Lifecycle

        #region Pagination

        private PaginationCache<SequenceInfoDto> _sequencesPagination;
        private SequenceInfoDto[] _currentPagination;
        private bool _paginationIsActive => _sequencesPagination.HasMoreThanOnePage();
        private int _calculatedPageSize = 1;

        [JSInvokable]
        public async Task OnResize()
        {
            if (!_isDisposed)
            {
                await SetVisibleSequences();
            }
        }

        private void InitializePagination()
        {
            _sequencesPagination = new(_projectInformationService.Sequences.Select(s => s.SequenceInfo).ToList());
        }

        private void UpdateCurrentPagination()
        {
            _currentPagination = _sequencesPagination.GetCurrent();
        }

        private async Task SetVisibleSequences()
        {
            await CalculateVisibleSequencesPageSize();

            _sequencesPagination.SetPageSize(_calculatedPageSize);
            _sequencesPagination.SelectItem(_projectInformationService.CurrentSequence.SequenceInfo);
            UpdateCurrentPagination();

            await InvokeAsync(() => StateHasChanged());
        }

        private async Task CalculateVisibleSequencesPageSize()
        {
            var dimensions = await _browserService.GetDimensions();
            _calculatedPageSize = ScreenSizeHelper.GetPageSize(dimensions.Width);
        }

        private void OnNextClicked()
        {
            _sequencesPagination.GoToNextPage();
            UpdateCurrentPagination();
        }

        private void OnPreviousClicked()
        {
            _sequencesPagination.GoToPreviousPage();
            UpdateCurrentPagination();
        }

        #endregion Pagination

        private async Task ChangeToOverview()
        {
            ResetCurrentHardwareZoneSequence();
            ShowOverview = true;
            await StateChanged.InvokeAsync(ShowOverview);
        }

        private void ResetCurrentHardwareZoneSequence()
        {
            if (_sequencesPagination != null)
            {
                _sequencesPagination.Reset();
                UpdateCurrentPagination();
            }
        }

        private async Task ChangeCurrentSequence(SequenceInfoDto sequence)
        {
            var foundSequence = _projectInformationService.Sequences
                .FirstOrDefault(seq => seq.SequenceInfo.Id.Equals(sequence.Id));

            if (foundSequence != null)
            {
                ShowOverview = false;

                _projectInformationService.SelectSequence(foundSequence);

                UpdateCurrentPagination();

                await StateChanged.InvokeAsync(ShowOverview);
                await OnCurrentSequenceChanged.InvokeAsync();
            }
        }

        private async Task SequenceUpdatedAsync()
        {
            InitializePagination();
            await SetVisibleSequences();
        }

        #region Handle ProcessValues

        private Dictionary<SequenceId, ProcessValuesSequenceStatus> _lookUpTableSeqStatus;
        private Dictionary<ProcessValueId, IProcessValue> _lookUpTableSeqStatusValues = new();

        private void CheckValue(IProcessValue value)
        {
            foreach (var item in _lookUpTableSeqStatus)
            {
                if (item.Value.Contains(value.ValueInfo.Id))
                {
                    _lookUpTableSeqStatusValues[value.ValueInfo.Id] = value;
                }
            }
        }

        private List<string> GetIconList(SequenceInfoDto seq)
        {
            var iconList = new List<IProcessValueInfo>();
            if (_lookUpTableSeqStatus == null)
            {
                return StatusToIconMapper.MapFromProcessValues(iconList);
            }

            if (_lookUpTableSeqStatus.TryGetValue(seq.Id, out var infoCollection))
            {
                foreach (var info in infoCollection.GetAllValueInfos())
                {
                    if (_lookUpTableSeqStatusValues.ContainsKey(info.Id))
                    {
                        if (_lookUpTableSeqStatusValues[info.Id].Value != null)
                        {
                            var value = (bool)_lookUpTableSeqStatusValues[info.Id].Value;
                            if (value)
                                iconList.Add(info);
                        }
                    }
                }
            }

            return StatusToIconMapper.MapFromProcessValues(iconList);
        }

        private OperationMode GetLedStatus(SequenceInfoDto seq)
        {
            if (_lookUpTableSeqStatus == null || !_lookUpTableSeqStatus.ContainsKey(seq.Id))
            {
                return OperationMode.Error;
            }

            var infos = _lookUpTableSeqStatus[seq.Id];

            return StatusHelper.GetOperationMode(infos, _lookUpTableSeqStatusValues);
        }

        private OperationMode GetOperationMode(ProcessValuesSequenceStatus infos, Dictionary<ProcessValueId, IProcessValue> sequenceStatusValues)
        {
            if (!sequenceStatusValues.ContainsKey(infos.AutomaticReleased.Id))
                return OperationMode.Error;

            return OperationMode.Error;
        }

        public async ValueTask DisposeAsync()
        {
            if (!_isDisposed)
            {
                _isDisposed = true;

                _projectInformationService.SequenceUpdatedAsync -= SequenceUpdatedAsync;

                if (_resizeCallbackRef != null && _resizeCallbackRef.IsValueCreated)
                {
                    var module = await _resizeCallbackRef.Value;
                    await module.DisposeAsync();
                }

                if (_viewportReference != null)
                {
                    _viewportReference.Dispose();
                    _viewportReference = null;
                }
            }
        }

        #endregion Handle ProcessValues

        #region ComponentHandling

        public event EventHandler<ComponentEventArgs> ComponentChangeEvent;

        public List<IProcessValueInfo> GetValueInfos()
        {
            if (!IsRendered)
                return new();

            var values = _processValueInfoService
               .CreateSeqStatusRegistration(_projectInformationService.Sequences.ToList()).ValueInfos;

            _lookUpTableSeqStatus = _processValueInfoService
                .GetLookUpTableForSeqStatus(_projectInformationService.Sequences.ToList());

            _readProcessValues = values.ToList();
            return values.ToList();
        }

        private List<IProcessValueInfo> _readProcessValues = new();

        public void Update(IDictionary<ProcessValueId, IProcessValue> updatedValues)
        {
            if (!IsRendered)
                return;

            foreach (var processValue in _readProcessValues)
            {
                if (updatedValues.ContainsKey(processValue.Id))
                {
                    CheckValue(updatedValues[processValue.Id]);
                }
            }
        }

        public void TriggerComponentChangeEvent(ComponentId id, EventType type, Guid valueId)
        {
            var processValue = GetProcessValue(valueId);

            //ComponentChangeEvent?.Invoke(this, ComponentEventArgs.Create(e, processValueHandleInfo));
        }

        private IProcessValue GetProcessValue(Guid valueId)
        {
            //TODO IMPLEMENT
            return null;
        }

        #endregion ComponentHandling
    }
}