using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Newtonsoft.Json;

namespace UnshackleGUI
{
    public partial class MainForm : Form
    {
        // =========================================================
        // 1. FILE PATHS & SETTINGS
        // =========================================================
        private string _configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json");
        private string _paramsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "params.json");
        private string _profilesDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles");

        // Data Storage
        private AppSettings _appSettings = new AppSettings();
        private List<UnshackleParameter> _parameterDefinitions = new List<UnshackleParameter>();
        private Dictionary<string, object> _propertyValues = new Dictionary<string, object>();

        // State Variables
        private Process? _currentProcess;
        private bool _isLoaded = false; // Flag to prevent saving while the app is starting up
        private string _lastLoadedProfile = ""; // Tracks the original name
        // =========================================================
        // 2. CONSTRUCTOR & INITIALIZATION
        // =========================================================
        public MainForm()
        {
            InitializeComponent();
            ApplyDarkTheme();

            // Ensure the Profiles folder exists
            if (!Directory.Exists(_profilesDir))
            {
                Directory.CreateDirectory(_profilesDir);
            }

            // Step 1: Set up all button clicks and text changes
            SetupEventHandlers();

            // Step 2: Load data from disk (Config, Params, Profiles)
            LoadData();
        }

        private void SetupEventHandlers()
        {
            // --- Property Grid (The Settings List) ---
            // Whenever a value changes in the grid, update the command text and save the profile immediately.
            pgProfile.PropertyValueChanged += (s, e) =>
            {
                UpdateCommandPreview();
                SaveCurrentProfile();
            };

            // --- Service Dropdown (Netflix, Amazon, etc.) ---
            comboService.SelectedIndexChanged += (s, e) =>
            {
                // Don't run this logic if the app is still starting up
                if (!_isLoaded) return;

                if (comboService.SelectedItem != null)
                {
                    // Update the "Service" value in our data dictionary
                    _propertyValues["Service"] = comboService.SelectedItem.ToString();

                    // Update the command preview
                    UpdateCommandPreview();

                    // Save this change to the JSON file immediately
                    SaveCurrentProfile();
                }
            };

            // --- Text Box Changes ---
            txtURL.TextChanged += (s, e) => UpdateCommandPreview();

            txtBinName.TextChanged += (s, e) =>
            {
                UpdateCommandPreview();
                SaveGlobalConfig();
            };

            txtRootPath.TextChanged += (s, e) => SaveGlobalConfig();

            // --- Button Clicks ---
            btnRun.Click += btnRun_Click;
            btnStop.Click += btnStop_Click;
            btnBrowse.Click += btnBrowse_Click;
            btnOpenCookies.Click += btnOpenCookies_Click;
            btnEditServiceConfig.Click += btnEditServiceConfig_Click;
            btnClearLog.Click += btnClearLog_Click;
            btnEditYaml.Click += btnEditYaml_Click;
            btnAddProfile.Click += btnAddProfile_Click;
            btnRemoveProfile.Click += btnRemoveProfile_Click;

            // This line connects the click to the function below
            btnSaveProfile.Click += btnSaveProfile_Click;
        }

        // =========================================================
        // 3. DATA LOADING LOGIC
        // =========================================================
        private void LoadData()
        {
            _isLoaded = false; // Stop events from firing while we load

            // A. Load Parameters (The definitions for flags like -q, -v, etc.)
            if (File.Exists(_paramsPath))
            {
                var json = File.ReadAllText(_paramsPath);
                _parameterDefinitions = JsonConvert.DeserializeObject<List<UnshackleParameter>>(json) ?? new List<UnshackleParameter>();
            }
            else
            {
                MessageBox.Show("Error: params.json not found! The grid will be empty.");
            }

            // B. Load Global Config (Root Path, Binary Name)
            if (File.Exists(_configPath))
            {
                var json = File.ReadAllText(_configPath);
                _appSettings = JsonConvert.DeserializeObject<AppSettings>(json) ?? new AppSettings();

                txtRootPath.Text = _appSettings.RootPath;
                txtBinName.Text = _appSettings.BinaryName;
            }

            // C. Populate the Services Dropdown (Read folders from disk)
            RefreshFolders();

            // D. Load Profiles and select the default one
            RefreshProfileList();

            _isLoaded = true; // Loading done, enable events

            // Force an initial update of the command text
            UpdateCommandPreview();
        }

        private void RefreshProfileList()
        {
            // 1. Temporarily stop listening to the selection event to prevent glitches
            comboProfiles.SelectedIndexChanged -= ComboProfiles_SelectedIndexChanged;

            // 2. Find all .json files in the Profiles folder
            var files = Directory.GetFiles(_profilesDir, "*.json");
            var profileNames = files.Select(Path.GetFileNameWithoutExtension).ToList();

            // 3. If no profiles exist, create a "Default" one
            if (profileNames.Count == 0)
            {
                CreateDefaultProfile();
                profileNames.Add("Default");
            }

            // 4. Update the ComboBox
            comboProfiles.DataSource = null;
            comboProfiles.DataSource = profileNames;

            // 5. Select "Default" or the first available profile
            string targetProfile = profileNames.Contains("Default") ? "Default" : profileNames[0];
            comboProfiles.SelectedItem = targetProfile;

            // 6. Manually trigger the load for this profile
            LoadProfile(targetProfile);

            // 7. Start listening to events again
            comboProfiles.SelectedIndexChanged += ComboProfiles_SelectedIndexChanged;
        }

        private void CreateDefaultProfile()
        {
            _propertyValues = new Dictionary<string, object>();

            // Fill with default values from params.json
            foreach (var p in _parameterDefinitions)
            {
                _propertyValues[p.Name] = p.Default ?? "";
            }

            // Set default service
            if (comboService.Items.Count > 0)
            {
                _propertyValues["Service"] = comboService.Items[0].ToString();
            }

            SaveProfileFile("Default");
        }

        // =========================================================
        // 4. PROFILE SWITCHING & LOADING
        // =========================================================
        private void ComboProfiles_SelectedIndexChanged(object? sender, EventArgs e)
        {
            if (comboProfiles.SelectedItem == null) return;

            string selectedProfile = comboProfiles.SelectedItem.ToString();
            LoadProfile(selectedProfile);
        }

        private void LoadProfile(string profileName)
        {

            _lastLoadedProfile = profileName;

            string path = Path.Combine(_profilesDir, $"{profileName}.json");

            if (!File.Exists(path)) return;

            try
            {
                var json = File.ReadAllText(path);
                var loadedValues = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

                if (loadedValues != null)
                {
                    _propertyValues = loadedValues;

                    // --- FIX 1: Add missing parameters ---
                    // If we added new options to params.json, old profiles won't have them.
                    // This adds them with default values.
                    foreach (var p in _parameterDefinitions)
                    {
                        if (!_propertyValues.ContainsKey(p.Name))
                        {
                            _propertyValues[p.Name] = p.Default ?? "";
                        }
                    }

                    // --- FIX 2: Handle the Service Dropdown ---
                    // Check if this profile has a saved Service (e.g., "AMZN")
                    if (_propertyValues.TryGetValue("Service", out var serviceObj))
                    {
                        string serviceName = serviceObj.ToString();

                        // Only try to switch if the service actually exists in the list
                        if (comboService.Items.Contains(serviceName))
                        {
                            // Temporarily disable _isLoaded to change dropdown without triggering a save loop
                            bool wasLoaded = _isLoaded;
                            _isLoaded = false;

                            comboService.SelectedItem = serviceName;

                            _isLoaded = wasLoaded;
                        }
                    }
                    else if (comboService.SelectedItem != null)
                    {
                        // If profile has NO service saved, save the current one to it
                        _propertyValues["Service"] = comboService.SelectedItem.ToString();
                    }

                    // --- FIX 3: Bind to PropertyGrid and Refresh ---
                    pgProfile.SelectedObject = new DynamicObject(_propertyValues, _parameterDefinitions);
                    pgProfile.Refresh(); // Crucial to prevent grey box

                    UpdateCommandPreview();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error loading profile: {ex.Message}");
            }
        }

        // =========================================================
        // 5. SAVING LOGIC
        // =========================================================
        private void SaveCurrentProfile()
        {
            // Don't save if we are loading or if nothing is selected
            if (!_isLoaded || comboProfiles.SelectedItem == null) return;

            string name = comboProfiles.SelectedItem.ToString();
            SaveProfileFile(name);
        }

        private void SaveProfileFile(string name)
        {
            try
            {
                // Ensure the currently selected service is saved into the dictionary
                if (comboService.SelectedItem != null)
                {
                    _propertyValues["Service"] = comboService.SelectedItem.ToString();
                }

                string path = Path.Combine(_profilesDir, $"{name}.json");
                File.WriteAllText(path, JsonConvert.SerializeObject(_propertyValues, Formatting.Indented));
            }
            catch (Exception ex)
            {
                AppendLog($"[Error Saving Profile]: {ex.Message}");
            }
        }

        private void SaveGlobalConfig()
        {
            if (!_isLoaded) return;

            _appSettings.RootPath = txtRootPath.Text;
            _appSettings.BinaryName = txtBinName.Text;

            try
            {
                File.WriteAllText(_configPath, JsonConvert.SerializeObject(_appSettings, Formatting.Indented));
            }
            catch
            {
                // Ignore errors while typing (file might be busy)
            }
        }

        // =========================================================
        // 6. COMMAND GENERATION
        // =========================================================
        private void UpdateCommandPreview()
        {
            if (!_isLoaded) return;

            StringBuilder sb = new StringBuilder();

            // 1. Start with binary and command
            // e.g., "uv run unshackle dl "
            sb.Append($"{txtBinName.Text} run unshackle dl ");

            // 2. Loop through all parameters in the grid
            foreach (var p in _parameterDefinitions)
            {
                // If value doesn't exist in our data, skip it
                if (!_propertyValues.TryGetValue(p.Name, out object val)) continue;

                // Case A: Boolean Flags (e.g., --no-mux)
                if (p.Type == "Bool" && val is bool isTrue && isTrue)
                {
                    sb.Append($"{p.Flag} ");
                }
                // Case B: Text / Selection / Number
                else if (p.Type != "Bool" && val != null)
                {
                    string sVal = val.ToString();

                    // Validation:
                    // 1. Not empty
                    // 2. Not "any" (Default)
                    // 3. Not "0" (Default for numbers)
                    if (!string.IsNullOrWhiteSpace(sVal) && sVal != "any" && sVal != "0")
                    {
                        // Quote the value if it has spaces
                        if (sVal.Contains(" "))
                        {
                            sVal = $"\"{sVal}\"";
                        }

                        sb.Append($"{p.Flag} {sVal} ");
                    }
                }
            }

            // 3. Append Service and URL
            // e.g., "AMZN https://..."
            sb.Append($"{comboService.Text} {txtURL.Text}");

            // 4. Update UI
            txtCommandPreview.Text = sb.ToString();
        }

        // =========================================================
        // 7. CLI EXECUTION (The Run Button)
        // =========================================================
        private async void btnRun_Click(object? sender, EventArgs e)
        {
            string finalCmd = txtCommandPreview.Text;

            if (string.IsNullOrWhiteSpace(finalCmd)) return;

            // Disable buttons while running
            ToggleUI(true);

            txtLog.AppendText($"> Executing: {finalCmd}{Environment.NewLine}");

            try
            {
                // Run in background thread to keep UI responsive
                await Task.Run(() => RunCli(finalCmd));
            }
            catch (Exception ex)
            {
                AppendLog($"[ERROR]: {ex.Message}");
            }
            finally
            {
                // Re-enable buttons
                ToggleUI(false);
            }
        }

        private void RunCli(string cmd)
        {
            // We use CMD.EXE instead of PowerShell to avoid Antivirus false positives
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = "cmd.exe",
                Arguments = $"/c chcp 65001 && {cmd}", // Force UTF-8
                WorkingDirectory = txtRootPath.Text,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true,
                StandardOutputEncoding = Encoding.UTF8
            };

            // Set Environment variable for Python
            psi.EnvironmentVariables["PYTHONIOENCODING"] = "utf-8";

            using (_currentProcess = Process.Start(psi))
            {
                if (_currentProcess == null) return;

                // Handle Output
                _currentProcess.OutputDataReceived += (s, e) =>
                {
                    // Filter out the "Active code page: 65001" noise
                    if (!string.IsNullOrEmpty(e.Data) && !e.Data.Contains("Active code page"))
                    {
                        AppendLog(e.Data);
                    }
                };

                // Handle Errors
                _currentProcess.ErrorDataReceived += (s, e) => AppendLog(e.Data);

                _currentProcess.BeginOutputReadLine();
                _currentProcess.BeginErrorReadLine();
                _currentProcess.WaitForExit();
            }
        }

        // =========================================================
        // 8. PROFILE MANAGEMENT (Add/Remove/Save Buttons)
        // =========================================================
        private void btnAddProfile_Click(object? sender, EventArgs e)
        {
            string name = Microsoft.VisualBasic.Interaction.InputBox("Enter profile name:", "New Profile", "");

            if (string.IsNullOrWhiteSpace(name)) return;

            // Sanitize filename (remove illegal characters like / \ : *)
            foreach (char c in Path.GetInvalidFileNameChars())
            {
                name = name.Replace(c, '_');
            }

            // Check if exists
            if (File.Exists(Path.Combine(_profilesDir, $"{name}.json")))
            {
                MessageBox.Show("Profile already exists.");
                return;
            }

            // Save current settings as the new profile
            SaveProfileFile(name);

            // Reload list and select new profile
            RefreshProfileList();
            comboProfiles.SelectedItem = name;
        }

        private void btnRemoveProfile_Click(object? sender, EventArgs e)
        {
            if (comboProfiles.SelectedItem == null) return;

            string selected = comboProfiles.SelectedItem.ToString();

            if (selected == "Default")
            {
                MessageBox.Show("Default profile cannot be deleted.");
                return;
            }

            string path = Path.Combine(_profilesDir, $"{selected}.json");

            if (File.Exists(path))
            {
                File.Delete(path);
            }

            RefreshProfileList();
        }

        private void btnSaveProfile_Click(object? sender, EventArgs e)
        {
            string newName = comboProfiles.Text.Trim();

            // 1. Validation
            if (string.IsNullOrWhiteSpace(newName))
            {
                MessageBox.Show("Please enter a profile name.");
                return;
            }

            foreach (char c in Path.GetInvalidFileNameChars()) newName = newName.Replace(c, '_');

            // 2. CHECK: Did the name change?
            if (newName != _lastLoadedProfile && !string.IsNullOrEmpty(_lastLoadedProfile))
            {
                // Special Case: Cannot rename Default
                if (_lastLoadedProfile == "Default")
                {
                    // Just create new, don't ask to rename Default
                    SaveProfileFile(newName);
                    RefreshProfileList();
                    comboProfiles.SelectedItem = newName;
                    MessageBox.Show($"Created new profile '{newName}' from Default.", "New Profile Created");
                    return;
                }

                // Ask the user what to do
                DialogResult choice = MessageBox.Show(
                    $"You changed the name from '{_lastLoadedProfile}' to '{newName}'.\n\n" +
                    "Click YES to RENAME (Delete old).\n" +
                    "Click NO to CREATE COPY (Keep old).",
                    "Rename or Copy?",
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Question);

                if (choice == DialogResult.Cancel) return;

                if (choice == DialogResult.Yes) // RENAME
                {
                    // Save New
                    SaveProfileFile(newName);

                    // Delete Old
                    string oldPath = Path.Combine(_profilesDir, $"{_lastLoadedProfile}.json");
                    if (File.Exists(oldPath)) File.Delete(oldPath);

                    RefreshProfileList();
                    comboProfiles.SelectedItem = newName; // This updates _lastLoadedProfile automatically
                    MessageBox.Show($"Renamed to '{newName}'.");
                }
                else // CREATE COPY
                {
                    SaveProfileFile(newName);
                    RefreshProfileList();
                    comboProfiles.SelectedItem = newName;
                    MessageBox.Show($"Created copy '{newName}'.");
                }
            }
            else
            {
                // 3. Name didn't change (Normal Save)
                SaveProfileFile(newName);
                MessageBox.Show($"Profile '{newName}' saved!", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }



        // =========================================================
        // 9. HELPER METHODS & THEME
        // =========================================================

        private void AppendLog(string? text)
        {
            if (string.IsNullOrEmpty(text)) return;

            // Ensure we update the UI on the main thread
            this.BeginInvoke(new Action(() =>
            {
                txtLog.AppendText(text + Environment.NewLine);
            }));
        }

        private void ToggleUI(bool isRunning)
        {
            this.BeginInvoke(new Action(() =>
            {
                btnRun.Enabled = !isRunning;
                btnStop.Visible = isRunning;
            }));
        }

        private void btnStop_Click(object? sender, EventArgs e)
        {
            if (_currentProcess != null && !_currentProcess.HasExited)
            {
                _currentProcess.Kill(true);
            }
        }

        private void btnClearLog_Click(object? sender, EventArgs e)
        {
            txtLog.Clear();
        }

        private void btnBrowse_Click(object? sender, EventArgs e)
        {
            using (var fbd = new FolderBrowserDialog())
            {
                if (fbd.ShowDialog() == DialogResult.OK)
                {
                    txtRootPath.Text = fbd.SelectedPath;
                    SaveGlobalConfig();
                    RefreshFolders();
                }
            }
        }

        private void btnOpenCookies_Click(object? sender, EventArgs e)
        {
            string path = Path.Combine(txtRootPath.Text, "unshackle", "cookies", comboService.Text);
            Directory.CreateDirectory(path);
            Process.Start("explorer.exe", path);
        }

        private void btnEditYaml_Click(object? sender, EventArgs e)
        {
            string yamlPath = Path.Combine(txtRootPath.Text, "unshackle/unshackle.yaml");
            if (File.Exists(yamlPath))
            {
                Process.Start(new ProcessStartInfo(yamlPath) { UseShellExecute = true });
            }
        }

        private void btnEditServiceConfig_Click(object? sender, EventArgs e)
        {
            string yamlPath = Path.Combine(txtRootPath.Text, "unshackle", "services", comboService.Text, "config.yaml");
            if (File.Exists(yamlPath))
            {
                Process.Start(new ProcessStartInfo(yamlPath) { UseShellExecute = true });
            }
            else
            {
                MessageBox.Show($"No config.yaml found for {comboService.Text}");
            }
        }

        private void RefreshFolders()
        {
            comboService.Items.Clear();
            string path = Path.Combine(txtRootPath.Text, "unshackle", "services");

            if (Directory.Exists(path))
            {
                var dirs = Directory.GetDirectories(path).Select(Path.GetFileName).ToArray();
                comboService.Items.AddRange(dirs.Cast<object>().ToArray());

                if (comboService.Items.Count > 0)
                {
                    comboService.SelectedIndex = 0;
                }
            }
        }

        private void ApplyDarkTheme()
        {
            this.BackColor = Color.FromArgb(30, 30, 30);
            this.ForeColor = Color.White;

            foreach (Control c in this.Controls)
            {
                UpdateControlTheme(c);
            }
        }

        private void UpdateControlTheme(Control c)
        {
            c.BackColor = Color.FromArgb(45, 45, 48);
            c.ForeColor = Color.White;

            if (c is TextBox tb)
            {
                tb.BorderStyle = BorderStyle.FixedSingle;
            }

            if (c is Button btn)
            {
                btn.FlatStyle = FlatStyle.Flat;
                btn.FlatAppearance.BorderColor = Color.Gray;
            }

            if (c is PropertyGrid pg)
            {
                pg.BackColor = Color.FromArgb(37, 37, 38);
                pg.ViewBackColor = Color.FromArgb(37, 37, 38);
                pg.ViewForeColor = Color.White;
                pg.LineColor = Color.FromArgb(45, 45, 48);
            }

            // Recursive call for panels and group boxes
            foreach (Control child in c.Controls)
            {
                UpdateControlTheme(child);
            }
        }
    }


    // =========================================================
    // 10. DATA MODELS
    // =========================================================

    public class AppSettings
    {
        public string RootPath { get; set; } = @"C:\DEVINE\unshackle";
        public string BinaryName { get; set; } = "uv";
    }

    public class UnshackleParameter
    {
        public string Flag { get; set; } = "";
        public string Name { get; set; } = "";
        public string Type { get; set; } = "Text";
        public List<string>? Options { get; set; }
        public object? Default { get; set; }
        public string Category { get; set; } = "Misc";
    }
}