using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Logger = Sonex.Client.Helpers.Logger;

namespace Sonex.Client.Controls.DataGridView;

// Odpowiedzialnosc pliku: eventy zmian stanu gridu (aktywna komorka/wiersz/zaznaczenie).

public sealed partial class DataGridView
{
    // Obiekt zdarzenia przekazywany przy zmianie aktywnej komorki, wiersza lub zaznaczenia.
    public sealed class DataGridStateChangedEventArgs : EventArgs
    {
        public DataGridStateChangedEventArgs(
            object? row,
            int rowIndex,
            Column? column,
            string source,
            int selectedRowsCount,
            int selectedColumnsCount)
        {
            Row = row;
            RowIndex = rowIndex;
            Column = column;
            Source = source;
            SelectedRowsCount = selectedRowsCount;
            SelectedColumnsCount = selectedColumnsCount;
        }

        public object? Row { get; }
        public int RowIndex { get; }
        public Column? Column { get; }
        public string Source { get; }
        public int SelectedRowsCount { get; }
        public int SelectedColumnsCount { get; }
    }

    public event EventHandler<DataGridStateChangedEventArgs>? OnChangeActiveCell;
    public event EventHandler<DataGridStateChangedEventArgs>? OnChangeActiveRow;
    public event EventHandler<DataGridStateChangedEventArgs>? OnChangeSelection;

    private bool _hasNotifiedGridState;
    private int _lastNotifiedActiveRowIndex = -1;
    private string _lastNotifiedActiveColumnName = string.Empty;
    private int _lastNotifiedActiveRowHash;
    private int _lastNotifiedSelectedRowsCount = -1;
    private int _lastNotifiedSelectedColumnsCount = -1;
    private int _lastNotifiedSelectionHash;

    private void NotifyGridStateChanged(string source, bool force = false)
    {
        try
        {
            var activeRowIndex = _ActiveCell?.RowIndex ?? -1;
            var activeColumn = _ActiveCell?.Header;
            var activeColumnName = activeColumn?.Name ?? string.Empty;
            var row = ResolveActiveRowItem(activeRowIndex);
            var rowHash = ComputeRowValueHash(row);

            var selectedRowsCount = _SelectedRows?.CountSelected() ?? 0;
            var selectedColumnsCount = _Columns.Count(c => c.isSelected);
            var selectionHash = ComputeSelectionHash();

            var hasRowChanged = force
                || !_hasNotifiedGridState
                || activeRowIndex != _lastNotifiedActiveRowIndex
                || rowHash != _lastNotifiedActiveRowHash;

            var hasCellChanged = force
                || !_hasNotifiedGridState
                || hasRowChanged
                || !string.Equals(activeColumnName, _lastNotifiedActiveColumnName, StringComparison.OrdinalIgnoreCase);

            var hasSelectionChanged = force
                || !_hasNotifiedGridState
                || selectedRowsCount != _lastNotifiedSelectedRowsCount
                || selectedColumnsCount != _lastNotifiedSelectedColumnsCount
                || selectionHash != _lastNotifiedSelectionHash;

            if (!hasRowChanged && !hasCellChanged && !hasSelectionChanged)
            {
                return;
            }

            var args = new DataGridStateChangedEventArgs(
                row: row,
                rowIndex: activeRowIndex,
                column: activeColumn,
                source: source,
                selectedRowsCount: selectedRowsCount,
                selectedColumnsCount: selectedColumnsCount);

            if (hasRowChanged)
            {
                OnChangeActiveRow?.Invoke(this, args);
                //if(this.FocusState == Microsoft.UI.Xaml.FocusState.Unfocused)
                    _Canvas.Focus(Microsoft.UI.Xaml.FocusState.Pointer);
            }

            if (hasCellChanged)
            {
                OnChangeActiveCell?.Invoke(this, args);
            }

            if (hasSelectionChanged)
            {
                OnChangeSelection?.Invoke(this, args);
            }

            _hasNotifiedGridState = true;
            _lastNotifiedActiveRowIndex = activeRowIndex;
            _lastNotifiedActiveColumnName = activeColumnName;
            _lastNotifiedActiveRowHash = rowHash;
            _lastNotifiedSelectedRowsCount = selectedRowsCount;
            _lastNotifiedSelectedColumnsCount = selectedColumnsCount;
            _lastNotifiedSelectionHash = selectionHash;
        }
        catch (Exception ex)
        {
            Logger.Error(
                ex,
                source: nameof(DataGridView),
                operation: "Events.NotifyGridStateChanged");
        }
    }

    private void CaptureGridStateSnapshot()
    {
        try
        {
            _lastNotifiedActiveRowIndex = _ActiveCell?.RowIndex ?? -1;
            _lastNotifiedActiveColumnName = _ActiveCell?.Header?.Name ?? string.Empty;
            _lastNotifiedActiveRowHash = ComputeRowValueHash(ResolveActiveRowItem(_lastNotifiedActiveRowIndex));
            _lastNotifiedSelectedRowsCount = _SelectedRows?.CountSelected() ?? 0;
            _lastNotifiedSelectedColumnsCount = _Columns.Count(c => c.isSelected);
            _lastNotifiedSelectionHash = ComputeSelectionHash();
            _hasNotifiedGridState = true;
        }
        catch (Exception ex)
        {
            Logger.Error(
                ex,
                source: nameof(DataGridView),
                operation: "Events.CaptureGridStateSnapshot");
        }
    }

    private int ComputeSelectionHash()
    {
        unchecked
        {
            var hash = 17;

            if (_SelectedRows != null)
            {
                var sampleCount = 0;
                foreach (var rowIndex in _SelectedRows)
                {
                    hash = (hash * 31) ^ rowIndex;
                    sampleCount++;
                    if (sampleCount >= 4096)
                    {
                        break;
                    }
                }

                hash = (hash * 31) ^ _SelectedRows.CountSelected();
            }

            foreach (var column in _Columns.Where(c => c.isSelected).OrderBy(c => c.Index))
            {
                hash = (hash * 31) ^ StringComparer.OrdinalIgnoreCase.GetHashCode(column.Name ?? string.Empty);
            }

            return hash;
        }
    }

    private static int ComputeRowValueHash(object? row)
    {
        if (row == null)
        {
            return 0;
        }

        unchecked
        {
            var hash = 17;
            var type = row.GetType();
            hash = (hash * 31) ^ type.GetHashCode();

            foreach (var property in type
                .GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
                .Where(p => p.GetIndexParameters().Length == 0)
                .OrderBy(p => p.Name, StringComparer.Ordinal))
            {
                object? value;
                try
                {
                    value = property.GetValue(row);
                }
                catch
                {
                    continue;
                }

                hash = (hash * 31) ^ StringComparer.Ordinal.GetHashCode(property.Name);
                hash = (hash * 31) ^ (value?.GetHashCode() ?? 0);
            }

            return hash;
        }
    }

    private object? ResolveActiveRowItem(int rowIndex)
    {
        if (rowIndex < 0)
        {
            return null;
        }

        var source = _ItemsSourceFiltred ?? _ItemsSource ?? ItemsSource;
        if (source is not IList list || rowIndex >= list.Count)
        {
            return null;
        }

        return list[rowIndex];
    }

    public List<object> GetSelectedItems()
    {
        var result = new List<object>();
        var source = _ItemsSourceFiltred ?? _ItemsSource ?? ItemsSource;

        if (source is not IList list || _SelectedRows == null || _SelectedRows.CountSelected() == 0)
            return result;

        foreach (var rowIndex in _SelectedRows)
        {
            if (rowIndex < 0 || rowIndex >= list.Count)
                continue;

            var item = list[rowIndex];
            if (item != null)
                result.Add(item);
        }

        return result;
    }

    public List<T> GetSelectedItems<T>()
    {
        return GetSelectedItems()
            .OfType<T>()
            .ToList();
    }

    private void NormalizeActiveCellForCurrentView()
    {
        try
        {
            var rowCount = _ItemsSourceFiltred?.Count ?? 0;
            if (rowCount <= 0)
            {
                _ActiveCell = null;
                return;
            }

            if (_ActiveCell == null)
            {
                return;
            }

            var visibleColumns = _Columns
                .Where(c => !c.IsHiden)
                .OrderBy(c => c.Index)
                .ToList();
            if (visibleColumns.Count == 0)
            {
                _ActiveCell = null;
                return;
            }

            var rowIndex = Math.Clamp(_ActiveCell.RowIndex, 0, rowCount - 1);
            var column = _ActiveCell.Header;
            if (column == null || column.IsHiden || !_Columns.Contains(column))
            {
                column = visibleColumns[0];
            }

            if (!ReferenceEquals(column, _ActiveCell.Header) || rowIndex != _ActiveCell.RowIndex)
            {
                _ActiveCell = new CellVector(column, rowIndex);
            }
        }
        catch (Exception ex)
        {
            Logger.Error(
                ex,
                source: nameof(DataGridView),
                operation: "Events.NormalizeActiveCellForCurrentView");
        }
    }
}
