using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CarManagerV3.Classes;

namespace CarManagerV3.Util
{
    /// <summary>
    /// Provides car manufacturer and model autocompletion data for the application.
    /// Manages fetching, caching, and retrieval of car brand and model information from remote sources and local storage.
    /// </summary>
    /// <remarks>
    /// This class maintains a static list of car manufacturers and their models, supports fetching updated data from remote URLs,
    /// and caches the data locally for offline use. It implements retry logic to prevent excessive network requests.
    /// </remarks>
    internal class CarCompletions
    {
        /// <summary>
        /// A list of URLs that should be used to fetch the json file with autocompletion data.
        /// Multiple URLs are provided to ensure that if one source is unavailable, the others can be used as a fallback. The application should attempt to fetch the data from each URL in order until it successfully retrieves the data or exhausts all options.
        /// </summary>
        private static readonly string[] carDataFileUrls =
        {
            "https://static.clsw.app/carmgm/merged-cars.json"
        };

        /// <summary>
        /// The filename for the cached car completion data in the user's data directory.
        /// </summary>
        private static readonly string carCompletionDataFileName = "car_data.json";

        /// <summary>
        /// The timestamp of the last fetch attempt for car completion data.
        /// Used to implement retry throttling to prevent excessive network requests.
        /// </summary>
        private static DateTime lastFetchAttempt = Properties.Settings.Default.LastFetchedAutoCompletions;

        /// <summary>
        /// The minimum time interval that must elapse between fetch attempts.
        /// Prevents excessive retry attempts when the remote source is unavailable.
        /// </summary>
        private static readonly TimeSpan fetchRetryInterval = Properties.Settings.Default.FetchAutoCompletionsInterval; // Minimum interval between fetch attempts

        /// <summary>
        /// Gets the full file path for the car completion data file.
        /// Ensures that the user data directory exists before returning the path.
        /// </summary>
        /// <returns>The full file path to the car completion data file in the user's data directory.</returns>
        private static string getCarCompletionDataFilePath()
        {
            var userDataDir = Properties.Settings.Default.DataLocation;
            SafeManager.EnsureDirectoryExists(userDataDir);
            return Path.Combine(userDataDir, carCompletionDataFileName);
        }

        /// <summary>
        /// The static list of car manufacturers and their available models.
        /// This list serves as the default data and is updated when fetching from remote sources or local cache.
        /// </summary>
        public static List<CarManufacturer> carBrands = new List<CarManufacturer>
        {
            new CarManufacturer("Toyota") { Models = new List<string> { "Camry", "Corolla", "RAV4" } },
            new CarManufacturer("Honda") { Models = new List<string> { "Civic", "Accord", "CR-V" } },
            new CarManufacturer("Ford") { Models = new List<string> { "F-150", "Mustang", "Escape" } },
            new CarManufacturer("Chevrolet") { Models = new List<string> { "Silverado", "Malibu", "Equinox" } },
            new CarManufacturer("BMW") { Models = new List<string> { "3 Series", "5 Series", "X5" } },
            new CarManufacturer("Mercedes-Benz") { Models = new List<string> { "C-Class", "E-Class", "GLC" } },
            new CarManufacturer("Audi") { Models = new List<string> { "A4", "A6", "Q5" } },
            new CarManufacturer("Volkswagen") { Models = new List<string> { "Golf", "Passat", "Tiguan" } },
            new CarManufacturer("Nissan") { Models = new List<string> { "Altima", "Sentra", "Rogue" } },
            new CarManufacturer("Hyundai") { Models = new List<string> { "Elantra", "Sonata", "Tucson" } },
            new CarManufacturer("SEAT") { Models = new List<string> { "Ibiza", "Leon", "Ateca", "Alhambra" } },
            new CarManufacturer("Skoda") { Models = new List<string> { "Octavia", "Fabia", "Kodiaq" } },
        };

        /// <summary>
        /// Gets the full list of car brands.
        /// </summary>
        /// <returns>A list of strings containing all available car manufacturer names.</returns>
        public static List<string> GetCarBrands()
        {
            return carBrands.Select(c => c.Name).ToList();
        }

        /// <summary>
        /// Retrieves the list of car models for a specified brand.
        /// The search is case-insensitive.
        /// </summary>
        /// <param name="brand">The name of the car brand to search for.</param>
        /// <returns>A list of car models for the specified brand, or an empty list if the brand is not found.</returns>
        public static List<string> GetCarModels(string brand)
        {
            var manufacturer = carBrands.FirstOrDefault(c => c.Name.Equals(brand, StringComparison.OrdinalIgnoreCase));
            return manufacturer != null ? manufacturer.Models : new List<string>();
        }

        /// <summary>
        /// A predefined list of common car colors for autocompletion purposes.
        /// Provides standard color options that are frequently used in vehicle descriptions.
        /// </summary>
        public static List<string> CommonColors = new List<string>
        {
            "Black", "White", "Gray", "Silver", "Red", "Blue", "Green", "Yellow", "Brown", "Orange"
        };

        /// <summary>
        /// Reads car completion data from an embedded resource file.
        /// This method is intended to populate the carBrands list from a JSON resource embedded in the project.
        /// </summary>
        /// <remarks>
        /// The expected resource format is a JSON object where keys are car brand names and values are lists of model names.
        /// Resource name: CarCompleteData (JSON file format).
        /// Currently not implemented.
        /// </remarks>
        public static void ReadFromResourceFile()
        {
            // Read the json file from the projects resources and populate the carBrands list
            // Format is a json object with the key being the car brand and the value being a list of models

            // Get the json string from the resources, Resource name is CarCompleteData, it is a .json file


        }

        /// <summary>
        /// Fetches the car data from urls asynchronous and saves it in the users data directory for offline use.
        /// </summary>
        /// <returns>A task representing the asynchronous operation.</returns>
        /// <remarks>
        /// This method implements retry throttling using the <see cref="fetchRetryInterval"/> to prevent excessive network requests.
        /// It attempts to fetch data from each URL in <see cref="carDataFileUrls"/> in order until successful or all URLs are exhausted.
        /// The fetched data is saved to the local file system for offline access.
        /// </remarks>
        public static async Task FetchCarCompletionDataFromUrlsAsync()
        {
            if(DateTime.Now - lastFetchAttempt < fetchRetryInterval)
            {
                System.Diagnostics.Debug.WriteLine("Fetch attempt skipped to avoid excessive retries. Last attempt was at " + lastFetchAttempt);
                Console.WriteLine("Fetch attempt skipped to avoid excessive retries. Last attempt was at " + lastFetchAttempt);
                return;
            }
            foreach (var url in carDataFileUrls)
            {
                try
                {
                    lastFetchAttempt = DateTime.Now;
                    Properties.Settings.Default.LastFetchedAutoCompletions = lastFetchAttempt;
                    Properties.Settings.Default.Save();
                    using var httpClient = new HttpClient();
                    var response = await httpClient.GetAsync(url);
                    response.EnsureSuccessStatusCode();
                    var jsonData = await response.Content.ReadAsStringAsync();
                    // Saves the json data to a file in the user's data directory for offline use
                    var filePath = getCarCompletionDataFilePath();
                    await File.WriteAllTextAsync(filePath, jsonData);
                    System.Diagnostics.Debug.WriteLine($"Successfully fetched car data from {url} and saved to {filePath}");
                    break; // Exit the loop if data is successfully fetched
                }
                catch (Exception ex)
                {
                    // Log the error and try the next URL
                    System.Diagnostics.Debug.WriteLine($"Failed to fetch car data from {url}: {ex.Message}");
                    Console.WriteLine($"Failed to fetch car data from {url}: {ex.Message}");
                }
            }
        }

        /// <summary>
        /// Fetches the car data from urls and saves it in the users data directory for offline use.
        /// This is a synchronous wrapper that blocks until the asynchronous fetch operation completes.
        /// </summary>
        /// <returns>A task representing the synchronous operation.</returns>
        /// <remarks>
        /// This method wraps <see cref="FetchCarCompletionDataFromUrlsAsync"/> and runs it synchronously.
        /// Consider using the async version directly when possible to avoid blocking the thread.
        /// </remarks>
        public static Task FetchCarCompletionDataFromUrls()
        {
            // Run synchronously
            return Task.Run(() => FetchCarCompletionDataFromUrlsAsync().Wait());
        }

        /// <summary>
        /// Loads car manufacturer data from the locally cached file.
        /// Optionally fetches updated data from remote sources before loading from file.
        /// </summary>
        /// <param name="skipOnlineUpdate">If true, skips fetching data from remote sources and only reads from the local cache. Default is false.</param>
        /// <returns>A list of <see cref="CarManufacturer"/> objects populated from the cached file, or an empty list if the file doesn't exist or an error occurs.</returns>
        /// <remarks>
        /// The expected file format is a JSON object where keys are car brand names and values are lists of model names.
        /// If the file doesn't exist or cannot be read, an error message is logged and an empty list is returned.
        /// </remarks>          
        public static List<CarManufacturer> GetFromFile(bool skipOnlineUpdate = false)
        {
            if (!skipOnlineUpdate) FetchCarCompletionDataFromUrls();
            var filePath = getCarCompletionDataFilePath();
            if (File.Exists(filePath))
            {
                try
                {
                    var jsonData = File.ReadAllText(filePath);
                    // Parse the json data and populate the carBrands list
                    // Format is a json object with the key being the car brand and the value being a list of models
                    var carData = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(jsonData);
                    List<CarManufacturer> _carBrands = carData.Select(kvp => new CarManufacturer(kvp.Key) { Models = kvp.Value }).ToList();
                    return _carBrands;
                }
                catch (Exception ex)
                {
                    // Silent error.
                    Console.WriteLine($"Failed to read car data from file: {ex.Message}");
                }
            }
            else
            {
                Console.Error.WriteLine("Car data file not found. Please ensure that the data has been fetched successfully at least once.");
            }
            return new List<CarManufacturer>();
        }

        /// <summary>
        /// Updates the static <see cref="carBrands"/> list with data from the local cache file.
        /// Only updates the list if the cached file contains valid data.
        /// </summary>
        /// <param name="skipOnlineUpdate">If true, skips fetching data from remote sources before updating. Default is false.</param>
        /// <remarks>
        /// This method calls <see cref="GetFromFile"/> and updates <see cref="carBrands"/> only if the returned list is not empty.
        /// If no data is retrieved, the existing <see cref="carBrands"/> list remains unchanged.
        /// </remarks>
        public static void UpdateCarCompletionData(bool skipOnlineUpdate = false)
        {
            var updatedCarBrands = GetFromFile(skipOnlineUpdate);
            if (updatedCarBrands.Count > 0)
            {
                carBrands = updatedCarBrands;
            }
        }

        /// <summary>
        /// Asynchronously updates the car completion data by fetching from remote sources and updating the local cache.
        /// This is the recommended method for refreshing car data in the application.
        /// </summary>
        /// <returns>A task representing the asynchronous update operation.</returns>
        /// <remarks>
        /// This method first attempts to fetch updated data from remote URLs using <see cref="FetchCarCompletionDataFromUrlsAsync"/>,
        /// then updates the static <see cref="carBrands"/> list from the local cache using <see cref="UpdateCarCompletionData"/>.
        /// </remarks>
        public static async Task UpdateCarCompletionDataAsync()
        {
            await FetchCarCompletionDataFromUrlsAsync();
            System.Diagnostics.Debug.WriteLine("Car completion data fetch attempt completed. Now updating local data.");
            UpdateCarCompletionData(true);
        }
    }

}
