using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AdvChkSys.Interfaces;

namespace AdvChkSys.Threading
{
    /// <summary>
    /// Provides a centralized manager for all threading operations in the chunk system.
    /// </summary>
    public class ChunkThreadingManager : IDisposable
    {
        private static ChunkThreadingManager? _instance;
        private static readonly object _instanceLock = new();

        // Thread safety utilities
        private readonly ChunkThreadSafetyManager _threadSafety;
        private readonly ChunkAsyncLock _asyncLock;

        // Operation queue
        private readonly ChunkOperationQueue _operationQueue;

        // Cancellation for shutdown
        private readonly CancellationTokenSource _shutdownCts = new();

        /// <summary>
        /// Gets the singleton instance of the ChunkThreadingManager.
        /// </summary>
        public static ChunkThreadingManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_instanceLock)
                    {
                        _instance ??= new ChunkThreadingManager();
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// Initializes a new instance of the ChunkThreadingManager class.
        /// </summary>
        private ChunkThreadingManager()
        {
            _threadSafety = new ChunkThreadSafetyManager(
                ChunkThreadingConfiguration.LockCleanupIntervalMinutes);

            _asyncLock = new ChunkAsyncLock(
                ChunkThreadingConfiguration.LockCleanupIntervalMinutes);

            _operationQueue = new ChunkOperationQueue(
                ChunkThreadingConfiguration.DefaultMaxDegreeOfParallelism);
        }

        /// <summary>
        /// Gets the thread safety manager for synchronous locking.
        /// </summary>
        public ChunkThreadSafetyManager ThreadSafety => _threadSafety;

        /// <summary>
        /// Gets the async lock manager for asynchronous locking.
        /// </summary>
        public ChunkAsyncLock AsyncLock => _asyncLock;

        /// <summary>
        /// Gets the operation queue for sequential chunk operations.
        /// </summary>
        public ChunkOperationQueue OperationQueue => _operationQueue;

        /// <summary>
        /// Acquires an exclusive lock on a chunk.
        /// </summary>
        /// <param name="chunk">The chunk to lock</param>
        /// <returns>A disposable that releases the lock when disposed</returns>
        public IDisposable AcquireLock(IChunk chunk)
        {
            return _threadSafety.AcquireLock(chunk);
        }

        /// <summary>
        /// Acquires a read lock on a chunk.
        /// </summary>
        /// <param name="chunk">The chunk to lock</param>
        /// <returns>A disposable that releases the lock when disposed</returns>
        public IDisposable AcquireReadLock(IChunk chunk)
        {
            return _threadSafety.AcquireReadLock(chunk);
        }

        /// <summary>
        /// Acquires a write lock on a chunk.
        /// </summary>
        /// <param name="chunk">The chunk to lock</param>
        /// <returns>A disposable that releases the lock when disposed</returns>
        public IDisposable AcquireWriteLock(IChunk chunk)
        {
            return _threadSafety.AcquireWriteLock(chunk);
        }

        /// <summary>
        /// Acquires a lock on a chunk asynchronously.
        /// </summary>
        /// <param name="chunk">The chunk to lock</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>A disposable that releases the lock when disposed</returns>
        public Task<IDisposable> LockAsync(IChunk chunk, CancellationToken cancellationToken = default)
        {
            return _asyncLock.LockAsync(chunk, cancellationToken);
        }

        /// <summary>
        /// Enqueues an operation to be performed on a chunk.
        /// </summary>
        /// <param name="chunk">The chunk to operate on</param>
        /// <param name="operation">The operation to perform</param>
        /// <returns>A task that completes when the operation is done</returns>
        public Task EnqueueOperationAsync(IChunk chunk, Func<Task> operation)
        {
            return _operationQueue.EnqueueOperationAsync(chunk, operation);
        }

        /// <summary>
        /// Runs an action with a lock on the chunk.
        /// </summary>
        /// <param name="chunk">The chunk to lock</param>
        /// <param name="action">The action to perform</param>
        public void WithLock(IChunk chunk, Action action)
        {
            using var lockObj = AcquireLock(chunk);
            action();
        }

        /// <summary>
        /// Runs a function with a lock on the chunk and returns the result.
        /// </summary>
        /// <typeparam name="T">The type of result</typeparam>
        /// <param name="chunk">The chunk to lock</param>
        /// <param name="func">The function to perform</param>
        /// <returns>The result of the function</returns>
        public T WithLock<T>(IChunk chunk, Func<T> func)
        {
            using var lockObj = AcquireLock(chunk);
            return func();
        }

        /// <summary>
        /// Runs an async action with a lock on the chunk.
        /// </summary>
        /// <param name="chunk">The chunk to lock</param>
        /// <param name="action">The async action to perform</param>
        /// <param name="cancellationToken">Cancellation token</param>
        public async Task WithLockAsync(IChunk chunk, Func<Task> action, CancellationToken cancellationToken = default)
        {
            using var lockObj = await LockAsync(chunk, cancellationToken).ConfigureAwait(false);
            await action().ConfigureAwait(false);
        }

        /// <summary>
        /// Runs an async function with a lock on the chunk and returns the result.
        /// </summary>
        /// <typeparam name="T">The type of result</typeparam>
        /// <param name="chunk">The chunk to lock</param>
        /// <param name="func">The async function to perform</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>The result of the function</returns>
        public async Task<T> WithLockAsync<T>(IChunk chunk, Func<Task<T>> func, CancellationToken cancellationToken = default)
        {
            using var lockObj = await LockAsync(chunk, cancellationToken).ConfigureAwait(false);
            return await func().ConfigureAwait(false);
        }

        /// <summary>
        /// Runs an action on multiple chunks with proper locking to avoid deadlocks.
        /// </summary>
        /// <param name="chunks">The chunks to lock, in order</param>
        /// <param name="action">The action to perform</param>
        public void WithMultiLock(IChunk[] chunks, Action action)
        {
            // Sort chunks by ID to prevent deadlocks
            var sortedChunks = SortChunksById(chunks);

            // Acquire locks in order
            var locks = new IDisposable[sortedChunks.Length];
            try
            {
                for (int i = 0; i < sortedChunks.Length; i++)
                {
                    locks[i] = AcquireLock(sortedChunks[i]);
                }

                // Execute the action
                action();
            }
            finally
            {
                // Release locks in reverse order
                for (int i = locks.Length - 1; i >= 0; i--)
                {
                    locks[i]?.Dispose();
                }
            }
        }

        /// <summary>
        /// Runs an async action on multiple chunks with proper locking to avoid deadlocks.
        /// </summary>
        /// <param name="chunks">The chunks to lock, in order</param>
        /// <param name="action">The async action to perform</param>
        /// <param name="cancellationToken">Cancellation token</param>
        public async Task WithMultiLockAsync(IChunk[] chunks, Func<Task> action, CancellationToken cancellationToken = default)
        {
            // Sort chunks by ID to prevent deadlocks
            var sortedChunks = SortChunksById(chunks);

            // Acquire locks in order
            var locks = new IDisposable[sortedChunks.Length];
            try
            {
                for (int i = 0; i < sortedChunks.Length; i++)
                {
                    locks[i] = await LockAsync(sortedChunks[i], cancellationToken).ConfigureAwait(false);
                }

                // Execute the action
                await action().ConfigureAwait(false);
            }
            finally
            {
                // Release locks in reverse order
                for (int i = locks.Length - 1; i >= 0; i--)
                {
                    locks[i]?.Dispose();
                }
            }
        }

        /// <summary>
        /// Sorts chunks by ID to prevent deadlocks when acquiring multiple locks.
        /// </summary>
        private IChunk[] SortChunksById(IChunk[] chunks)
        {
            // Use System.Linq for OrderBy
            return chunks.OrderBy(c => c.GetHashCode()).ToArray();
        }

        /// <summary>
        /// Disposes all resources.
        /// </summary>
        public void Dispose()
        {
            _shutdownCts.Cancel();

            // Shutdown operation queue
            _operationQueue.ShutdownAsync().GetAwaiter().GetResult();

            // Dispose thread safety managers
            (_threadSafety as IDisposable)?.Dispose();
            (_asyncLock as IDisposable)?.Dispose();

            _shutdownCts.Dispose();
        }

        /// <summary>
        /// Resets the singleton instance (for testing).
        /// </summary>
        internal static void ResetInstance()
        {
            lock (_instanceLock)
            {
                _instance?.Dispose();
                _instance = null;
            }
        }
    }
}