using Sbroenne.ExcelMcp.ComInterop;
using Sbroenne.ExcelMcp.ComInterop.Session;
using Sbroenne.ExcelMcp.Core.Models;

namespace Sbroenne.ExcelMcp.Core.Commands.Chart;

/// <summary>
/// Result of finding a chart by name.
/// </summary>
internal sealed class ChartFindResult
{
    public dynamic? Chart;
    public dynamic? Shape;
    public string SheetName = string.Empty;
}

/// <summary>
/// Chart data source operations - set range, add/remove series.
/// </summary>
public partial class ChartCommands
{
    /// <inheritdoc />
    public OperationResult SetSourceRange(IExcelBatch batch, string chartName, string sourceRange)
    {
        return batch.Execute((ctx, ct) =>
        {
            // Find chart by name
            var findResult = FindChart(ctx.Book, chartName);
            if (findResult.Chart == null)
            {
                throw new InvalidOperationException($"Chart '{chartName}' not found in workbook.");
            }

            try
            {
                // Determine strategy and delegate
                IChartStrategy strategy = _pivotStrategy.CanHandle(findResult.Chart) ? _pivotStrategy : _regularStrategy;
#pragma warning disable CS8604 // CodeQL false positive: Both strategies implement IChartStrategy.SetSourceRange with dynamic parameter
                strategy.SetSourceRange(findResult.Chart, sourceRange);
#pragma warning restore CS8604

                return new OperationResult { Success = true, FilePath = batch.WorkbookPath }; // Void operation completed
            }
            finally
            {
                if (findResult.Shape != null) ComUtilities.Release(ref findResult.Shape!);
                if (findResult.Chart != null) ComUtilities.Release(ref findResult.Chart!);
            }
        });
    }

    /// <inheritdoc />
    public SeriesInfo AddSeries(
        IExcelBatch batch,
        string chartName,
        string seriesName,
        string valuesRange,
        string? categoryRange = null)
    {
        return batch.Execute((ctx, ct) =>
        {
            // Find chart by name
            var findResult = FindChart(ctx.Book, chartName);
            if (findResult.Chart == null)
            {
                throw new InvalidOperationException($"Chart '{chartName}' not found in workbook.");
            }

            try
            {
                // Determine strategy and delegate
                IChartStrategy strategy = _pivotStrategy.CanHandle(findResult.Chart) ? _pivotStrategy : _regularStrategy;
#pragma warning disable CS8604 // CodeQL false positive: Both strategies implement IChartStrategy.AddSeries with dynamic parameter
                var result = strategy.AddSeries(findResult.Chart, seriesName, valuesRange, categoryRange);
#pragma warning restore CS8604

                return result;
            }
            finally
            {
                if (findResult.Shape != null) ComUtilities.Release(ref findResult.Shape!);
                if (findResult.Chart != null) ComUtilities.Release(ref findResult.Chart!);
            }
        });
    }

    /// <inheritdoc />
    public OperationResult RemoveSeries(IExcelBatch batch, string chartName, int seriesIndex)
    {
        return batch.Execute((ctx, ct) =>
        {
            // Find chart by name
            var findResult = FindChart(ctx.Book, chartName);
            if (findResult.Chart == null)
            {
                throw new InvalidOperationException($"Chart '{chartName}' not found in workbook.");
            }

            try
            {
                // Determine strategy and delegate
                IChartStrategy strategy = _pivotStrategy.CanHandle(findResult.Chart) ? _pivotStrategy : _regularStrategy;
#pragma warning disable CS8604 // CodeQL false positive: Both strategies implement IChartStrategy.RemoveSeries with dynamic parameter
                strategy.RemoveSeries(findResult.Chart, seriesIndex);
#pragma warning restore CS8604

                return new OperationResult { Success = true, FilePath = batch.WorkbookPath }; // Void operation completed
            }
            finally
            {
                if (findResult.Shape != null) ComUtilities.Release(ref findResult.Shape!);
                if (findResult.Chart != null) ComUtilities.Release(ref findResult.Chart!);
            }
        });
    }

    /// <summary>
    /// Finds a chart by name across all worksheets.
    /// Returns result with chart, shape, sheetName properties. Caller must release chart and shape.
    /// </summary>
    private static ChartFindResult FindChart(dynamic workbook, string chartName)
    {
        dynamic worksheets = workbook.Worksheets;
        int wsCount = Convert.ToInt32(worksheets.Count);

        for (int i = 1; i <= wsCount; i++)
        {
            dynamic? worksheet = null;
            dynamic? shapes = null;

            try
            {
                worksheet = worksheets.Item(i);
                string sheetName = worksheet.Name?.ToString() ?? $"Sheet{i}";
                shapes = worksheet.Shapes;
                int shapeCount = Convert.ToInt32(shapes.Count);

                for (int j = 1; j <= shapeCount; j++)
                {
                    dynamic? shape = null;
                    dynamic? chart = null;

                    try
                    {
                        shape = shapes.Item(j);

                        // Check if this is a chart (msoChart = 3)
                        if (Convert.ToInt32(shape.Type) != 3)
                        {
                            ComUtilities.Release(ref shape!);
                            continue;
                        }

                        string shapeName = shape.Name?.ToString() ?? string.Empty;
                        if (!shapeName.Equals(chartName, StringComparison.OrdinalIgnoreCase))
                        {
                            ComUtilities.Release(ref shape!);
                            continue;
                        }

                        // Found it!
                        chart = shape.Chart;
                        ComUtilities.Release(ref shapes!);
                        ComUtilities.Release(ref worksheet!);
                        ComUtilities.Release(ref worksheets!);

                        return new ChartFindResult { Chart = chart, Shape = shape, SheetName = sheetName }; // Caller must release both
                    }
                    catch (System.Runtime.InteropServices.COMException)
                    {
                        ComUtilities.Release(ref chart!);
                        ComUtilities.Release(ref shape!);
                        throw;
                    }
                }
            }
            finally
            {
                ComUtilities.Release(ref shapes!);
                ComUtilities.Release(ref worksheet!);
            }
        }

        ComUtilities.Release(ref worksheets!);
        return new ChartFindResult { Chart = null, Shape = null, SheetName = string.Empty };
    }
}


