// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp;

namespace Microsoft.Maui.Platform.Linux.Hosting;

/// <summary>
/// Entry point for running MAUI applications on Linux.
/// </summary>
public static class LinuxProgramHost
{
    /// <summary>
    /// Runs the MAUI application on Linux.
    /// </summary>
    /// <typeparam name="TApp">The application type.</typeparam>
    /// <param name="args">Command line arguments.</param>
    public static void Run<TApp>(string[] args) where TApp : class, IApplication, new()
    {
        Run<TApp>(args, null);
    }

    /// <summary>
    /// Runs the MAUI application on Linux with additional configuration.
    /// </summary>
    /// <typeparam name="TApp">The application type.</typeparam>
    /// <param name="args">Command line arguments.</param>
    /// <param name="configure">Optional builder configuration action.</param>
    public static void Run<TApp>(string[] args, Action<MauiAppBuilder>? configure) where TApp : class, IApplication, new()
    {
        // Build the MAUI application
        var builder = MauiApp.CreateBuilder();
        builder.UseLinux();
        configure?.Invoke(builder);
        builder.UseMauiApp<TApp>();
        var mauiApp = builder.Build();

        // Get application options
        var options = mauiApp.Services.GetService<LinuxApplicationOptions>()
                     ?? new LinuxApplicationOptions();
        ParseCommandLineOptions(args, options);

        // Initialize GTK for WebView support
        GtkHostService.Instance.Initialize(options.Title ?? "MAUI Application", options.Width, options.Height);
        DiagnosticLog.Debug("LinuxProgramHost", "GTK initialized for WebView support");

        // Create Linux application
        using var linuxApp = new LinuxApplication();
        linuxApp.Initialize(options);

        // Create MAUI context
        var mauiContext = new LinuxMauiContext(mauiApp.Services, linuxApp);

        // Get the MAUI application instance
        var application = mauiApp.Services.GetService<IApplication>();

        // Ensure Application.Current is set - required for Shell.Current to work
        if (application is Application app && Application.Current == null)
        {
            // Use reflection to set Current since it has a protected setter
            var currentProperty = typeof(Application).GetProperty("Current");
            currentProperty?.SetValue(null, app);
        }

        // Try to render the application's main page
        SkiaView? rootView = null;

        if (application != null)
        {
            rootView = RenderApplication(application, mauiContext, options);
        }

        // Fallback to demo if no application view is available
        if (rootView == null)
        {
            DiagnosticLog.Warn("LinuxProgramHost", "No application page found. Showing demo UI.");
            rootView = CreateDemoView();
        }

        linuxApp.RootView = rootView;
        linuxApp.Run();
    }

    /// <summary>
    /// Renders the MAUI application and returns the root SkiaView.
    /// </summary>
    private static SkiaView? RenderApplication(IApplication application, LinuxMauiContext mauiContext, LinuxApplicationOptions options)
    {
        try
        {
            // For Applications, we need to create a window
            if (application is Application app)
            {
                Page? mainPage = app.MainPage;

                // If no MainPage set, check for windows
                if (mainPage == null && application.Windows.Count > 0)
                {
                    var existingWindow = application.Windows[0];
                    if (existingWindow.Content is Page page)
                    {
                        mainPage = page;
                    }
                }

                if (mainPage != null)
                {
                    // Create a MAUI Window and add it to the application
                    // This ensures Shell.Current works properly (it reads from Application.Current.Windows[0].Page)
                    if (app.Windows.Count == 0)
                    {
                        var mauiWindow = new Microsoft.Maui.Controls.Window(mainPage);

                        // Try OpenWindow first
                        app.OpenWindow(mauiWindow);

                        // If that didn't work, use reflection to add directly to _windows
                        if (app.Windows.Count == 0)
                        {
                            var windowsField = typeof(Application).GetField("_windows",
                                System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                            if (windowsField?.GetValue(app) is System.Collections.IList windowsList)
                            {
                                windowsList.Add(mauiWindow);
                            }
                        }
                    }

                    return RenderPage(mainPage, mauiContext);
                }
            }

            return null;
        }
        catch (Exception ex)
        {
            DiagnosticLog.Error("LinuxProgramHost", $"Error rendering application: {ex.Message}");
            DiagnosticLog.Error("LinuxProgramHost", ex.StackTrace ?? "");
            return null;
        }
    }

    /// <summary>
    /// Renders a MAUI Page to a SkiaView.
    /// </summary>
    private static SkiaView? RenderPage(Page page, LinuxMauiContext mauiContext)
    {
        var renderer = new LinuxViewRenderer(mauiContext);
        return renderer.RenderPage(page);
    }

    private static void ParseCommandLineOptions(string[] args, LinuxApplicationOptions options)
    {
        for (int i = 0; i < args.Length; i++)
        {
            switch (args[i].ToLowerInvariant())
            {
                case "--title" when i + 1 < args.Length:
                    options.Title = args[++i];
                    break;
                case "--width" when i + 1 < args.Length && int.TryParse(args[i + 1], out var w):
                    options.Width = w;
                    i++;
                    break;
                case "--height" when i + 1 < args.Length && int.TryParse(args[i + 1], out var h):
                    options.Height = h;
                    i++;
                    break;
                case "--demo":
                    // Force demo mode
                    options.ForceDemo = true;
                    break;
            }
        }
    }

    /// <summary>
    /// Creates a demo view showcasing all controls.
    /// </summary>
    public static SkiaView CreateDemoView()
    {
        // Create scrollable container
        var scroll = new SkiaScrollView();

        var root = new SkiaStackLayout
        {
            Orientation = StackOrientation.Vertical,
            Spacing = 15,
            BackgroundColor = Color.FromRgb(0xF5, 0xF5, 0xF5)
        };
        root.Padding = new Thickness(20, 20, 20, 20);

        // ========== TITLE ==========
        root.AddChild(new SkiaLabel
        {
            Text = "OpenMaui Linux Control Demo",
            FontSize = 28,
            TextColor = Color.FromRgb(0x1A, 0x23, 0x7E),
            FontAttributes = FontAttributes.Bold
        });
        root.AddChild(new SkiaLabel
        {
            Text = "All controls rendered using SkiaSharp on X11",
            FontSize = 14,
            TextColor = Colors.Gray
        });

        // ========== LABELS SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Labels"));
        var labelSection = new SkiaStackLayout { Orientation = StackOrientation.Vertical, Spacing = 5 };
        labelSection.AddChild(new SkiaLabel { Text = "Normal Label", FontSize = 16, TextColor = Colors.Black });
        labelSection.AddChild(new SkiaLabel { Text = "Bold Label", FontSize = 16, TextColor = Colors.Black, FontAttributes = FontAttributes.Bold });
        labelSection.AddChild(new SkiaLabel { Text = "Italic Label", FontSize = 16, TextColor = Colors.Gray, FontAttributes = FontAttributes.Italic });
        labelSection.AddChild(new SkiaLabel { Text = "Colored Label (Pink)", FontSize = 16, TextColor = Color.FromRgb(0xE9, 0x1E, 0x63) });
        root.AddChild(labelSection);

        // ========== BUTTONS SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Buttons"));
        var buttonSection = new SkiaStackLayout { Orientation = StackOrientation.Horizontal, Spacing = 10 };

        var btnPrimary = new SkiaButton { Text = "Primary", FontSize = 14 };
        btnPrimary.BackgroundColor = Color.FromRgb(0x21, 0x96, 0xF3);
        btnPrimary.TextColor = Colors.White;
        var clickCount = 0;
        btnPrimary.Clicked += (s, e) => { clickCount++; btnPrimary.Text = $"Clicked {clickCount}x"; };
        buttonSection.AddChild(btnPrimary);

        var btnSuccess = new SkiaButton { Text = "Success", FontSize = 14 };
        btnSuccess.BackgroundColor = Color.FromRgb(0x4C, 0xAF, 0x50);
        btnSuccess.TextColor = Colors.White;
        buttonSection.AddChild(btnSuccess);

        var btnDanger = new SkiaButton { Text = "Danger", FontSize = 14 };
        btnDanger.BackgroundColor = Color.FromRgb(0xF4, 0x43, 0x36);
        btnDanger.TextColor = Colors.White;
        buttonSection.AddChild(btnDanger);

        root.AddChild(buttonSection);

        // ========== ENTRY SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Text Entry"));
        var entry = new SkiaEntry { Placeholder = "Type here...", FontSize = 14 };
        root.AddChild(entry);

        // ========== SEARCHBAR SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("SearchBar"));
        var searchBar = new SkiaSearchBar { Placeholder = "Search for items..." };
        var searchResultLabel = new SkiaLabel { Text = "", FontSize = 12, TextColor = Colors.Gray };
        searchBar.TextChanged += (s, e) => searchResultLabel.Text = $"Searching: {e.NewTextValue}";
        searchBar.SearchButtonPressed += (s, e) => searchResultLabel.Text = $"Search submitted: {searchBar.Text}";
        root.AddChild(searchBar);
        root.AddChild(searchResultLabel);

        // ========== EDITOR SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Editor (Multi-line)"));
        var editor = new SkiaEditor
        {
            Placeholder = "Enter multiple lines of text...",
            FontSize = 14,
            BackgroundColor = Colors.White
        };
        root.AddChild(editor);

        // ========== CHECKBOX SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("CheckBox"));
        var checkSection = new SkiaStackLayout { Orientation = StackOrientation.Horizontal, Spacing = 20 };
        var cb1 = new SkiaCheckBox { IsChecked = true };
        checkSection.AddChild(cb1);
        checkSection.AddChild(new SkiaLabel { Text = "Checked", FontSize = 14 });
        var cb2 = new SkiaCheckBox { IsChecked = false };
        checkSection.AddChild(cb2);
        checkSection.AddChild(new SkiaLabel { Text = "Unchecked", FontSize = 14 });
        root.AddChild(checkSection);

        // ========== SWITCH SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Switch"));
        var switchSection = new SkiaStackLayout { Orientation = StackOrientation.Horizontal, Spacing = 20 };
        var sw1 = new SkiaSwitch { IsOn = true };
        switchSection.AddChild(sw1);
        switchSection.AddChild(new SkiaLabel { Text = "On", FontSize = 14 });
        var sw2 = new SkiaSwitch { IsOn = false };
        switchSection.AddChild(sw2);
        switchSection.AddChild(new SkiaLabel { Text = "Off", FontSize = 14 });
        root.AddChild(switchSection);

        // ========== RADIOBUTTON SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("RadioButton"));
        var radioSection = new SkiaStackLayout { Orientation = StackOrientation.Horizontal, Spacing = 15 };
        radioSection.AddChild(new SkiaRadioButton { Content = "Option A", IsChecked = true, GroupName = "demo" });
        radioSection.AddChild(new SkiaRadioButton { Content = "Option B", IsChecked = false, GroupName = "demo" });
        radioSection.AddChild(new SkiaRadioButton { Content = "Option C", IsChecked = false, GroupName = "demo" });
        root.AddChild(radioSection);

        // ========== SLIDER SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Slider"));
        var sliderLabel = new SkiaLabel { Text = "Value: 50", FontSize = 14 };
        var slider = new SkiaSlider { Minimum = 0, Maximum = 100, Value = 50 };
        slider.ValueChanged += (s, e) => sliderLabel.Text = $"Value: {(int)slider.Value}";
        root.AddChild(slider);
        root.AddChild(sliderLabel);

        // ========== STEPPER SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Stepper"));
        var stepperSection = new SkiaStackLayout { Orientation = StackOrientation.Horizontal, Spacing = 10 };
        var stepperLabel = new SkiaLabel { Text = "Value: 5", FontSize = 14 };
        var stepper = new SkiaStepper { Value = 5, Minimum = 0, Maximum = 10, Increment = 1 };
        stepper.ValueChanged += (s, e) => stepperLabel.Text = $"Value: {(int)stepper.Value}";
        stepperSection.AddChild(stepper);
        stepperSection.AddChild(stepperLabel);
        root.AddChild(stepperSection);

        // ========== PROGRESSBAR SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("ProgressBar"));
        var progress = new SkiaProgressBar { Progress = 0.7f };
        root.AddChild(progress);
        root.AddChild(new SkiaLabel { Text = "70% Complete", FontSize = 12, TextColor = Colors.Gray });

        // ========== ACTIVITYINDICATOR SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("ActivityIndicator"));
        var activitySection = new SkiaStackLayout { Orientation = StackOrientation.Horizontal, Spacing = 10 };
        var activity = new SkiaActivityIndicator { IsRunning = true };
        activitySection.AddChild(activity);
        activitySection.AddChild(new SkiaLabel { Text = "Loading...", FontSize = 14, TextColor = Colors.Gray });
        root.AddChild(activitySection);

        // ========== PICKER SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Picker (Dropdown)"));
        var picker = new SkiaPicker { Title = "Select an item" };
        picker.SetItems(new[] { "Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape" });
        var pickerLabel = new SkiaLabel { Text = "Selected: (none)", FontSize = 12, TextColor = Colors.Gray };
        picker.SelectedIndexChanged += (s, e) => pickerLabel.Text = $"Selected: {picker.SelectedItem}";
        root.AddChild(picker);
        root.AddChild(pickerLabel);

        // ========== DATEPICKER SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("DatePicker"));
        var datePicker = new SkiaDatePicker { Date = DateTime.Today };
        var dateLabel = new SkiaLabel { Text = $"Date: {DateTime.Today:d}", FontSize = 12, TextColor = Colors.Gray };
        datePicker.DateSelected += (s, e) => dateLabel.Text = $"Date: {datePicker.Date:d}";
        root.AddChild(datePicker);
        root.AddChild(dateLabel);

        // ========== TIMEPICKER SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("TimePicker"));
        var timePicker = new SkiaTimePicker();
        var timeLabel = new SkiaLabel { Text = $"Time: {DateTime.Now:t}", FontSize = 12, TextColor = Colors.Gray };
        timePicker.TimeSelected += (s, e) => timeLabel.Text = $"Time: {DateTime.Today.Add(timePicker.Time):t}";
        root.AddChild(timePicker);
        root.AddChild(timeLabel);

        // ========== BORDER SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Border"));
        var border = new SkiaBorder
        {
            CornerRadius = 8,
            StrokeThickness = 2,
            Stroke = Color.FromRgb(0x21, 0x96, 0xF3),
            BackgroundColor = Color.FromRgb(0xE3, 0xF2, 0xFD)
        };
        border.SetPadding(15);
        border.AddChild(new SkiaLabel { Text = "Content inside a styled Border", FontSize = 14, TextColor = Color.FromRgb(0x1A, 0x23, 0x7E) });
        root.AddChild(border);

        // ========== FRAME SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Frame (with shadow)"));
        var frame = new SkiaFrame();
        frame.BackgroundColor = Colors.White;
        frame.AddChild(new SkiaLabel { Text = "Content inside a Frame with shadow effect", FontSize = 14 });
        root.AddChild(frame);

        // ========== COLLECTIONVIEW SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("CollectionView (List)"));
        var collectionView = new SkiaCollectionView
        {
            SelectionMode = SkiaSelectionMode.Single,
            Header = "Fruits",
            Footer = "End of list"
        };
        collectionView.ItemsSource =(new object[] { "Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape", "Honeydew" });
        var collectionLabel = new SkiaLabel { Text = "Selected: (none)", FontSize = 12, TextColor = Colors.Gray };
        collectionView.SelectionChanged += (s, e) =>
        {
            var selected = e.CurrentSelection.FirstOrDefault();
            collectionLabel.Text = $"Selected: {selected}";
        };
        root.AddChild(collectionView);
        root.AddChild(collectionLabel);

        // ========== IMAGEBUTTON SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("ImageButton"));
        var imageButtonSection = new SkiaStackLayout { Orientation = StackOrientation.Horizontal, Spacing = 10 };

        // Create ImageButton with a generated icon (since we don't have image files)
        var imgBtn = new SkiaImageButton
        {
            CornerRadius = 8,
            StrokeColor = Color.FromRgb(0x21, 0x96, 0xF3),
            StrokeThickness = 1,
            ImageBackgroundColor = Color.FromRgb(0xE3, 0xF2, 0xFD),
            Padding = new Thickness(10)
        };
        // Generate a simple star icon bitmap
        var iconBitmap = CreateStarIcon(32, new SKColor(0x21, 0x96, 0xF3));
        imgBtn.Bitmap = iconBitmap;
        var imgBtnLabel = new SkiaLabel { Text = "Click the star!", FontSize = 12, TextColor = Colors.Gray };
        imgBtn.Clicked += (s, e) => imgBtnLabel.Text = "Star clicked!";
        imageButtonSection.AddChild(imgBtn);
        imageButtonSection.AddChild(imgBtnLabel);
        root.AddChild(imageButtonSection);

        // ========== IMAGE SECTION ==========
        root.AddChild(CreateSeparator());
        root.AddChild(CreateSectionHeader("Image"));
        var imageSection = new SkiaStackLayout { Orientation = StackOrientation.Horizontal, Spacing = 10 };

        // Create Image with a generated sample image
        var img = new SkiaImage();
        var sampleBitmap = CreateSampleImage(80, 60);
        img.Bitmap = sampleBitmap;
        imageSection.AddChild(img);
        imageSection.AddChild(new SkiaLabel { Text = "Sample generated image", FontSize = 12, TextColor = Colors.Gray });
        root.AddChild(imageSection);

        // ========== FOOTER ==========
        root.AddChild(CreateSeparator());
        root.AddChild(new SkiaLabel
        {
            Text = "All 25+ controls are interactive - try them all!",
            FontSize = 16,
            TextColor = Color.FromRgb(0x4C, 0xAF, 0x50),
            FontAttributes = FontAttributes.Bold
        });
        root.AddChild(new SkiaLabel
        {
            Text = "Scroll down to see more controls",
            FontSize = 12,
            TextColor = Colors.Gray
        });

        scroll.Content = root;
        return scroll;
    }

    private static SkiaLabel CreateSectionHeader(string text)
    {
        return new SkiaLabel
        {
            Text = text,
            FontSize = 18,
            TextColor = Color.FromRgb(0x37, 0x47, 0x4F),
            FontAttributes = FontAttributes.Bold
        };
    }

    private static SkiaView CreateSeparator()
    {
        var sep = new SkiaLabel { Text = "", BackgroundColor = Color.FromRgb(0xE0, 0xE0, 0xE0), RequestedHeight = 1 };
        return sep;
    }

    private static SKBitmap CreateStarIcon(int size, SKColor color)
    {
        var bitmap = new SKBitmap(size, size);
        using var canvas = new SKCanvas(bitmap);
        canvas.Clear(SKColors.Transparent);

        using var paint = new SKPaint
        {
            Color = color,
            Style = SKPaintStyle.Fill,
            IsAntialias = true
        };

        // Draw a 5-point star
        using var path = new SKPath();
        var cx = size / 2f;
        var cy = size / 2f;
        var outerRadius = size / 2f - 2;
        var innerRadius = outerRadius * 0.4f;

        for (int i = 0; i < 5; i++)
        {
            var outerAngle = (i * 72 - 90) * Math.PI / 180;
            var innerAngle = ((i * 72) + 36 - 90) * Math.PI / 180;

            var ox = cx + outerRadius * (float)Math.Cos(outerAngle);
            var oy = cy + outerRadius * (float)Math.Sin(outerAngle);
            var ix = cx + innerRadius * (float)Math.Cos(innerAngle);
            var iy = cy + innerRadius * (float)Math.Sin(innerAngle);

            if (i == 0)
                path.MoveTo(ox, oy);
            else
                path.LineTo(ox, oy);

            path.LineTo(ix, iy);
        }
        path.Close();
        canvas.DrawPath(path, paint);

        return bitmap;
    }

    private static SKBitmap CreateSampleImage(int width, int height)
    {
        var bitmap = new SKBitmap(width, height);
        using var canvas = new SKCanvas(bitmap);

        // Draw gradient background
        using var bgPaint = new SKPaint();
        using var shader = SKShader.CreateLinearGradient(
            new SKPoint(0, 0),
            new SKPoint(width, height),
            new SKColor[] { new SKColor(0x42, 0xA5, 0xF5), new SKColor(0x7E, 0x57, 0xC2) },
            new float[] { 0, 1 },
            SKShaderTileMode.Clamp);
        bgPaint.Shader = shader;
        canvas.DrawRect(0, 0, width, height, bgPaint);

        // Draw some shapes
        using var shapePaint = new SKPaint
        {
            Color = SKColors.White.WithAlpha(180),
            Style = SKPaintStyle.Fill,
            IsAntialias = true
        };
        canvas.DrawCircle(width * 0.3f, height * 0.4f, 15, shapePaint);
        canvas.DrawRect(width * 0.5f, height * 0.3f, 20, 20, shapePaint);

        // Draw "IMG" text
        using var font = new SKFont(SKTypeface.Default, 12);
        using var textPaint = new SKPaint(font)
        {
            Color = SKColors.White,
            IsAntialias = true
        };
        canvas.DrawText("IMG", 10, height - 8, textPaint);

        return bitmap;
    }
}
