using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Serilog;
using Outlook = Microsoft.Office.Interop.Outlook;

namespace OutlookAddin.Services
{
    public sealed class FolderResolver
    {
        private readonly Outlook.Application _app;
        private readonly ILogger _logger;

        public FolderResolver(Outlook.Application app, ILogger logger)
        {
            _app = app ?? throw new ArgumentNullException(nameof(app));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        /// <summary>
        /// Resolves a stored "<see cref="WatchedFolder.StoreId"/> + <see cref="WatchedFolder.FolderPath"/>"
        /// pair back to a live <see cref="Outlook.Folder"/>. Path format is
        /// <c>\\Inbox\\Clients</c> (backslash-separated, starting with the
        /// store root folder). Caller owns the returned RCW and must release it.
        /// </summary>
        public Outlook.Folder? Resolve(string storeId, string folderPath)
        {
            if (string.IsNullOrWhiteSpace(storeId) || string.IsNullOrWhiteSpace(folderPath)) return null;

            Outlook.NameSpace? ns = null;
            Outlook.Stores? stores = null;
            Outlook.Store? store = null;
            Outlook.Folder? root = null;
            try
            {
                ns = _app.Session;
                stores = ns.Stores;
                for (int i = 1; i <= stores.Count; i++)
                {
                    var candidate = stores[i];
                    try
                    {
                        if (string.Equals(candidate.StoreID, storeId, StringComparison.Ordinal))
                        {
                            store = candidate;
                            break;
                        }
                    }
                    finally
                    {
                        if (store != candidate && Marshal.IsComObject(candidate))
                        {
                            Marshal.ReleaseComObject(candidate);
                        }
                    }
                }
                if (store == null) return null;

                root = (Outlook.Folder)store.GetRootFolder();
                var segments = SplitPath(folderPath);
                return Descend(root, segments, 0);
            }
            catch (Exception ex)
            {
                _logger.Warning(ex, "FolderResolver.Resolve({StoreId}, {FolderPath}) failed", storeId, folderPath);
                return null;
            }
            finally
            {
                if (store != null && Marshal.IsComObject(store)) Marshal.ReleaseComObject(store);
                if (stores != null && Marshal.IsComObject(stores)) Marshal.ReleaseComObject(stores);
                if (ns != null && Marshal.IsComObject(ns)) Marshal.ReleaseComObject(ns);
                // Caller owns root only if it IS the returned folder (segments was empty).
            }
        }

        private Outlook.Folder? Descend(Outlook.Folder current, IReadOnlyList<string> segments, int index)
        {
            if (current == null) return null;
            if (index >= segments.Count) return current;

            Outlook.Folders? children = null;
            try
            {
                children = current.Folders;
                for (int i = 1; i <= children.Count; i++)
                {
                    var child = (Outlook.Folder)children[i];
                    try
                    {
                        if (string.Equals(child.Name, segments[index], StringComparison.Ordinal))
                        {
                            // Move ownership down — release the current folder above us, return descended.
                            var descended = Descend(child, segments, index + 1);
                            if (descended != child && Marshal.IsComObject(child)) Marshal.ReleaseComObject(child);
                            if (current != null && index > 0 && Marshal.IsComObject(current)) Marshal.ReleaseComObject(current);
                            return descended;
                        }
                    }
                    catch
                    {
                        if (Marshal.IsComObject(child)) Marshal.ReleaseComObject(child);
                        throw;
                    }
                    if (Marshal.IsComObject(child)) Marshal.ReleaseComObject(child);
                }
                return null;
            }
            finally
            {
                if (children != null && Marshal.IsComObject(children)) Marshal.ReleaseComObject(children);
            }
        }

        public static string BuildPath(Outlook.Folder folder)
        {
            // Outlook gives us "\\Top of Outlook data file\Inbox\Clients" via
            // Folder.FolderPath; the store root prefix matches Store.DisplayName
            // and varies per provider — peel it off to get a portable path.
            if (folder == null) return string.Empty;
            try
            {
                var raw = folder.FolderPath ?? string.Empty;
                // Strip the leading backslashes and the first segment (store name).
                if (raw.StartsWith("\\\\", StringComparison.Ordinal))
                {
                    raw = raw.Substring(2);
                }
                var firstSlash = raw.IndexOf('\\');
                if (firstSlash >= 0)
                {
                    raw = raw.Substring(firstSlash);
                }
                if (string.IsNullOrEmpty(raw)) return "\\";
                if (!raw.StartsWith("\\", StringComparison.Ordinal)) raw = "\\" + raw;
                return raw;
            }
            catch
            {
                return string.Empty;
            }
        }

        private static IReadOnlyList<string> SplitPath(string folderPath)
        {
            var trimmed = folderPath.TrimStart('\\');
            if (string.IsNullOrEmpty(trimmed)) return Array.Empty<string>();
            return trimmed.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
        }
    }
}
