// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Common.UI;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.ViewModels;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using PowerToys.GPOWrapper;
using Settings.UI.Library;
using Windows.Devices.Geolocation;

namespace Microsoft.PowerToys.Settings.UI.Views
{
    public sealed partial class LightSwitchPage : NavigablePage, IRefreshablePage
    {
        private static readonly TimeSpan GeoLocationTimeout = TimeSpan.FromSeconds(10);

        private readonly string appName = "LightSwitch";
        private readonly SettingsUtils settingsUtils;
        private readonly Func<string, int> sendConfigMsg = ShellPage.SendDefaultIPCMessage;

        private readonly SettingsRepository<GeneralSettings> generalSettingsRepository;
        private readonly SettingsRepository<LightSwitchSettings> moduleSettingsRepository;

        private readonly IFileSystem fileSystem;
        private readonly IFileSystemWatcher fileSystemWatcher;
        private readonly DispatcherQueue dispatcherQueue;
        private bool suppressViewModelUpdates;

        private LightSwitchViewModel ViewModel { get; set; }

        public LightSwitchPage()
        {
            this.settingsUtils = SettingsUtils.Default;
            this.sendConfigMsg = ShellPage.SendDefaultIPCMessage;

            this.generalSettingsRepository = SettingsRepository<GeneralSettings>.GetInstance(this.settingsUtils);
            this.moduleSettingsRepository = SettingsRepository<LightSwitchSettings>.GetInstance(this.settingsUtils);

            // Get settings from JSON (or defaults if JSON missing)
            var darkSettings = this.moduleSettingsRepository.SettingsConfig;

            // Pass them into the ViewModel
            this.ViewModel = new LightSwitchViewModel(this.generalSettingsRepository, darkSettings, ShellPage.SendDefaultIPCMessage);
            this.ViewModel.PropertyChanged += ViewModel_PropertyChanged;

            this.LoadSettings(this.generalSettingsRepository, this.moduleSettingsRepository);

            DataContext = this.ViewModel;

            var settingsPath = this.settingsUtils.GetSettingsFilePath(this.appName);

            this.dispatcherQueue = DispatcherQueue.GetForCurrentThread();
            this.fileSystem = new FileSystem();

            this.fileSystemWatcher = this.fileSystem.FileSystemWatcher.New();
            this.fileSystemWatcher.Path = this.fileSystem.Path.GetDirectoryName(settingsPath);
            this.fileSystemWatcher.Filter = this.fileSystem.Path.GetFileName(settingsPath);
            this.fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime;
            this.fileSystemWatcher.Changed += Settings_Changed;
            this.fileSystemWatcher.EnableRaisingEvents = true;

            this.InitializeComponent();
            Loaded += LightSwitchPage_Loaded;
            Loaded += (s, e) => this.ViewModel.OnPageLoaded();
        }

        public void RefreshEnabledState()
        {
            this.ViewModel.RefreshEnabledState();
        }

        private void LightSwitchPage_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.ViewModel.SearchLocations.Count == 0)
            {
                foreach (var city in SearchLocationLoader.GetAll())
                {
                    this.ViewModel.SearchLocations.Add(city);
                }
            }

            this.ViewModel.InitializeScheduleMode();
        }

        private async void GetGeoLocation_Click(object sender, RoutedEventArgs e)
        {
            this.LatitudeBox.IsEnabled = false;
            this.LongitudeBox.IsEnabled = false;
            this.SyncButton.IsEnabled = false;
            this.SyncLoader.IsActive = true;
            this.SyncLoader.Visibility = Visibility.Visible;
            this.LocationResultPanel.Visibility = Visibility.Collapsed;
            this.LocationErrorText.Visibility = Visibility.Collapsed;

            try
            {
                // Request access
                var accessStatus = await Geolocator.RequestAccessAsync();
                if (accessStatus != GeolocationAccessStatus.Allowed)
                {
                    ShowLocationError(ResourceLoaderInstance.ResourceLoader.GetString("LightSwitch_LocationError_Unavailable"));
                    return;
                }

                var geolocator = new Geolocator { DesiredAccuracy = PositionAccuracy.Default };

                using var cts = new CancellationTokenSource(GeoLocationTimeout);
                var positionTask = geolocator.GetGeopositionAsync().AsTask(cts.Token);
                Geoposition pos = await positionTask;

                double latitude = Math.Round(pos.Coordinate.Point.Position.Latitude);
                double longitude = Math.Round(pos.Coordinate.Point.Position.Longitude);

                ViewModel.LocationPanelLatitude = latitude;
                ViewModel.LocationPanelLongitude = longitude;

                var result = SunCalc.CalculateSunriseSunset(latitude, longitude, DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);

                this.ViewModel.LocationPanelLightTimeMinutes = (result.SunriseHour * 60) + result.SunriseMinute;
                this.ViewModel.LocationPanelDarkTimeMinutes = (result.SunsetHour * 60) + result.SunsetMinute;

                // Since we use this mode, we can remove the selected city data.
                this.ViewModel.SelectedCity = null;

                this.SyncButton.IsEnabled = true;
                this.SyncLoader.IsActive = false;
                this.SyncLoader.Visibility = Visibility.Collapsed;
                this.LocationDialog.IsPrimaryButtonEnabled = true;
                this.LatitudeBox.IsEnabled = true;
                this.LongitudeBox.IsEnabled = true;
                this.LocationResultPanel.Visibility = Visibility.Visible;
            }
            catch (OperationCanceledException)
            {
                ShowLocationError(ResourceLoaderInstance.ResourceLoader.GetString("LightSwitch_LocationError_Timeout"));
            }
            catch (Exception ex)
            {
                ShowLocationError(ResourceLoaderInstance.ResourceLoader.GetString("LightSwitch_LocationError_Timeout"));
                Logger.LogInfo($"Location error: " + ex.Message);
            }
        }

        private void ShowLocationError(string message)
        {
            this.SyncButton.IsEnabled = true;
            this.SyncLoader.IsActive = false;
            this.SyncLoader.Visibility = Visibility.Collapsed;
            this.LocationResultPanel.Visibility = Visibility.Collapsed;
            this.LatitudeBox.IsEnabled = true;
            this.LongitudeBox.IsEnabled = true;
            this.LocationErrorText.Text = message;
            this.LocationErrorText.Visibility = Visibility.Visible;
        }

        private void LatLonBox_ValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args)
        {
            double latitude = this.LatitudeBox.Value;
            double longitude = this.LongitudeBox.Value;

            if (double.IsNaN(latitude) || double.IsNaN(longitude) || (latitude == 0 && longitude == 0))
            {
                return;
            }

            var result = SunCalc.CalculateSunriseSunset(latitude, longitude, DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);

            this.ViewModel.LocationPanelLightTimeMinutes = (result.SunriseHour * 60) + result.SunriseMinute;
            this.ViewModel.LocationPanelDarkTimeMinutes = (result.SunsetHour * 60) + result.SunsetMinute;

            this.LocationResultPanel.Visibility = Visibility.Visible;
            if (this.LocationDialog != null)
            {
                this.LocationDialog.IsPrimaryButtonEnabled = true;
            }
        }

        private void LocationDialog_PrimaryButtonClick(object sender, ContentDialogButtonClickEventArgs args)
        {
            if (double.IsNaN(this.LatitudeBox.Value) || double.IsNaN(this.LongitudeBox.Value))
            {
                return;
            }

            double latitude = this.LatitudeBox.Value;
            double longitude = this.LongitudeBox.Value;

            // need to save the values
            this.ViewModel.Latitude = latitude.ToString(CultureInfo.InvariantCulture);
            this.ViewModel.Longitude = longitude.ToString(CultureInfo.InvariantCulture);
            this.ViewModel.SyncButtonInformation = $"{this.ViewModel.Latitude}°, {this.ViewModel.Longitude}°";

            var result = SunCalc.CalculateSunriseSunset(latitude, longitude, DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);

            this.ViewModel.LightTime = (result.SunriseHour * 60) + result.SunriseMinute;
            this.ViewModel.DarkTime = (result.SunsetHour * 60) + result.SunsetMinute;

            this.SunriseModeChartState();
        }

        private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (this.suppressViewModelUpdates)
            {
                return;
            }

            if (e.PropertyName == "IsEnabled")
            {
                if (this.ViewModel.IsEnabled != this.generalSettingsRepository.SettingsConfig.Enabled.LightSwitch)
                {
                    this.generalSettingsRepository.SettingsConfig.Enabled.LightSwitch = this.ViewModel.IsEnabled;

                    var generalSettingsMessage = new OutGoingGeneralSettings(this.generalSettingsRepository.SettingsConfig).ToString();
                    Logger.LogInfo($"Saved general settings from Light Switch page.");

                    this.sendConfigMsg?.Invoke(generalSettingsMessage);
                }
            }
            else
            {
                if (this.ViewModel.ModuleSettings != null)
                {
                    SndLightSwitchSettings currentSettings = new(this.moduleSettingsRepository.SettingsConfig);
                    SndModuleSettings<SndLightSwitchSettings> csIpcMessage = new(currentSettings);

                    SndLightSwitchSettings outSettings = new(this.ViewModel.ModuleSettings);
                    SndModuleSettings<SndLightSwitchSettings> outIpcMessage = new(outSettings);

                    string csMessage = csIpcMessage.ToJsonString();
                    string outMessage = outIpcMessage.ToJsonString();

                    if (!csMessage.Equals(outMessage, StringComparison.Ordinal))
                    {
                        Logger.LogInfo($"Saved Light Switch settings from Light Switch page.");

                        this.sendConfigMsg?.Invoke(outMessage);
                    }
                }
            }
        }

        private void LoadSettings(SettingsRepository<GeneralSettings> generalSettingsRepository, SettingsRepository<LightSwitchSettings> moduleSettingsRepository)
        {
            if (generalSettingsRepository != null)
            {
                if (moduleSettingsRepository != null)
                {
                    UpdateViewModelSettings(moduleSettingsRepository.SettingsConfig, generalSettingsRepository.SettingsConfig);
                }
                else
                {
                    throw new ArgumentNullException(nameof(moduleSettingsRepository));
                }
            }
            else
            {
                throw new ArgumentNullException(nameof(generalSettingsRepository));
            }
        }

        private void UpdateViewModelSettings(LightSwitchSettings lightSwitchSettings, GeneralSettings generalSettings)
        {
            if (lightSwitchSettings != null)
            {
                if (generalSettings != null)
                {
                    this.ViewModel.IsEnabled = generalSettings.Enabled.LightSwitch;
                    this.ViewModel.ModuleSettings = (LightSwitchSettings)lightSwitchSettings.Clone();

                    UpdateEnabledState(generalSettings.Enabled.LightSwitch);
                }
                else
                {
                    throw new ArgumentNullException(nameof(generalSettings));
                }
            }
            else
            {
                throw new ArgumentNullException(nameof(lightSwitchSettings));
            }
        }

        private void Settings_Changed(object sender, FileSystemEventArgs e)
        {
            this.dispatcherQueue.TryEnqueue(() =>
            {
                this.suppressViewModelUpdates = true;

                this.moduleSettingsRepository.ReloadSettings();
                this.LoadSettings(this.generalSettingsRepository, this.moduleSettingsRepository);

                this.suppressViewModelUpdates = false;
            });
        }

        private void UpdateEnabledState(bool recommendedState)
        {
            ViewModel.RefreshEnabledState();
        }

        private async void SyncLocationButton_Click(object sender, RoutedEventArgs e)
        {
            this.LocationDialog.IsPrimaryButtonEnabled = false;
            this.LocationResultPanel.Visibility = Visibility.Collapsed;
            this.LocationErrorText.Visibility = Visibility.Collapsed;

            // Pre-check location services availability
            var accessStatus = await Geolocator.RequestAccessAsync();
            if (accessStatus != GeolocationAccessStatus.Allowed)
            {
                this.SyncButton.IsEnabled = false;
                this.LocationErrorText.Text = ResourceLoaderInstance.ResourceLoader.GetString("LightSwitch_LocationError_Unavailable");
                this.LocationErrorText.Visibility = Visibility.Visible;
            }
            else
            {
                this.SyncButton.IsEnabled = true;
            }

            await this.LocationDialog.ShowAsync();
        }

        private void CityAutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
        {
            if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput && !string.IsNullOrWhiteSpace(sender.Text))
            {
                string query = sender.Text.ToLower(CultureInfo.CurrentCulture);

                // Filter your cities (assuming ViewModel.Cities is a List<City>)
                var filtered = this.ViewModel.SearchLocations
                    .Where(c =>
                        (c.City?.Contains(query, StringComparison.CurrentCultureIgnoreCase) ?? false) ||
                        (c.Country?.Contains(query, StringComparison.CurrentCultureIgnoreCase) ?? false))
                    .ToList();

                sender.ItemsSource = filtered;
            }
        }

        /* private void CityAutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
        {
            if (args.SelectedItem is SearchLocation location)
            {
                ViewModel.SelectedCity = location;

                // CityAutoSuggestBox.Text = $"{location.City}, {location.Country}";
                LocationDialog.IsPrimaryButtonEnabled = true;
                LocationResultPanel.Visibility = Visibility.Visible;
            }
        } */

        private void ModeSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            switch (this.ViewModel.ScheduleMode)
            {
                case "FixedHours":
                    VisualStateManager.GoToState(this, "ManualState", true);
                    this.TimelineCard.Visibility = Visibility.Visible;
                    break;
                case "SunsetToSunrise":
                    VisualStateManager.GoToState(this, "SunsetToSunriseState", true);
                    this.SunriseModeChartState();
                    break;
                case "FollowNightLight":
                    VisualStateManager.GoToState(this, "FollowNightLightState", true);
                    TimelineCard.Visibility = Visibility.Collapsed;
                    break;
                default:
                    VisualStateManager.GoToState(this, "OffState", true);
                    this.TimelineCard.Visibility = Visibility.Collapsed;
                    break;
            }
        }

        private void OpenNightLightSettings_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Helpers.StartProcessHelper.Start(Helpers.StartProcessHelper.NightLightSettings);
            }
            catch (Exception ex)
            {
                Logger.LogError("Error while trying to open the system night light settings", ex);
            }
        }

        private void SunriseModeChartState()
        {
            if (this.ViewModel.Latitude != "0.0" && this.ViewModel.Longitude != "0.0")
            {
                this.TimelineCard.Visibility = Visibility.Visible;
                this.LocationWarningBar.Visibility = Visibility.Collapsed;
            }
            else
            {
                this.TimelineCard.Visibility = Visibility.Collapsed;
                this.LocationWarningBar.Visibility = Visibility.Visible;
            }
        }

        private void NavigatePowerDisplaySettings_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
        {
            ShellPage.Navigate(typeof(PowerDisplayPage));
        }
    }
}
