using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;

internal static class Program
{
    private static string _assemblyDirectory = string.Empty;

    private static int Main(string[] args)
    {
        if (args.Length < 2 || !File.Exists(args[0]))
        {
            Console.Error.WriteLine("Usage: Nexamas.UI.PerformanceSmoke.exe <Nexamas.UI.dll> <report.md>");
            return 2;
        }

        string assemblyPath = Path.GetFullPath(args[0]);
        string reportPath = Path.GetFullPath(args[1]);
        _assemblyDirectory = Path.GetDirectoryName(assemblyPath) ?? string.Empty;
        AppDomain.CurrentDomain.AssemblyResolve += ResolveFromNexamasUIDirectory;

        try
        {
            Directory.CreateDirectory(Path.GetDirectoryName(reportPath) ?? ".");

            Stopwatch totalWatch = Stopwatch.StartNew();
            Assembly assembly = Assembly.LoadFrom(assemblyPath);
            string assemblyName = assembly.GetName().Name ?? string.Empty;
            if (!string.Equals(assemblyName, "Nexamas.UI", StringComparison.Ordinal))
            {
                throw new InvalidOperationException("Unexpected assembly loaded for performance smoke: " + assemblyName);
            }

            Stopwatch rows1kWatch = Stopwatch.StartNew();
            int visible1k = NewDataView(assembly, 1000);
            rows1kWatch.Stop();

            Stopwatch rows10kWatch = Stopwatch.StartNew();
            int visible10k = NewDataView(assembly, 10000);
            rows10kWatch.Stop();

            Type gridType = assembly.GetType("Nexamas.UI.Components.MASDataGrid", true) ?? throw new InvalidOperationException("MASDataGrid type was not found.");
            Stopwatch gridWatch = Stopwatch.StartNew();
            object grid = Activator.CreateInstance(gridType) ?? throw new InvalidOperationException("Performance smoke failed to construct MASDataGrid.");
            GC.KeepAlive(grid);
            gridWatch.Stop();
            totalWatch.Stop();

            const double threshold1k = 2500.0;
            const double threshold10k = 15000.0;
            const double thresholdGrid = 1000.0;
            bool passed = rows1kWatch.Elapsed.TotalMilliseconds <= threshold1k &&
                          rows10kWatch.Elapsed.TotalMilliseconds <= threshold10k &&
                          gridWatch.Elapsed.TotalMilliseconds <= thresholdGrid;

            var lines = new List<string>
            {
                "# Nexamas UI Commercial Performance Smoke",
                string.Empty,
                "Generated UTC: " + DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture),
                "Assembly: `" + assemblyPath + "`",
                string.Empty,
                "Threshold result: " + (passed ? "PASS" : "FAIL"),
                string.Empty,
                "| Workload | Rows | Visible rows | Elapsed ms | Threshold ms |",
                "|---|---:|---:|---:|---:|",
                string.Format(CultureInfo.InvariantCulture, "| DataView setup/sort/filter | 1000 | {0} | {1:N2} | {2:N2} |", visible1k, rows1kWatch.Elapsed.TotalMilliseconds, threshold1k),
                string.Format(CultureInfo.InvariantCulture, "| DataView setup/sort/filter | 10000 | {0} | {1:N2} | {2:N2} |", visible10k, rows10kWatch.Elapsed.TotalMilliseconds, threshold10k),
                string.Format(CultureInfo.InvariantCulture, "| DataGrid construction | 1 control | 1 | {0:N2} | {1:N2} |", gridWatch.Elapsed.TotalMilliseconds, thresholdGrid),
                string.Empty,
                string.Format(CultureInfo.InvariantCulture, "Total elapsed ms: {0:N2}", totalWatch.Elapsed.TotalMilliseconds)
            };
            File.WriteAllLines(reportPath, lines);

            Console.WriteLine("Performance smoke report: " + reportPath);
            Console.WriteLine("Threshold result: " + (passed ? "PASS" : "FAIL"));
            return passed ? 0 : 1;
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine(ex.ToString());
            return 1;
        }
    }

    private static Assembly ResolveFromNexamasUIDirectory(object sender, ResolveEventArgs args)
    {
        if (string.IsNullOrEmpty(_assemblyDirectory)) { return null; }
        string name = new AssemblyName(args.Name).Name ?? string.Empty;
        string candidate = Path.Combine(_assemblyDirectory, name + ".dll");
        if (File.Exists(candidate)) { return Assembly.LoadFrom(candidate); }
        return null;
    }

    private static int NewDataView(Assembly assembly, int rows)
    {
        Type viewType = assembly.GetType("Nexamas.UI.Components.MASDataView", true) ?? throw new InvalidOperationException("MASDataView type was not found.");
        Type sortDirectionType = assembly.GetType("Nexamas.UI.Components.MASDataGridSortDirection", true) ?? throw new InvalidOperationException("MASDataGridSortDirection type was not found.");
        object view = Activator.CreateInstance(viewType) ?? throw new InvalidOperationException("MASDataView could not be constructed.");

        MethodInfo addColumn = viewType.GetMethod("AddColumn", new[] { typeof(string), typeof(string) }) ?? throw new MissingMethodException(viewType.FullName, "AddColumn(string,string)");
        MethodInfo addRow = viewType.GetMethod("AddRow", new[] { typeof(object[]) }) ?? throw new MissingMethodException(viewType.FullName, "AddRow(object[])");
        MethodInfo setFilterText = viewType.GetMethod("SetFilterText", new[] { typeof(string) }) ?? throw new MissingMethodException(viewType.FullName, "SetFilterText(string)");
        MethodInfo sortByColumn = viewType.GetMethod("SortByColumn", new[] { typeof(string), sortDirectionType }) ?? throw new MissingMethodException(viewType.FullName, "SortByColumn(string,direction)");
        PropertyInfo visibleRowCount = viewType.GetProperty("VisibleRowCount") ?? throw new MissingMemberException(viewType.FullName, "VisibleRowCount");

        addColumn.Invoke(view, new object[] { "id", "ID" });
        addColumn.Invoke(view, new object[] { "name", "Name" });
        addColumn.Invoke(view, new object[] { "group", "Group" });
        addColumn.Invoke(view, new object[] { "amount", "Amount" });

        for (int i = 0; i < rows; i++)
        {
            object[] values = { i, "Item " + i.ToString(CultureInfo.InvariantCulture), "G" + (i % 10).ToString(CultureInfo.InvariantCulture), i * 3 };
            addRow.Invoke(view, new object[] { values });
        }

        object descending = Enum.Parse(sortDirectionType, "Descending");
        sortByColumn.Invoke(view, new[] { "amount", descending });
        setFilterText.Invoke(view, new object[] { "Item" });
        int visible = Convert.ToInt32(visibleRowCount.GetValue(view, null), CultureInfo.InvariantCulture);
        if (visible <= 0) { throw new InvalidOperationException("Performance smoke produced no visible rows for workload " + rows.ToString(CultureInfo.InvariantCulture) + "."); }
        return visible;
    }
}
