using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Sonex.Client.Dialogs;
using Sonex.Client.Layouts;
using Sonex.Data.Records;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sonex.Client.Pages.Administrator.Accounts;

public sealed partial class Groups : Page
{
    private static readonly string[] CreateKeywords =
    [
        "create", "add", "insert", "new", "register", "grant"
    ];

    private static readonly string[] ReadKeywords =
    [
        "view", "read", "show", "list", "get", "open", "browse", "find", "search", "preview"
    ];

    private static readonly string[] UpdateKeywords =
    [
        "edit", "change", "update", "rename", "write", "modify", "set", "patch", "upsert"
    ];

    private static readonly string[] DeleteKeywords =
    [
        "delete", "remove", "drop", "clear", "erase", "purge", "revoke"
    ];

    private static readonly string[] ExecuteKeywords =
    [
        "execute", "exec", "run", "start", "trigger", "launch", "apply", "approve", "process"
    ];

    private const string GlyphCreate = "\uE710"; // Add
    private const string GlyphRead = "\uE890"; // View
    private const string GlyphUpdate = "\uE70F"; // Edit
    private const string GlyphDelete = "\uE74D"; // Delete
    private const string GlyphExecute = "\uE768"; // Play

    public ObservableCollection<ItemsAndContentListItem> RecordItems { get; } = new();

    public GroupRecord SelectedRecord { get; set; } = new();
    public string SelectedRecordDisplayName { get; set; } = string.Empty;

    // Widoczność panelu szczegółów po prawej stronie.
    public Visibility IsRecordSelected => _Layout.SelectedItemId != null
        ? Visibility.Visible
        : Visibility.Collapsed;

    private bool _isProgrammaticChange;
    public Groups()
    {
        InitializeComponent();
    }

    private async void Layout_Loaded(object sender, RoutedEventArgs e)
    {
        await LoadListAsync();
    }

    // Główne ładowanie listy (po starcie i po operacjach CRUD).
    public async Task LoadListAsync(long? preferredRecordId = null)
    {
        _isProgrammaticChange = true;

        try
        {
            var records = await GroupRecord.GetAll();

            await this.ShowMessageAsync(records);

            RecordItems.Clear();

            foreach (var record in records.Items.OrderBy(x => x.Name))
            {
                RecordItems.Add(CreateListItem(record));
            }

            long? selectedId = preferredRecordId;

            if (!selectedId.HasValue && SelectedRecord.Id > 0)
                selectedId = SelectedRecord.Id;

            if (!selectedId.HasValue)
                selectedId = RecordItems.FirstOrDefault()?.Id;

            _Layout.SelectedItemId = selectedId;

            if (selectedId.HasValue)
                await LoadSelectedRecordAsync(selectedId.Value);
            else
                ClearSelectedRecordView();
        }
        finally
        {
            _isProgrammaticChange = false;
        }
    }

    public async Task LoadSelectedRecordAsync(long recordId)
    {
        _isProgrammaticChange = true;

        try
        {
            var selectedListItem = RecordItems.FirstOrDefault(x => x.Id == recordId);
            if (selectedListItem == null || string.IsNullOrWhiteSpace(selectedListItem.Key))
            {
                ClearSelectedRecordView();
                return;
            }

            var record = await GroupRecord.Get(selectedListItem.Key);
            var allPermissions = await PermissionRecord.GetAll();

            await this.ShowMessageAsync(record);
            await this.ShowMessageAsync(allPermissions);

            if (record != null && record.Item != null)
            {
                SelectedRecord = record.Item;
                SelectedRecordDisplayName = SelectedRecord.DisplayName;
                PermissionsControl.ItemsSource = allPermissions.Success
                    ? BuildPermissionItems(allPermissions.Items, SelectedRecord.GrantedPermission)
                    : [];
                UpdateSelectedListItemFromSelectedRecord();
            }
            else
            {
                ClearSelectedRecordView();
            }

            Bindings.Update();
        }
        finally
        {
            _isProgrammaticChange = false;
        }
    }

    private void ClearSelectedRecordView()
    {
        SelectedRecord = new();
        SelectedRecordDisplayName = string.Empty;
        PermissionsControl.ItemsSource = null;
        Bindings.Update();
    }

    private static ItemsAndContentListItem CreateListItem(GroupRecord record)
    {
        return new ItemsAndContentListItem
        {
            Id = record.Id,
            Key = record.Name ?? string.Empty,
            Title = record.DisplayName ?? string.Empty,
            SearchText = BuildSearchText(record),
            Description = record.Description,
            IsEnabled = true,
            Picture = null
        };
    }

    private static string BuildSearchText(GroupRecord record)
    {
        return string.Join(" ",
        [
            record.Name ?? string.Empty,
            string.Join(' ',record.GrantedPermission ?? []),
            record.DisplayName ?? string.Empty, record.Description
        ]);
    }

    private void UpdateSelectedListItemFromSelectedRecord()
    {
        if (SelectedRecord == null || SelectedRecord.Id <= 0)
            return;

        var item = RecordItems.FirstOrDefault(x => x.Id == SelectedRecord.Id);
        if (item == null)
            return;

        item.Key = SelectedRecord.Name ?? string.Empty;
        item.Title = SelectedRecord.DisplayName ?? string.Empty;
        item.Description = SelectedRecord.Description ?? string.Empty;
        item.SearchText = BuildSearchText(SelectedRecord);
        item.IsEnabled = true;
    }

    private async void Layout_SelectedItemChanged(object? sender, SelectedItemChangedEventArgs e)
    {
        if (_isProgrammaticChange)
            return;

        if (!e.NewId.HasValue)
        {
            ClearSelectedRecordView();
            return;
        }

        await LoadSelectedRecordAsync(e.NewId.Value);
    }

    private async void Layout_AddClicked(object sender, RoutedEventArgs e)
    {
        var dialog = new CreateNewGroupDialog
        {
            XamlRoot = Content.XamlRoot
        };

        dialog.UpdateLayout();
        await dialog.ShowAsync();

        if (dialog.GroupId > -1)
            await LoadListAsync(dialog.GroupId);
    }
    private async void Layout_DeleteClicked(object sender, RoutedEventArgs e)
    {
        if (SelectedRecord == null || string.IsNullOrWhiteSpace(SelectedRecord.Name))
            return;

        var confirm = new ContentDialog
        {
            XamlRoot = Content.XamlRoot,
            Title = "Delete group",
            Content = $"Are you sure you want to delete group '{SelectedRecord.DisplayName}'?",
            PrimaryButtonText = "Delete",
            CloseButtonText = "Cancel",
            DefaultButton = ContentDialogButton.Close
        };

        var result = await confirm.ShowAsync();
        if (result != ContentDialogResult.Primary)
            return;

        var deleted = await SelectedRecord.Delete();
        await this.ShowMessageAsync(deleted);
        if (deleted.Success)
            await LoadListAsync();
    }

    private async void SaveBasicInformation_Click(object sender, RoutedEventArgs e)
    {
        if (SelectedRecord == null)
            return;

        var displayName = (SelectedRecordDisplayName ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(displayName))
        {
            await this.ShowMessageAsync("Error", "Group name cannot be empty.");
            return;
        }

        var newName = BuildGroupNameFromDisplayName(displayName);
        if (string.IsNullOrWhiteSpace(newName))
        {
            await this.ShowMessageAsync("Error", "Group name contains no valid characters.");
            return;
        }

        var previousName = SelectedRecord.Name;
        SelectedRecord.Name = newName;

        var result = await SelectedRecord.Save();

        await this.ShowMessageAsync(result);
        if (!result.Success)
        {
            SelectedRecord.Name = previousName;
            return;
        }

        SelectedRecordDisplayName = SelectedRecord.DisplayName;
        UpdateSelectedListItemFromSelectedRecord();
        Bindings.Update();
        await LoadListAsync(SelectedRecord.Id);
    }

    private static string BuildGroupNameFromDisplayName(string displayName)
    {
        var input = (displayName ?? string.Empty).Trim();
        if (input.StartsWith("group_", StringComparison.OrdinalIgnoreCase))
            input = input[6..];

        var sb = new StringBuilder();
        var previousWasUnderscore = false;

        foreach (var ch in input)
        {
            if (char.IsLetterOrDigit(ch))
            {
                sb.Append(char.ToLowerInvariant(ch));
                previousWasUnderscore = false;
                continue;
            }

            if (ch == '_' || char.IsWhiteSpace(ch) || ch == '-')
            {
                if (!previousWasUnderscore)
                {
                    sb.Append('_');
                    previousWasUnderscore = true;
                }
            }
        }

        var normalized = sb.ToString().Trim('_');
        if (string.IsNullOrWhiteSpace(normalized))
            return string.Empty;

        return $"group_{normalized}";
    }

    private async void PermissionsControl_ItemChecked(object sender, Controls.CheckListChangedEventArgs e)
    {
        if (_isProgrammaticChange)
            return;

        if (SelectedRecord == null || string.IsNullOrWhiteSpace(SelectedRecord.Name))
            return;

        var permissionName = e.Item?.Key;
        if (string.IsNullOrWhiteSpace(permissionName))
            return;

        var result = await SelectedRecord.AddPermission(permissionName);
        await this.ShowMessageAsync(result);

        if (!result.Success)
        {
            _isProgrammaticChange = true;
            try
            {
                e.CheckBox.IsChecked = false;
            }
            finally
            {
                _isProgrammaticChange = false;
            }

            return;
        }

        AddGrantedPermissionLocal(permissionName);
        UpdateSelectedListItemFromSelectedRecord();
        Bindings.Update();
    }

    private async void PermissionsControl_ItemUnchecked(object sender, Controls.CheckListChangedEventArgs e)
    {
        if (_isProgrammaticChange)
            return;

        if (SelectedRecord == null || string.IsNullOrWhiteSpace(SelectedRecord.Name))
            return;

        var permissionName = e.Item?.Key;
        if (string.IsNullOrWhiteSpace(permissionName))
            return;

        var result = await SelectedRecord.RemovePermission(permissionName);
        await this.ShowMessageAsync(result);

        if (!result.Success)
        {
            _isProgrammaticChange = true;
            try
            {
                e.CheckBox.IsChecked = true;
            }
            finally
            {
                _isProgrammaticChange = false;
            }

            return;
        }

        RemoveGrantedPermissionLocal(permissionName);
        UpdateSelectedListItemFromSelectedRecord();
        Bindings.Update();
    }

    private static List<Controls.CheckListItem> BuildPermissionItems(
        IEnumerable<PermissionRecord> allPermissions,
        IEnumerable<string>? grantedPermissions)
    {
        var grantedSet = new HashSet<string>(
            grantedPermissions ?? [],
            StringComparer.OrdinalIgnoreCase);

        return allPermissions
            .OrderBy(x => x.Name)
            .Select(x => new Controls.CheckListItem
            {
                Key = x.Name ?? string.Empty,
                Title = x.DisplayName,
                Description = x.Description ?? string.Empty,
                IconGlyph = ResolvePermissionGlyph(x.Name),
                Checked = !string.IsNullOrWhiteSpace(x.Name) && grantedSet.Contains(x.Name)
            })
            .ToList();
    }

    private void AddGrantedPermissionLocal(string permissionName)
    {
        var set = new HashSet<string>(
            SelectedRecord.GrantedPermission ?? [],
            StringComparer.OrdinalIgnoreCase);

        if (!set.Add(permissionName))
            return;

        SelectedRecord.GrantedPermission = set.OrderBy(x => x).ToArray();
    }

    private void RemoveGrantedPermissionLocal(string permissionName)
    {
        var set = new HashSet<string>(
            SelectedRecord.GrantedPermission ?? [],
            StringComparer.OrdinalIgnoreCase);

        if (!set.Remove(permissionName))
            return;

        SelectedRecord.GrantedPermission = set.OrderBy(x => x).ToArray();
    }

    private static string ResolvePermissionGlyph(string? permissionKey)
    {
        if (string.IsNullOrWhiteSpace(permissionKey))
            return string.Empty;

        var tokens = TokenizePermissionKey(permissionKey);

        if (ContainsAnyKeyword(tokens, DeleteKeywords))
            return GlyphDelete;

        if (ContainsAnyKeyword(tokens, CreateKeywords))
            return GlyphCreate;

        if (ContainsAnyKeyword(tokens, UpdateKeywords))
            return GlyphUpdate;

        if (ContainsAnyKeyword(tokens, ReadKeywords))
            return GlyphRead;

        if (ContainsAnyKeyword(tokens, ExecuteKeywords))
            return GlyphExecute;

        return string.Empty;
    }

    private static string[] TokenizePermissionKey(string permissionKey)
    {
        return permissionKey
            .ToLowerInvariant()
            .Split(['_', '-', '.', ':', '/', '\\', ' '], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
    }

    private static bool ContainsAnyKeyword(IEnumerable<string> tokens, IEnumerable<string> keywords)
    {
        foreach (var token in tokens)
        {
            foreach (var keyword in keywords)
            {
                if (token.Equals(keyword, StringComparison.OrdinalIgnoreCase))
                    return true;

                if (token.StartsWith(keyword, StringComparison.OrdinalIgnoreCase))
                    return true;
            }
        }

        return false;
    }
}
