using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using CarManagerV3.Forms;
using CarManagerV3.Manager;
using CarManagerV3.Util;

namespace CarManagerV3
{
    public partial class MainForm : Form
    {
        List<Car> cars = new List<Car>();
        string filepath = "";

        public static MainForm GetMainForm()
        {
            // This is a singleton pattern to ensure only one instance of MainForm exists.
            // If you need to access the MainForm instance, you can use this method.
            return Application.OpenForms.OfType<MainForm>().FirstOrDefault() ?? new MainForm();
        }

        public MainForm(string pathToOpen = "")
        {
            InitializeComponent();
            if (Properties.Settings.Default.DataLocation == "")
            {
                Properties.Settings.Default.DataLocation = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\CarManagerV3";
                Properties.Settings.Default.Save();
            }

            // Open the most recent file if it exists. Otherwise, use default filepath.
            List<string> recentFiles = SafeManager.GetRecentPaths();
            if (!String.IsNullOrEmpty(pathToOpen))
            {
                filepath = pathToOpen;
                SafeManager.AddRecentPath(filepath);
            }
            else if (recentFiles.Count > 0)
            {
                filepath = recentFiles[0];
            }
            else
            {
                openWelcomeScreen();
            }

            SafeManager.InitializeFile(filepath);
            StateManager.setFilePath(filepath);
            try
            {
                List<Car> _cars = SafeManager.ReadCars(filepath);
                cars = _cars;
                refreshCars(_cars, false);
            }
            catch (LegacyException)
            {
                Console.Error.WriteLine("Legacy file format detected. Prompting user to select a new file.");
                showOpenFileDialog();
            }

            refreshRecents();
            try
            {
                if (Updater.IsUpdateAvailable(Properties.Settings.Default.AllowPrerelease))
                {
                    if (InstallModeDetector.IsInstalledViaMsi())
                    {
                        UpdatePromptForm updatePrompt = new UpdatePromptForm(Updater.GetCurrentVersion(true), Updater.GetLatestVersion(Properties.Settings.Default.AllowPrerelease));
                        updatePrompt.ShowDialog();
                    } else
                    {
                        DialogResult updateResult = MessageBox.Show($"A new version of Car Manager is available! \nYour Version: {Updater.GetCurrentVersion(true)} \nLatest Version: {Updater.GetLatestVersion(Properties.Settings.Default.AllowPrerelease)} \n\nDo you want to open the Git repository to download the update?", "Update Available", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
                        if (updateResult == DialogResult.Yes)
                        {
                            Updater.OpenReleasePage(Updater.GetLatestVersion(Properties.Settings.Default.AllowPrerelease));
                        }
                    }
                    
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Error checking for updates: " + ex.Message);
            }

            CarCompletions.UpdateCarCompletionDataAsync();


        }

        public void openWelcomeScreen()
        {
            Welcome welcome = new Welcome();
            // disable main form while welcome screen is open
            this.Enabled = false;
            welcome.OpenFileCallback = () =>
            {
                showOpenFileDialog();
                if (filepath != "")
                {
                    welcome.Close();
                    this.Enabled = true;
                }
            };
            welcome.NewFileCallback = () =>
            {
                showSaveAsDialog();
                if (filepath != "")
                {
                    welcome.Close();
                    this.Enabled = true;
                }
            };

            welcome.ShowDialog();

        }

        public void showOpenFileDialog()
        {
            openToolStripMenuItem.PerformClick();
        }

        public void showSaveAsDialog()
        {
            saveAsToolStripMenuItem.PerformClick();
        }


        /// <summary>
        /// Refreshes the cars displayed in the flow layout panel.
        /// </summary>
        /// <param name="_cars">The cars.</param>
        /// <param name="updateGlobal">if set to <c>true</c> [update global].</param>
        private async void refreshCars(List<Car> _cars, bool updateGlobal = true, bool force = false)
        {

            this.Text = "Car Manager - " + System.IO.Path.GetFileName(filepath);

            // Sort by Car.Order. If equal, sort by ID
            _cars = _cars.Count > 0 ? _cars.OrderBy(c => c.Order).ToList() : _cars;

            if (updateGlobal)
            {
                cars = _cars;
            }

            foreach (Car car in _cars)
            {
                // not in list? add it
                bool isNew = flpCars.Controls.OfType<CarCard>().All(c => c.Car.Id != car.Id);
                // existing but changed? update it
                CarCard card = new CarCard();
                if (!isNew)
                {
                    CarCard existing = flpCars.Controls.OfType<CarCard>().First(c => c.Car.Id == car.Id);
                    Car existingCar = existing.Car;
                    if (existing == null)
                    {
                        Console.Error.WriteLine($"[L] Error: Existing car card not found for car ID: {car.Id}");
                        continue;
                    }
                    // compare details
                    // Console.WriteLine($"[L] Checking car: {car.Id} | Car Color: {car.Color} | Ex Color: {existingCar.Color}");
                    if (existingCar.IsChanged(car) || force)
                    {
                        Console.WriteLine($"[L] Updating car: {car.Id}");
                        // changes
                        card = existing;
                        if (force) card.LoadImage(); // reload image if forced refresh
                    }
                    else
                    {
                        // no changes
                        // Console.WriteLine($"[L] No changes for car: {car.Id}");
                        flpCars.Controls.SetChildIndex(existing, _cars.IndexOf(car));
                        continue;
                    }
                }

                card.CarName = $"{car.Make} {car.Model}";
                card.CarDetails = $"({car.Order}) {car.Year}, {car.Mileage} km, ${car.Price}";
                card.Car = car.Clone();
                card.LoadImage();
                // clear existing event handlers to prevent multiple subscriptions
                card.ClearCardClickedHandlers();
                card.CardClicked += (s, e) =>
                {
                    Console.WriteLine($"Card clicked: {car.Id}");
                    CarDetailsForm detailsForm = new CarDetailsForm(car);
                    detailsForm.FormClosed += async (s2, e2) =>
                    {
                        Console.WriteLine("Car details form closed.");
                        // refresh cars
                        Console.WriteLine("Refreshing cars...");
                        List<Car> __cars = await Task.Run(() => SafeManager.ReadCars(filepath));
                        if (tbxSearch.Text.Length > 0)
                        {
                            Console.WriteLine("Search box has text, applying search filter.");
                            cars = __cars;
                            searchList(tbxSearch.Text);
                            return;
                        }
                        refreshCars(__cars);

                    };
                    detailsForm.ShowDialog();
                };

                ContextMenuStrip cms = new ContextMenuStrip();
                cms.Items.Add("Move up", null, (s, e) =>
                {
                    int order = car.Order;
                    // find car with order just less than this one
                    Car other = cars.Where(c => c.Order < order).OrderByDescending(c => c.Order).FirstOrDefault();
                    if (other != null)
                    {
                        System.Diagnostics.Debug.WriteLine($"Swapping order of {car.ToString()} ({car.Order}) and {other.ToString()} ({other.Order})");
                        int temp = car.Order;
                        car.Order = other.Order;
                        other.Order = temp;
                        cars = StateManager.normalizeOrders(cars);
                        SafeManager.SaveCars(filepath, cars);
                        refreshCars(cars);
                    }
                });

                cms.Items.Add("Move down", null, (s, e) =>
                {
                    int order = car.Order;
                    // find car with order just greater than this one
                    Car other = cars.Where(c => c.Order > order).OrderBy(c => c.Order).FirstOrDefault();
                    if (other != null)
                    {
                        System.Diagnostics.Debug.WriteLine($"Swapping order of {car.ToString()} ({car.Order}) and {other.ToString()} ({other.Order})");
                        int temp = car.Order;
                        car.Order = other.Order;
                        other.Order = temp;
                        cars = StateManager.normalizeOrders(cars);
                        SafeManager.SaveCars(filepath, cars);
                        refreshCars(cars);
                    }
                });

                card.ContextMenuStrip = cms;

                if (isNew)
                {
                    flpCars.Controls.Add(card);
                }
                flpCars.Controls.SetChildIndex(card, _cars.IndexOf(car));
            }

            // Remove cards that are no longer in _cars
            var carIds = _cars.Select(c => c.Id).ToList();
            var cardsToRemove = flpCars.Controls.OfType<CarCard>().Where(c => !carIds.Contains(c.Car.Id)).ToList();
            foreach (var card in cardsToRemove)
            {
                flpCars.Controls.Remove(card);
            }

            flpCars.Refresh();
            flpCars.Invalidate();
            flpCars.Update();
        }


        private void btnNewCar_Click(object sender, EventArgs e)
        {
            Car foocar = new Car("0", "New", "Car", 2020, "White", 0, 20000);
            CarDetailsForm detailsForm = new CarDetailsForm(foocar);
            detailsForm.FormClosed += (s2, e2) =>
            {
                // refresh cars
                Console.WriteLine("Refreshing cars...");
                List<Car> cars_ = SafeManager.ReadCars(filepath);
                refreshCars(cars_, false);
            };
            detailsForm.ShowDialog();
        }

        /// <summary>
        /// Filters the cars by a search query.
        /// </summary>
        /// <param name="query">The search query.</param>
        /// <returns>
        /// A list of <see cref="Car"/> objects that match the query.
        /// </returns>
        List<Car> filterCarsByQuery(string query)
        {
            List<Car> results = new List<Car>();
            foreach (Car car in cars)
            {
                if (car.Make.ToLower().Contains(query.ToLower()) || car.Model.ToLower().Contains(query.ToLower()) || car.Year.ToString().Contains(query) || car.Mileage.ToString().Contains(query) || car.Price.ToString().Contains(query))
                {
                    results.Add(car);
                }
            }
            return results;
        }

        /// <summary>
        /// Searches the list of cars by a query and refreshes the display.
        /// </summary>
        /// <param name="query">The query.</param>
        void searchList(string query)
        {
            List<Car> results = filterCarsByQuery(query);
            refreshCars(results, false);
        }

        private async void tbxSearch_TextChanged(object sender, EventArgs e)
        {
            string query = tbxSearch.Text;
            await Task.Delay(100); // debounce
            if (query != tbxSearch.Text) return; // text changed during delay
            //flpCars.Controls.Clear();
            if (string.IsNullOrWhiteSpace(query))
            {
                refreshCars(cars);
            }
            else
            {
                searchList(query);
            }
        }

        private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
        {

        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlgOpen = new OpenFileDialog();
            dlgOpen.Filter = "Compatible Files (*.csv;*.cars)|*.csv;*.cars|CSV Files (*.csv)|*.csv|Car Manager CSV (*.cars)|*.cars|All Files (*.*)|*.*";
            dlgOpen.Title = "Open Car Data File";
            // Default to users documents
            dlgOpen.InitialDirectory = SafeManager.getRecentFolder();

            DialogResult result = dlgOpen.ShowDialog();
            if (result == DialogResult.OK)
            {
                try
                {
                    List<Car> importedCars = SafeManager.ReadCars(dlgOpen.FileName);
                    if (importedCars.Count == 0)
                    {
                        throw new Exception("File doesn't contain valid Cars.");
                    }
                    filepath = dlgOpen.FileName;

                    cars = importedCars;
                    StateManager.setFilePath(filepath);
                    // Refresh display
                    refreshCars(cars);
                    MessageBox.Show("File loaded successfully.", "Load File", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    SafeManager.AddRecentPath(filepath);
                    refreshRecents();
                }
                catch (LegacyException)
                {
                    showOpenFileDialog();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error loading file: " + ex.Message);
                }
            }
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            refreshCars(cars);
            SafeManager.SaveCars(filepath, cars);
            MessageBox.Show("File saved successfully.", "Save File", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dlgSave = new SaveFileDialog();
            dlgSave.Filter = "Compatible Files (*.csv;*.cars)|*.csv;*.cars|CSV Files (*.csv)|*.csv|Car Manager CSV (*.cars)|*.cars|All Files (*.*)|*.*";
            dlgSave.Title = "Save Car Data File As";
            // Default to users documents
            dlgSave.InitialDirectory = SafeManager.getRecentFolder();
            DialogResult result = dlgSave.ShowDialog();

            if (result == DialogResult.OK)
            {
                // does file already exist?
                /*if (System.IO.File.Exists(dlgSave.FileName))
                {
                    var overwriteResult = MessageBox.Show("File already exists. Overwrite?", "Overwrite File", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                    if (overwriteResult != DialogResult.Yes)
                    {
                        return;
                    }
                }*/
                // Windows already handles this lmao
                filepath = dlgSave.FileName;
                this.Text = "Car Manager - " + System.IO.Path.GetFileName(filepath);
                StateManager.setFilePath(filepath);
                SafeManager.SaveCars(filepath, cars);
                SafeManager.AddRecentPath(filepath);
            }
            refreshRecents();
        }

        private void importToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("Importing will add cars from another file to this file. This action cannot be undone. Continue?", "Import Cars", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
            if (result == DialogResult.Yes)
            {
                OpenFileDialog dlgOpen = new OpenFileDialog();
                dlgOpen.Filter = "Compatible Files (*.csv;*.cars)|*.csv;*.cars|CSV Files (*.csv)|*.csv|Car Manager CSV (*.cars)|*.cars|All Files (*.*)|*.*";
                dlgOpen.Title = "Import Car Data File";
                // Default to users documents
                dlgOpen.InitialDirectory = SafeManager.getRecentFolder();
                DialogResult dlgResult = dlgOpen.ShowDialog();
                if (dlgResult == DialogResult.OK)
                {
                    try
                    {
                        Console.WriteLine("Starting merge...");
                        List<Car> importedCars = SafeManager.ReadCars(dlgOpen.FileName);
                        if (importedCars.Count == 0)
                        {
                            throw new Exception("File doesn't contain valid Cars.");
                        }
                        // merge cars
                        foreach (Car car in importedCars)
                        {
                            cars.Add(car);
                        }
                        DialogResult mergeAsNewFileResult = MessageBox.Show("Do you want to save the merged cars as a new file?", "Save As New File", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                        if (mergeAsNewFileResult == DialogResult.Yes)
                        {
                            SaveFileDialog dlgSave = new SaveFileDialog();
                            dlgSave.Filter = "Compatible Files (*.csv;*.cars)|*.csv;*.cars|CSV Files (*.csv)|*.csv|Car Manager CSV (*.cars)|*.cars|All Files (*.*)|*.*";
                            dlgSave.Title = "Save Merged Car Data File As";
                            // Default to users documents
                            dlgSave.InitialDirectory = SafeManager.getRecentFolder();
                            DialogResult saveResult = dlgSave.ShowDialog();
                            if (saveResult == DialogResult.OK)
                            {
                                filepath = dlgSave.FileName;
                                StateManager.setFilePath(filepath);
                                SafeManager.SaveCars(filepath, cars);
                                SafeManager.AddRecentPath(filepath);
                                refreshRecents();
                            }
                        }
                        else
                        {
                            // save to current file
                            SafeManager.SaveCars(filepath, cars);
                        }
                        // Refresh display
                        refreshCars(cars);
                        MessageBox.Show("File imported successfully.", "Import File", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                    catch (LegacyException)
                    {
                        MessageBox.Show("The file you are trying to import is in a legacy format that is no longer supported. Please convert the file to the new format and try again.", "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Error importing file: " + ex.Message);
                    }
                }
                else
                {
                    Console.WriteLine("Import cancelled.");
                }
            }
        }

        // TODO: Unbind and remove this.
        private void recentFilesToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Refreshes the recently opened files menu.
        /// </summary>
        private void refreshRecents()
        {
            recentFilesToolStripMenuItem.DropDownItems.Clear();
            List<string> recentFiles = SafeManager.GetRecentPaths();
            recentFilesToolStripMenuItem.Enabled = recentFiles.Count > 0;
            recentFilesToolStripMenuItem.ToolTipText = recentFiles.Count > 0 ? "" : "No recent files.";
            foreach (string path in recentFiles)
            {
                ToolStripMenuItem item = new ToolStripMenuItem(path);
                item.Click += (s, e2) =>
                {
                    try
                    {
                        List<Car> importedCars = SafeManager.ReadCars(path);
                        if (importedCars.Count == 0)
                        {
                            throw new Exception("File doesn't contain valid Cars.");
                        }
                        filepath = path;
                        cars = importedCars;
                        StateManager.setFilePath(filepath);
                        // Refresh display
                        refreshCars(cars);
                        MessageBox.Show("File loaded successfully.", "Load File", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                    catch (LegacyException)
                    {
                        MessageBox.Show("The file you are trying to open is in a legacy format that is no longer supported. Please convert the file to the new format and try again.", "Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Error loading file: " + ex.Message);
                    }
                };
                recentFilesToolStripMenuItem.DropDownItems.Add(item);

            }
        }

        private void revealInFileExplorerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Open File Explorer at the location of the current filepath
            if (System.IO.File.Exists(filepath))
            {
                System.Diagnostics.Process.Start("explorer.exe", "/select,\"" + filepath + "\"");
            }
            else
            {
                MessageBox.Show("File does not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void openWelcomeScreenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openWelcomeScreen();
        }

        private void addCarToolStripMenuItem_Click(object sender, EventArgs e)
        {
            btnNewCar.PerformClick();
        }

        private void clearSearchToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tbxSearch.Text = "";
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SafeManager.SaveCars(filepath, cars);
            Environment.Exit(0);
        }

        private void clearRecentFilesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SafeManager.ClearRecentPaths();
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SettingsForm settingsForm = new SettingsForm();
            settingsForm.FormClosed += (s2, e2) =>
            {
                // refresh cars in case data location changed
                List<Car> cars_ = SafeManager.ReadCars(filepath);
                refreshCars(cars_, false, true);
                System.Diagnostics.Debug.WriteLine("Refreshed!");
            };
            settingsForm.ShowDialog();

        }

        private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                if (Updater.IsUpdateAvailable(Properties.Settings.Default.AllowPrerelease))
                {
                    UpdatePromptForm updatePrompt = new UpdatePromptForm(Updater.GetCurrentVersion(true), Updater.GetLatestVersion(Properties.Settings.Default.AllowPrerelease));
                    updatePrompt.ShowDialog();
                }
                else
                {
                    MessageBox.Show($"You are already using the latest version. \nYour Version: {Updater.GetCurrentVersion(true)} \nLatest Version: {Updater.GetLatestVersion(Properties.Settings.Default.AllowPrerelease)}", "No Updates Available", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error checking for updates: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void gitRepositoryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Open the Git repository in the user's default browser
            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
            {
                FileName = "https://git.jaro.digital/frozd/carmanager-3",
                UseShellExecute = true
            });
        }
    }
}
