// Copyright © Selmo Technology GmbH. All Rights Reserved

using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Microsoft.JSInterop.Implementation;

namespace SelmoStudio.Hmi.Server.SelmoHmi.Shared.Service
{
    public class ViewportResizeService : IViewportResizeService, IAsyncDisposable
    {
        private readonly IJSRuntime _jsRuntime;
        private readonly ILogger<ViewportResizeService> _logger;
        private Lazy<Task<IJSObjectReference>> _jsDiagramUtilObjectReference;
        private bool _disposed;
        private DotNetObjectReference<IComponent> _componentRef;
        private ElementReference _contentContainerRef;
        private ElementReference _parentContainerRef;
        private ElementReference _widthContainerRef;
        private Lazy<Task<IJSObjectReference>> _resizeObserverRef;
        private Lazy<Task<IJSObjectReference>> _widthObserverRef;

        public ViewportResizeService(IJSRuntime jsRuntime, ILogger<ViewportResizeService> logger)
        {
            _jsRuntime = jsRuntime;
            _logger = logger;
        }

        public async Task Initialize(DotNetObjectReference<IComponent> component, ElementReference contentContainerRef,
            ElementReference parentContainerRef)
        {
            if (_disposed)
            {
                _logger.LogWarning("Attempted to initialize disposed service");
                return;
            }

            try
            {
                _componentRef = component;
                _contentContainerRef = contentContainerRef;
                _parentContainerRef = parentContainerRef;
                _jsDiagramUtilObjectReference = GetDiagramUtilJSObjectReference();
                var jsObject = await _jsDiagramUtilObjectReference.Value;
                _resizeObserverRef = new Lazy<Task<IJSObjectReference>>(() =>
                    jsObject.InvokeAsync<IJSObjectReference>("initializeResizeObserver",
                        _componentRef, _contentContainerRef, _parentContainerRef).AsTask());
            }
            catch (JSDisconnectedException)
            {
                _logger.LogInformation("JS runtime disconnected during initialization");
            }
            catch (ObjectDisposedException)
            {
                _logger.LogInformation("Service disposed during initialization");
            }
        }

        public async Task Initialize(DotNetObjectReference<IComponent> component, ElementReference containerRef)
        {
            if (_disposed)
            {
                _logger.LogWarning("Attempted to initialize disposed service");
                return;
            }

            try
            {
                _componentRef = component;
                _widthContainerRef = containerRef;
                _jsDiagramUtilObjectReference = GetDiagramUtilJSObjectReference();
                var jsObject = await _jsDiagramUtilObjectReference.Value;
                _widthObserverRef = new Lazy<Task<IJSObjectReference>>(() => jsObject.InvokeAsync<IJSObjectReference>("initializeWidthObserver",
                    _componentRef, _widthContainerRef).AsTask());
            }
            catch (JSDisconnectedException)
            {
                _logger.LogInformation("JS runtime disconnected during initialization");
            }
            catch (ObjectDisposedException)
            {
                _logger.LogInformation("Service disposed during initialization");
            }
        }

        public async Task<double> GetWidthAsync(ElementReference containerRef)
        {
            if (_disposed)
            {
                _logger.LogWarning("Attempted to get width from disposed service");
                return 0;
            }

            try
            {
                _widthContainerRef = containerRef;
                _jsDiagramUtilObjectReference ??= GetDiagramUtilJSObjectReference();
                var jsObject = await _jsDiagramUtilObjectReference.Value;
                return await jsObject.InvokeAsync<double>("getElementWidth", containerRef);
            }
            catch (JSDisconnectedException)
            {
                _logger.LogInformation("JS runtime disconnected while getting width");
                return 0;
            }
            catch (ObjectDisposedException)
            {
                _logger.LogInformation("Service disposed while getting width");
                return 0;
            }
        }

        private Lazy<Task<IJSObjectReference>> GetDiagramUtilJSObjectReference()
        {
            return new Lazy<Task<IJSObjectReference>>(async () => await _jsRuntime.InvokeAsync<IJSObjectReference>("import", "/js/DiagramUtil.js").AsTask());
        }

        public async ValueTask DisposeAsync()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;

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

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

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

            _componentRef?.Dispose();
        }
    }
}