using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Linq;
using System.Windows.Controls;
using System.Windows;
using CtrEditor.ObjetosSim;
using LibS7Adv;

namespace CtrEditor
{
    /// <summary>
    /// Metadata de una página, incluyendo información sobre la imagen y configuración
    /// </summary>
    public class PageMetadata
    {
        public string PageId { get; set; }
        public string PageName { get; set; }
        public string ImagePath { get; set; }
        public DateTime LastModified { get; set; }
        public Dictionary<string, object> CustomMetadata { get; set; } = new();
    }

    /// <summary>
    /// Estado de un objeto dentro de una página específica
    /// </summary>
    public class PageObjectState
    {
        public string GlobalObjectId { get; set; }
        public bool IsVisible { get; set; } = true;
        public float Left { get; set; }
        public float Top { get; set; }
        public float Rotation { get; set; }
        public Dictionary<string, object> CustomProperties { get; set; } = new();
    }

    /// <summary>
    /// Estado de una página específica
    /// </summary>
    public class PageState
    {
        public string PageId { get; set; }
        public string PageName { get; set; }
        public List<osBase> LocalObjects { get; set; } = new();
        public Dictionary<string, PageObjectState> GlobalObjectStates { get; set; } = new();
        public Dictionary<string, object> PageMetadata { get; set; } = new();
    }

    /// <summary>
    /// Estado global de la aplicación
    /// </summary>
    public class GlobalState
    {
        public List<osBase> SharedObjects { get; set; } = new();
        public Dictionary<string, PageMetadata> PagesMetadata { get; set; } = new();
        public PLCViewModel PLCConfiguration { get; set; }
        public UnitConverter UnitConverter { get; set; }
        public int LastUsedId { get; set; }
    }

    /// <summary>
    /// Gestor principal de estados de la aplicación
    /// </summary>
    public class StateManager : IDisposable
    {
        private readonly string _basePath;
        private GlobalState _globalState;
        private Dictionary<string, PageState> _loadedPages;
        private string _currentPageId;
        private readonly object _lockObject = new();
        private bool _hasUnsavedChanges;
        private MainViewModel _mainViewModel;

        public event EventHandler<string> PageStateChanged;
        public event EventHandler GlobalStateChanged;

        public StateManager(string basePath, MainViewModel mainViewModel)
        {
            _basePath = basePath;
            _mainViewModel = mainViewModel;
            _loadedPages = new Dictionary<string, PageState>();
            Directory.CreateDirectory(basePath);
        }

        public async Task InitializeAsync()
        {
            await LoadGlobalStateAsync();
            if (_mainViewModel.SelectedImage != null)
            {
                await LoadPageStateAsync(_mainViewModel.SelectedImage);
            }
        }

        private JsonSerializerSettings GetSerializerSettings()
        {
            return new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto,
                ObjectCreationHandling = ObjectCreationHandling.Replace,
                Formatting = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore
            };
        }

        public async Task LoadGlobalStateAsync()
        {
            var path = Path.Combine(_basePath, "global.json");
            if (File.Exists(path))
            {
                try
                {
                    var json = await File.ReadAllTextAsync(path);
                    _globalState = JsonConvert.DeserializeObject<GlobalState>(json, GetSerializerSettings());

                    // Restaurar configuración global
                    _mainViewModel.PLCViewModel = _globalState.PLCConfiguration ?? new PLCViewModel();
                    if (_globalState.UnitConverter != null)
                        PixelToMeter.Instance.calc = _globalState.UnitConverter;
                    // No crear un nuevo UnitConverter si no hay uno guardado, mantener el actual

                    // Restaurar objetos globales
                    foreach (var obj in _globalState.SharedObjects)
                    {
                        await RestoreObjectAsync(obj);
                    }
                }
                catch (Exception ex)
                {
                    // Log error y crear nuevo estado global
                    _globalState = new GlobalState();
                }
            }
            else
            {
                _globalState = new GlobalState();
            }
        }

        public async Task SaveGlobalStateAsync()
        {
            var path = Path.Combine(_basePath, "global.json");
            var backupPath = Path.ChangeExtension(path, ".bak");

            // Actualizar estado global
            _globalState.PLCConfiguration = _mainViewModel.PLCViewModel;
            _globalState.UnitConverter = PixelToMeter.Instance.calc;

            // Crear backup
            if (File.Exists(path))
            {
                File.Copy(path, backupPath, true);
            }

            try
            {
                // Preparar objetos para serialización
                foreach (var obj in _globalState.SharedObjects)
                {
                    obj.SalvarDatosNoSerializables();
                }

                var json = JsonConvert.SerializeObject(_globalState, GetSerializerSettings());
                await File.WriteAllTextAsync(path, json);
                _hasUnsavedChanges = false;
                GlobalStateChanged?.Invoke(this, EventArgs.Empty);
            }
            finally
            {
                // Restaurar estado de objetos
                foreach (var obj in _globalState.SharedObjects)
                {
                    obj.RestaurarDatosNoSerializables();
                }
            }
        }

        public async Task<PageState> LoadPageStateAsync(string pageId)
        {
            // Retornar página cacheada si existe
            if (_loadedPages.TryGetValue(pageId, out var cachedState))
            {
                return cachedState;
            }

            var path = Path.Combine(_basePath, $"page_{pageId}.json");
            try
            {
                PageState pageState;
                if (File.Exists(path))
                {
                    var json = await File.ReadAllTextAsync(path);
                    pageState = JsonConvert.DeserializeObject<PageState>(json, GetSerializerSettings());
                }
                else
                {
                    pageState = new PageState
                    {
                        PageId = pageId,
                        PageName = _globalState.PagesMetadata.GetValueOrDefault(pageId)?.PageName ?? pageId
                    };
                }

                _loadedPages[pageId] = pageState;

                // Restaurar objetos locales
                foreach (var obj in pageState.LocalObjects)
                {
                    await RestoreObjectAsync(obj);
                }

                // Aplicar estados de objetos globales
                ApplyGlobalObjectStates(pageState);

                return pageState;
            }
            catch (Exception ex)
            {
                // Log error
                throw;
            }
        }

        private void ApplyGlobalObjectStates(PageState pageState)
        {
            foreach (var kvp in pageState.GlobalObjectStates)
            {
                var globalObj = _globalState.SharedObjects.FirstOrDefault(o => o.Id.Value.ToString() == kvp.Key);
                if (globalObj != null)
                {
                    var state = kvp.Value;
                    globalObj.Left = state.Left;
                    globalObj.Top = state.Top;
                    globalObj.Angulo = state.Rotation;

                    // Actualizar Show_On_This_Page que ahora maneja internamente showOnThisPagesList
                    if (state.IsVisible && !globalObj.Show_On_This_Page)
                        globalObj.Show_On_This_Page = true;
                    else if (!state.IsVisible && globalObj.Show_On_This_Page)
                        globalObj.Show_On_This_Page = false;

                    // Aplicar propiedades personalizadas
                    foreach (var prop in state.CustomProperties)
                    {
                        var property = globalObj.GetType().GetProperty(prop.Key);
                        if (property != null && property.CanWrite)
                        {
                            property.SetValue(globalObj, prop.Value);
                        }
                    }
                }
            }
        }

        private async Task RestoreObjectAsync(osBase obj)
        {
            if (obj != null)
            {
                obj.CheckData();
                await Task.Run(() =>
                {
                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        CrearUserControlDesdeObjetoSimulable(obj);
                    });
                });
            }
        }

        private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto)
        {
            Type tipoObjeto = osObjeto.GetType();
            UserControl userControl = UserControlFactory.GetControlForType(tipoObjeto);

            if (userControl != null)
            {
                UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager);
                osObjeto._mainViewModel = _mainViewModel;

                if (osObjeto.Id == null)
                {
                    osObjeto.Id = new UniqueId().ObtenerNuevaID();
                }

                _mainViewModel.MainWindow.AgregarRegistrarUserControlCanvas(userControl);
                return true;
            }
            return false;
        }

        public async Task SavePageStateAsync(string pageId)
        {
            if (!_loadedPages.TryGetValue(pageId, out var pageState))
                return;

            var path = Path.Combine(_basePath, $"page_{pageId}.json");
            var backupPath = Path.ChangeExtension(path, ".bak");

            // Crear backup
            if (File.Exists(path))
            {
                File.Copy(path, backupPath, true);
            }

            // Actualizar estado de objetos globales
            pageState.GlobalObjectStates.Clear();
            foreach (var obj in _mainViewModel.ObjetosSimulables.Where(o => o.Enable_On_All_Pages))
            {
                var currentPageId = _mainViewModel.SelectedImage;
                pageState.GlobalObjectStates[obj.Id.Value.ToString()] = new PageObjectState
                {
                    GlobalObjectId = obj.Id.Value.ToString(),
                    IsVisible = obj.Show_On_This_Page,
                    Left = obj.Left,
                    Top = obj.Top,
                    Rotation = obj.Angulo,
                    CustomProperties = CaptureCustomProperties(obj)
                };
            }

            try
            {
                // Preparar objetos para serialización
                foreach (var obj in pageState.LocalObjects)
                {
                    obj.SalvarDatosNoSerializables();
                }

                var json = JsonConvert.SerializeObject(pageState, GetSerializerSettings());
                await File.WriteAllTextAsync(path, json);
                _hasUnsavedChanges = false;
                PageStateChanged?.Invoke(this, pageId);
            }
            finally
            {
                // Restaurar estado de objetos
                foreach (var obj in pageState.LocalObjects)
                {
                    obj.RestaurarDatosNoSerializables();
                }
            }
        }

        private Dictionary<string, object> CaptureCustomProperties(osBase obj)
        {
            var customProps = new Dictionary<string, object>();
            var properties = obj.GetType().GetProperties()
                .Where(p => p.GetCustomAttributes(typeof(SerializeAttribute), true).Any());

            foreach (var prop in properties)
            {
                customProps[prop.Name] = prop.GetValue(obj);
            }

            return customProps;
        }

        public async Task SaveAllAsync()
        {
            foreach (var pageId in _loadedPages.Keys)
            {
                await SavePageStateAsync(pageId);
            }
            await SaveGlobalStateAsync();
        }

        public void Dispose()
        {
            if (_hasUnsavedChanges)
            {
                SaveAllAsync().Wait();
            }
        }
    }

    /// <summary>
    /// Atributo para marcar propiedades que deben ser serializadas como propiedades personalizadas
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class SerializeAttribute : Attribute { }
}