using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CarManagerV3.Util;

namespace CarManagerV3.Forms
{
    public partial class SettingsForm : Form
    {
        // Settings map (Maps settings to controls and default values + optional change event handler function that takes previous and new value)
        private readonly Dictionary<string, ISettingBinding> settingsMap = new();

        public SettingsForm()
        {
            InitializeComponent();
            initializeSettingsMap();
        }

        private void initializeSettingsMap()
        {
            // Initialize the settings map with setting keys, associated controls, default values, and optional change event handlers
            settingsMap["DataLocation"] =
                new SettingBinding<string>(
                    control: tbxDataLocation,
                    defaultValue: Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\CarManagerV3",
                    read: () => Properties.Settings.Default.DataLocation,          // strongly typed
                    write: v => Properties.Settings.Default.DataLocation = v,     // strongly typed
                    onChange: (before, after) =>
                    {
                        // TODO: handle path change if needed
                    });

            settingsMap["AllowPrerelease"] =
                new SettingBinding<bool>(
                    control: cbxPreRelease,
                    defaultValue: false,
                    read: () => Properties.Settings.Default.AllowPrerelease,
                    write: v => Properties.Settings.Default.AllowPrerelease = v);

            //TODO: implement refresh settings


            lblEnv.Text = lblEnv.Text.Replace("%E%", InstallModeDetector.IsInstalledViaMsi() ? "Installed via MSI" : "Running in portable mode");

        }

        private void SettingsForm_Load(object sender, EventArgs e)
        {
            loadSettings();
        }

        private void loadSettings()
        {
            foreach (var kvp in settingsMap)
                kvp.Value.Load();
        }

        private void saveSettings()
        {
            bool requiresRestart = false;
            foreach (var kvp in settingsMap)
            {
                kvp.Value.Save();
                if (kvp.Value.RequiresRestart())
                    requiresRestart = true;
            }
                

            Properties.Settings.Default.Save();
            if(requiresRestart)
            {
                DialogResult result = MessageBox.Show(
                    "Some changes you made require a restart to take effect. Do you want to restart now?",
                    "Restart Required",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Information);
                if (result == DialogResult.Yes)
                {
                    Application.Restart();
                } else
                {
                    MessageBox.Show("Please restart the application as soon as possible to ensure all changes take effect.", "Restart Recommended", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }

            
        }

        private void resetSettings()
        {
            DialogResult confirmReset = MessageBox.Show(
                "Are you sure you want to reset all settings to their default values? This action cannot be undone.",
                "Confirm Reset",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Warning);

            if (confirmReset != DialogResult.Yes)
                return;

            foreach (var kvp in settingsMap)
                kvp.Value.Reset();

            Properties.Settings.Default.Save();
            loadSettings();
        }

        private void btnAccept_Click(object sender, EventArgs e)
        {
            saveSettings();
            this.Close();
        }

        private void btnDiscard_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            resetSettings();
        }
    }

    internal interface ISettingBinding
    {
        void Load();
        void Save();
        void Reset();
        bool RequiresRestart();
    }

    internal sealed class SettingBinding<T> : ISettingBinding
    {
        private readonly Control control;
        private readonly T defaultValue;
        private readonly Func<T> read;
        private readonly Action<T> write;
        private readonly Action<T, T>? onChange;
        private readonly bool requiresRestart;
        private bool changeRequiresRestart;

        public SettingBinding(Control control, T defaultValue, Func<T> read, Action<T> write, bool requiresRestart = false, Action<T, T>? onChange = null)
        {
            this.control = control;
            this.defaultValue = defaultValue;
            this.read = read;
            this.write = write;
            this.onChange = onChange;
            this.requiresRestart = requiresRestart;
            this.changeRequiresRestart = false;
        }

        public void Load()
        {
            T value = read();
            ApplyToControl(value);
        }

        public void Save()
        {
            T before = read();
            T after = ReadFromControl();

            write(after);

            if (onChange != null && !EqualityComparer<T>.Default.Equals(before, after))
                onChange(before, after);

            if (requiresRestart && !EqualityComparer<T>.Default.Equals(before, after))
                changeRequiresRestart = true;
        }

        public void Reset() => write(defaultValue);

        public bool RequiresRestart() => changeRequiresRestart;

        private void ApplyToControl(T value)
        {
            switch (control)
            {
                case TextBox tb:
                    tb.Text = value?.ToString() ?? string.Empty;
                    break;

                case CheckBox cb:
                    cb.Checked = value is bool b ? b : Convert.ToBoolean(value);
                    break;

                default:
                    throw new NotSupportedException($"Control type '{control.GetType().Name}' not supported for {typeof(T).Name}.");
            }
        }

        private T ReadFromControl()
        {
            object result = control switch
            {
                TextBox tb when typeof(T) == typeof(string) => tb.Text,
                CheckBox cb when typeof(T) == typeof(bool) => cb.Checked,
                _ => throw new NotSupportedException($"Cannot read {typeof(T).Name} from control type '{control.GetType().Name}'.")
            };

            return (T)result;
        }
    }
}
