using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
namespace C4IT.LIAM
{
    public static class LiamInitializer
    {
        static public cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData)
        {
            return new cLiamProviderFileshare(LiamConfiguration, ProviderData);
        }
    }

    public class cLiamProviderFileshare : cLiamProviderBase
    {
        public readonly cFile MsSharepoint = new cMsGraphSharepoint(new cMsGraphBase());
        public Exception LastException { get; private set; } = null;
        public string LastErrorMessage { get; private set; } = null;
        public bool IsOnline { get; private set; } = false;
        public string AccessToken { get; private set; } = null;
        public DateTime TokenExpiresIn { get; private set; } = DateTime.MinValue;
        private cFileshareLogonInfo privLogonInfo = null;

        public cLiamProviderFileshare(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) :
            base(LiamConfiguration, ProviderData)
        { }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ResetError()
        {
            LastException = null;
            LastErrorMessage = null;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void SetErrorException(string Action, Exception E, LogLevels lev = LogLevels.Error)
        {
            LastException = E;
            LastErrorMessage = Action + ": " + E.Message;
            cLogManager.LogEntry(Action, lev);
        }
        public override async Task<cLiamDataAreaBase> LoadDataArea(string UID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            await Task.Delay(0);
            try
            {
                var lstIds = UID.Split('|');



                return null;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }
        public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int Depth = -1)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!await LogonAsync())
                    return null;

                var DataAreas = new List<cLiamDataAreaBase>();

                var DAL = await MsSharepoint.RequestTeamsListAsync();
                if (DAL == null)
                    return null;

                foreach (var Entry in DAL)
                {
                    if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(Entry.Key, this.DataAreaRegEx).Success)
                        continue;

                    var MsTeam = await MsSharepoint.RequestGroupInfoAsync(Entry.Value);
                    if (MsTeam == null)
                        continue;

                    var Team = new cLiamMsTeamsTeam(this, MsTeam);
                    DataAreas.Add(Team);
                }

                if (Depth > 0)
                {
                    var allChilds = new List<cLiamDataAreaBase>();
                    foreach (var Entry in DataAreas)
                    {
                        var entryChilds = await (Entry as cLiamMsTeamsTeam).getChannels(true);
                        if (entryChilds != null && entryChilds.Count > 0)
                            allChilds.AddRange(entryChilds);
                    }
                    DataAreas.AddRange(allChilds);
                }

                return DataAreas;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return null;
        }
        public override async Task<bool> LogonAsync()
        {
            return await LogonAsync(true);
        }
        public async Task<bool> LogonAsync(bool force = false)
        {
            if (!force && IsOnline)
                return true;

            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var LI = new cFileshareLogonInfo()
                {
                    Domain = this.Domain,
                    User = this.Credential?.Identification,
                    UserSecret = this.Credential?.Secret,
                    TargetNetworkName = this.RootPath
                };
                var RetVal = await LogonAsync(LI);
                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return false;
        }
        private async Task<bool> privRelogon()
        {
            if (DateTime.UtcNow < TokenExpiresIn)
                return true;
            if (privLogonInfo == null)
                return false;
            var RetVal = await privLogonAsync(privLogonInfo);
            return RetVal;
        }

        public async Task<bool> LogonAsync(cFileshareLogonInfo LogonInfo)
        {
            var RetVal = await privLogonAsync(LogonInfo);
            if (RetVal == true)
                privLogonInfo = LogonInfo;
            return RetVal;
        }
        private async Task<bool> privLogonAsync(cFileshareLogonInfo LogonInfo)
        {
            //TODO: remove dummy delay?
            await Task.Delay(0);
            try
            {
                ResetError();
                IsOnline = false;

                var netResource = new NetResource()
                {
                    Scope = ResourceScope.GlobalNetwork,
                    ResourceType = ResourceType.Disk,
                    DisplayType = ResourceDisplaytype.Share,
                    RemoteName = LogonInfo.TargetNetworkName
                };

                var result = WNetAddConnection2(
                    netResource,
                    LogonInfo.UserSecret,
                    LogonInfo.User,
                    0);

                if (result != 0)
                {
                    //DefaultLogger.LogEntry(LogLevels.Debug, $"Error in connect occured ({result}) {new Win32Exception(result).Message}");
                    //throw new Win32Exception(result);
                    return false;
                }

                IsOnline = true;
                return true;


            }
            catch (Exception E)
            {
                SetErrorException("exception error while network drive login", E, LogLevels.Debug);
                cLogManager.LogException(E, LogLevels.Debug);
            }

            return false;
        }
        [DllImport("mpr.dll")]
        private static extern int WNetAddConnection2(NetResource netResource,
    string password, string username, int flags);

    }

    public class cFileshareLogonInfo
    {
        public string Domain;
        public string User;
        public string UserSecret;
        public string TargetNetworkName;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class NetResource
    {
        public ResourceScope Scope;
        public ResourceType ResourceType;
        public ResourceDisplaytype DisplayType;
        public int Usage;
        public string LocalName;
        public string RemoteName;
        public string Comment;
        public string Provider;
    }

    public enum ResourceScope : int
    {
        Connected = 1,
        GlobalNetwork,
        Remembered,
        Recent,
        Context
    };

    public enum ResourceType : int
    {
        Any = 0,
        Disk = 1,
        Print = 2,
        Reserved = 8,
    }

    public enum ResourceDisplaytype : int
    {
        Generic = 0x0,
        Domain = 0x01,
        Server = 0x02,
        Share = 0x03,
        File = 0x04,
        Group = 0x05,
        Network = 0x06,
        Root = 0x07,
        Shareadmin = 0x08,
        Directory = 0x09,
        Tree = 0x0a,
        Ndscontainer = 0x0b
    }

    public enum NetError : uint
    {
        NERR_Success = 0,
        NERR_BASE = 2100,
        NERR_UnknownDevDir = (NERR_BASE + 16),
        NERR_DuplicateShare = (NERR_BASE + 18),
        NERR_BufTooSmall = (NERR_BASE + 23),
    }
    public enum SHARE_TYPE : uint
    {
        STYPE_DISKTREE = 0,
        STYPE_PRINTQ = 1,
        STYPE_DEVICE = 2,
        STYPE_IPC = 3,
        STYPE_SPECIAL = 0x80000000,
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct SHARE_INFO_1
    {
        public string shi1_netname;
        public uint shi1_type;
        public string shi1_remark;
        public SHARE_INFO_1(string sharename, uint sharetype, string remark)
        {
            this.shi1_netname = sharename;
            this.shi1_type = sharetype;
            this.shi1_remark = remark;
        }
        public override string ToString()
        {
            return shi1_netname;
        }
    }

    public class cLiamNtfsShare : cLiamDataAreaBase
    {
        public new readonly cLiamProviderFileshare Provider = null;

        private readonly cMsGraphResultGroup MsTeam = null;

        public cLiamMsTeamsTeam(cLiamProviderMsTeams Provider, cMsGraphResultGroup MsTeam) :
            base(Provider)
        {
            this.Provider = Provider;
            this.MsTeam = MsTeam;

            this.TechnicalName = MsTeam.DisplayName;
            this.Level = 0;
            this.UID = getUID(MsTeam.ID);
            this.ParentUID = "";
            this.DataType = eLiamDataAreaTypes.MsTeamsTeam;
            this.SupportsOwners = true;
            this.SupportsPermissions = true;
        }

        internal static string getUID(string TeamID)
        {
            return $"{TeamID}";
        }

        internal async Task<List<cLiamDataAreaBase>> getChannels(bool OnlyPrivate)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (MsTeam == null)
                    return null;

                var RegExFilter = this.Provider?.DataAreaRegEx;
                var SP = this.Provider?.MsSharepoint;
                var WithoutPrivateChannels = this.Provider?.WithoutPrivateChannels ?? false;

                if (MsTeam.Channels == null)
                    if (SP == null || !await MsTeam.ResolveChannels(SP, OnlyPrivate))
                        return null;

                var RetVal = new List<cLiamDataAreaBase>(MsTeam.Channels.Count);
                foreach (var Entry in MsTeam.Channels.Values)
                {
                    if (!string.IsNullOrEmpty(RegExFilter) && !Regex.Match(Entry.DisplayName, RegExFilter).Success)
                        continue;
                    if (WithoutPrivateChannels && !string.IsNullOrEmpty(Entry.MembershipType) && Entry.MembershipType == "private")
                        continue;

                    var DA = new cLiamMsTeamsChannel(Provider, this, Entry);
                    RetVal.Add(DA);
                }

                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var RetVal = await getChannels(false);
                if (RetVal != null)
                    RetVal.AddRange(await getChildrenFromListAsync(RetVal, Depth));

                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public static async Task<cLiamMsTeamsTeam> Load(cLiamProviderMsTeams Provider, string UID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var arrIDs = UID.Split('|');
                if (arrIDs.Length != 1)
                    return null;

                var TeamID = cLiamProviderMsTeams.getUidItem(ref UID);

                var T = await Provider.MsSharepoint.RequestGroupInfoAsync(TeamID);
                if (T == null)
                    return null;

                var RetVal = new cLiamMsTeamsTeam(Provider, T);

                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }


        }

    }
    public class cNtfsResultBase
    {
        public string ID { get; private set; } = null;
        public string DisplayName { get; private set; } = null;
        public dynamic Result { get; private set; } = null;

        public cNtfsResultBase(dynamic Result)
        {
            this.Result = Result;
        }

        public cNtfsResultBase(cNtfsResultBase Result)
        {
            if (Result == null)
                return;

            this.Result = Result.Result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static string GetStringFromDynamic(dynamic O, string ProperyName)
        {
            try
            {
                return (string)O[ProperyName];
            }
            catch { }
            return null;
        }
    }
    public class cLiamNtfsFolder : cLiamDataAreaBase
    {
        public new readonly cLiamProviderFileshare Provider = null;
        public readonly cLiamNtfsShare Share = null;
        public readonly cMsGraphResultFileObject MsFolder = null;

        public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (MsFolder == null)
                    return null;

                var RegExFilter = this.Provider?.DataAreaRegEx;
                var SP = this.Provider?.MsSharepoint;

                if (MsFolder.Folders == null)
                    if (SP == null || !await MsFolder.ResolveFolders(SP))
                        return null;

                var DataAreas = new List<cLiamDataAreaBase>();
                foreach (var Entry in MsFolder.Folders.Values)
                {
                    if (!string.IsNullOrEmpty(RegExFilter) && !Regex.Match(Entry.DisplayName, RegExFilter).Success)
                        continue;

                    var DA = new cLiamMsTeamsFolder(this.Provider, this.Channel, this, Entry, this.ChannelUID);
                    DataAreas.Add(DA);
                }
                var CL = await getChildrenFromListAsync(DataAreas, Depth);
                if (CL != null)
                    DataAreas.AddRange(CL);

                return DataAreas;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public static async Task<cLiamMsTeamsFolder> Load(cLiamProviderMsTeams Provider, string UID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var arrIDs = UID.Split('|');
                if (arrIDs.Length != 4)
                    return null;

                var FolderID = cLiamProviderMsTeams.getUidItem(ref UID);
                var DriveID = cLiamProviderMsTeams.getUidItem(ref UID);

                var F = await Provider.MsSharepoint.RequestFileObjectInfo(DriveID, FolderID);
                if (F == null)
                    return null;

                var RetVal = new cLiamMsTeamsFolder(Provider, null, null, F, UID);

                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }


        }
    }
}
