using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using MarcusLaw.OutlookAddin.Core.Models;
using Serilog;
using Outlook = Microsoft.Office.Interop.Outlook;

namespace OutlookAddin.Services
{
    /// <summary>
    /// One-shot, STA-bound traversal of the user's mailbox tree. Emits a
    /// flat list of <see cref="FolderTreeNode"/> values that the Settings
    /// dialog can data-bind without keeping any Outlook COM references alive.
    /// </summary>
    public static class FolderTreeBuilder
    {
        private const int MaxDepth = 4;
        private const int MaxFolders = 500;

        public static IReadOnlyList<FolderTreeNode> BuildFlatList(Outlook.Application app, ILogger logger)
        {
            var result = new List<FolderTreeNode>();
            Outlook.NameSpace? ns = null;
            Outlook.Stores? stores = null;
            try
            {
                ns = app.Session;
                stores = ns.Stores;
                for (int i = 1; i <= stores.Count; i++)
                {
                    var store = stores[i];
                    try
                    {
                        var storeId = store.StoreID;
                        var storeName = store.DisplayName ?? "(default)";
                        Outlook.Folder? root = null;
                        try
                        {
                            root = (Outlook.Folder)store.GetRootFolder();
                            VisitChildren(root, storeId, storeName, "", 0, result, logger);
                        }
                        finally
                        {
                            if (root != null && Marshal.IsComObject(root)) Marshal.ReleaseComObject(root);
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.Warning(ex, "FolderTreeBuilder: skipping store at index {Index}", i);
                    }
                    finally
                    {
                        if (Marshal.IsComObject(store)) Marshal.ReleaseComObject(store);
                    }
                    if (result.Count >= MaxFolders) break;
                }
            }
            catch (Exception ex)
            {
                logger.Warning(ex, "FolderTreeBuilder.BuildFlatList failed");
            }
            finally
            {
                if (stores != null && Marshal.IsComObject(stores)) Marshal.ReleaseComObject(stores);
                if (ns != null && Marshal.IsComObject(ns)) Marshal.ReleaseComObject(ns);
            }
            return result;
        }

        private static void VisitChildren(
            Outlook.Folder parent,
            string storeId,
            string storeName,
            string parentPath,
            int depth,
            List<FolderTreeNode> sink,
            ILogger logger)
        {
            if (depth >= MaxDepth || sink.Count >= MaxFolders) return;

            Outlook.Folders? children = null;
            try
            {
                children = parent.Folders;
                for (int i = 1; i <= children.Count; i++)
                {
                    var child = (Outlook.Folder)children[i];
                    try
                    {
                        var name = child.Name ?? string.Empty;
                        if (string.IsNullOrWhiteSpace(name)) continue;
                        var path = parentPath + "\\" + name;
                        sink.Add(new FolderTreeNode
                        {
                            StoreId = storeId,
                            StoreDisplayName = storeName,
                            FolderPath = path,
                            DisplayName = name,
                            Depth = depth
                        });
                        VisitChildren(child, storeId, storeName, path, depth + 1, sink, logger);
                    }
                    catch (Exception ex)
                    {
                        logger.Warning(ex, "FolderTreeBuilder: skipping a folder at depth {Depth}", depth);
                    }
                    finally
                    {
                        if (Marshal.IsComObject(child)) Marshal.ReleaseComObject(child);
                    }
                    if (sink.Count >= MaxFolders) break;
                }
            }
            finally
            {
                if (children != null && Marshal.IsComObject(children)) Marshal.ReleaseComObject(children);
            }
        }
    }
}
