using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.ComponentModel;
using System.Threading;

public class NetworkShareAccess : IDisposable
{
    private readonly string _networkPath;
    private readonly string _username;
    private readonly string _password;

    private bool _isConnected = false;
    private static readonly object _lock = new object();

    public NetworkShareAccess(string networkPath, string username, string password)
    {
        _networkPath = networkPath?.Trim().Replace('/', '\\') ?? throw new ArgumentNullException(nameof(networkPath));
        if (!_networkPath.StartsWith(@"\\"))
            throw new ArgumentException("Network path must start with \\\\");

        _username = username ?? throw new ArgumentNullException(nameof(username));
        _password = password ?? throw new ArgumentNullException(nameof(password));
    }

    public void ConnectToNetworkPath()
    {
        lock (_lock)
        {
            if (_isConnected) return;

            int retries = 0;
            while (retries < 3)
            {
                try
                {
                    var netResource = new NetResource
                    {
                        Scope = ResourceScope.GlobalNetwork,
                        ResourceType = ResourceType.Disk,
                        DisplayType = ResourceDisplayType.Share,
                        RemoteName = _networkPath
                    };

                    int result = WNetAddConnection2(netResource, _password, _username, 0);

                    if (result == 0)
                    {
                        _isConnected = true;
                        Console.WriteLine($"Connected to {_networkPath}");
                        return;
                    }

                    if (result == 1219) // Multiple connections
                    {
                        WNetCancelConnection2(_networkPath, 0, true);
                        Thread.Sleep(1000);
                        retries++;
                        continue;
                    }

                    throw new IOException($"Failed to connect. Error {result}: {GetNetworkErrorDescription(result)}");
                }
                catch
                {
                    if (++retries >= 3) throw;
                    Thread.Sleep(1000);
                }
            }
        }
    }

    public void DisconnectFromNetworkShare()
    {
        lock (_lock)
        {
            if (!_isConnected) return;

            try
            {
                int result = WNetCancelConnection2(_networkPath, 0, true);
                if (result != 0)
                    Console.WriteLine($"Warning: Disconnect failed (Error {result})");
            }
            finally
            {
                _isConnected = false;
            }
        }
    }

    public void Dispose() => DisconnectFromNetworkShare();

    private string GetNetworkErrorDescription(int errorCode) => errorCode switch
    {
        5 => "Access denied",
        53 => "Network path not found",
        67 => "Network name not found",
        85 => "Network connection already exists",
        86 => "Invalid password",
        1219 => "Multiple connections to a server or shared resource not allowed",
        _ => new Win32Exception(errorCode).Message
    };

    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags);

    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    private static extern int WNetCancelConnection2(string name, int flags, bool force);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private class NetResource
    {
        public ResourceScope Scope;
        public ResourceType ResourceType;
        public ResourceDisplayType DisplayType;
        public int Usage;
        public string? LocalName; //? or required TBD 
        public string? RemoteName; //
        public string? Comment; //
        public string? Provider; //
    }

    private enum ResourceScope { Connected = 1, GlobalNetwork, Remembered, Recent, Context }
    private enum ResourceType { Any = 0, Disk = 1, Print = 2, Reserved = 8 }
    private enum ResourceDisplayType { Generic = 0x0, Domain = 0x01, Server = 0x02, Share = 0x03, File = 0x04, Group = 0x05 }
}