@using System.Text.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using RobotNet.WebApp.Maps.Components.Editor.Element

@inject ISnackbar Snackbar
@inject IHttpClientFactory HttpClientFactory
@inject IDialogService Dialog

@foreach (var node in Models)
{
    <Node Model="node" ShowName="@ShowName" OnClick="NodeClickChanged" DoubleClick="OnNodeDoubleClick" EditorState="@EditorState" />
}

@foreach (var element in Elements)
{
    <Element Model="@element" ShowName="@ShowName" EditorState="EditorState" DoubleClick="ElementDoubleClick" />
}

<MudDialog @bind-Visible="updateNodeVisible">
    <TitleContent>
        <MudText Typo="Typo.h6">
            Update node @UpdateModel.Id
        </MudText>
    </TitleContent>
    <DialogContent>
        <div class="d-flex flex-row">
            <div class="d-flex flex-column">
                <div class="mb-2">
                    <MudTextField @bind-Value="UpdateModel.Name" Label="Name" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
                </div>
                <div class="mb-2">
                    <MudNumericField @bind-Value="UpdateModel.X" Label="X" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
                </div>
                <div class="mb-2">
                    <MudNumericField @bind-Value="UpdateModel.Y" Label="Y" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
                </div>
                <div class="mb-2">
                    <MudNumericField @bind-Value="UpdateModel.Theta" Label="Theta" AdornmentText="Degree" Max="180" Min="-180" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
                </div>
                <div class="mb-2">
                    <MudNumericField @bind-Value="UpdateModel.AllowedDeviationXy" Label="AllowedDeviationXy" Step="0.01" Min="0" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
                </div>
                <div>
                    <MudNumericField @bind-Value="UpdateModel.AllowedDeviationTheta" Label="AllowedDeviationTheta" Step="1" Min="0" Max="180" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
                </div>
            </div>
            <div class="d-flex flex-column ms-2 flex-grow-1">
                <div class="d-flex flex-row mb-5">
                    <MudSelect @bind-Value=ActionSelected Margin="Margin.Dense" T="ActionDto" Label="Action" Variant="Variant.Outlined" Class="w-100" AnchorOrigin="Origin.BottomLeft">
                        @foreach (var action in Actions)
                        {
                            <MudSelectItem Value="@action">@action.Name</MudSelectItem>
                        }
                    </MudSelect>
                    <MudFab Class="ms-2" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Add" Size="Size.Medium" OnClick="AddAction" />
                </div>
                <div class="paper-action">
                    @foreach (var actionId in UpdateModel.Actions)
                    {
                        <div class="m-1" style="height: fit-content">
                            <MudButton Variant="Variant.Filled" Color="Color.Info" Style="text-transform:none" OnClick="(() => DeleteAction(actionId))">
                                @(Actions.FirstOrDefault(ac => ac.Id == actionId)?.Name)
                            </MudButton>
                        </div>
                    }
                </div>
            </div>
        </div>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="(() => updateNodeVisible = false)" Class="px-10" Variant="Variant.Filled">Cancel</MudButton>
        <MudButton OnClick="UpdateNode" Variant="Variant.Filled" Color="Color.Primary" Class="px-10 me-4" Disabled=@(MapIsActive)>Update</MudButton>
    </DialogActions>
</MudDialog>

<MudDialog @bind-Visible="createElementVisible">
    <TitleContent>
        <MudText Typo="Typo.h6">
            Create Element Node @ElementCreateModel.NodeId
        </MudText>
    </TitleContent>
    <DialogContent>
        <div class="d-flex flex-column" style="max-height: 700px; overflow-y: auto">
            <MudTextField Class="my-2"
                          @bind-Value="ElementCreateModel.Name" T="string"
                          Label="Name" Required="true" RequiredError="Name is required!"
                          Counter="18" Variant="Variant.Outlined"
                          Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))" />
            <div class="d-flex flex-row mb-2">
                <MudNumericField Class="me-2" @bind-Value="ElementCreateModel.OffsetX" Label="Offset X" Step="0.1" Variant="Variant.Outlined" />
                <MudNumericField Class="ms-2" @bind-Value="ElementCreateModel.OffsetY" Label="Offset Y" Step="0.1" Variant="Variant.Outlined" />
            </div>
            <MudSelect Class="mb-2" T="ElementModelDto" Variant="Variant.Outlined" Label="Type" Required="true" RequiredError="Type is required!"
                       @bind-Value="@ElementModelSelected">
                @foreach (var model in ElementModels)
                {
                    <MudSelectItem Value="@model">@model.Name</MudSelectItem>
                }
            </MudSelect>
        </div>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="(() => createElementVisible = false)" Class="px-10" Variant="Variant.Filled">Cancel</MudButton>
        <MudButton OnClick="CreateElement" Variant="Variant.Filled" Color="Color.Primary" Class="px-10" Disabled=@(MapIsActive)>Create</MudButton>
    </DialogActions>
</MudDialog>

<MudDialog @bind-Visible="updateElementVisble">
    <TitleContent>
        <MudText Typo="Typo.h6">
            Update Element @ElementUpdateModel.Name
        </MudText>
    </TitleContent>
    <DialogContent>
        <div class="d-flex flex-row" style="max-height: 700px; overflow-y: auto; width: 600px">
            <div class="d-flex flex-column h-100 me-2" style="width: 47%">
                <MudTextField T="string" Label="Name" @bind-value="ElementUpdateModel.Name" Variant="Variant.Outlined" ReadOnly="@(MapIsActive)"
                              Required="true" RequiredError="Name is required!"
                              Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))" />
                <MudNumericField T="double" Label="Offset X" @bind-value="ElementUpdateModel.OffsetX" Variant="Variant.Outlined" Step="0.1" ReadOnly="@(MapIsActive)" />
                <MudNumericField T="double" Label="Offset Y" @bind-value="ElementUpdateModel.OffsetY" Variant="Variant.Outlined" Step="0.1" ReadOnly="@(MapIsActive)" />
                @* <MudCheckBox Size="Size.Small" T="bool" LabelPlacement="Placement.Start" Label="IsOpen" @bind-value="ElementUpdateModel.IsOpen" Color="Color.Success"
                             UncheckedColor="Color.Default" ReadOnly /> *@
            </div>
            <div class="paper-property-container">
                <div class="paper-property ms-2">
                    @foreach (var property in ElementProperties)
                    {
                        @if (property.Type == typeof(int).ToString())
                        {
                            if (int.TryParse(property.DefaultValue, out int value))
                            {
                                <MudNumericField T="int" Margin="Margin.Dense" Label="@property.Name" Value="value" Variant="Variant.Outlined" ReadOnly="@(MapIsActive)"
                                                 ValueChanged="((data) => DefaultIntValueChanged(property.Id, data))" />
                            }
                        }
                        else if (property.Type == typeof(double).ToString())
                        {
                            if (double.TryParse(property.DefaultValue, out double value))
                            {
                                <MudNumericField T="double" Margin="Margin.Dense" Label="@property.Name" Value="value" Variant="Variant.Outlined" Step="0.1" ReadOnly="@(MapIsActive)"
                                                 ValueChanged="((data) => DefaultDoubleValueChanged(property.Id, data))" />
                            }
                        }
                        else if (property.Type == typeof(bool).ToString())
                        {
                            if (bool.TryParse(property.DefaultValue, out bool value))
                            {
                                <MudCheckBox Class="m-0" Size="Size.Small" T="bool" LabelPlacement="Placement.Start" Label="@property.Name" Value="value" Color="Color.Success"
                                             UncheckedColor="Color.Default" ReadOnly="@(MapIsActive)" ValueChanged="((data) => DefaultBooleanValueChanged(property.Id, data))" />
                            }
                        }
                        else if (property.Type == typeof(string).ToString())
                        {
                            <MudTextField T="string" Label="@property.Name" Value="@property.DefaultValue" Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly="@(MapIsActive)"
                                          ValueChanged="((data) => property.DefaultValue = data)" />
                        }
                    }
                </div>
                <span class="paper-title">Properties</span>
            </div>
        </div>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="Delete" Variant="Variant.Filled" Color="Color.Secondary" Class="px-10" Disabled=@(MapIsActive)>Delete</MudButton>
        <MudButton OnClick="(() => updateElementVisble = false)" Class="px-10" Variant="Variant.Filled">Cancel</MudButton>
        <MudButton OnClick="UpdateElement" Variant="Variant.Filled" Color="Color.Primary" Class="px-10" Disabled=@(MapIsActive)>Update</MudButton>
    </DialogActions>
</MudDialog>

@code {
    [Parameter, EditorRequired]
    public MapNodeModel Models { get; set; } = null!;

    [Parameter]
    public MapElementModel Elements { get; set; } = [];

    [CascadingParameter]
    protected bool MapIsActive { get; set; }

    [Parameter]
    public bool ShowName { get; set; }

    [Parameter]
    public EditorState EditorState { get; set; }

    private NodeUpdateModel UpdateModel = new();
    private bool updateNodeVisible;

    private List<ActionDto> Actions = [];
    private ActionDto? ActionSelected = null;

    public List<ElementModelDto> ElementModels { get; set; } = [];
    private bool createElementVisible;
    private ElementCreateModel ElementCreateModel = new();
    private ElementModelDto? ElementModelSelected = null;
    private NodeModel? NodeModelDoubleClick = null;

    private bool updateElementVisble;
    private ElementUpdateModel ElementUpdateModel = new();
    private List<ElementProperty> ElementProperties = [];
    private HttpClient Http = default!;

    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);

        if (!firstRender) return;

        Http = HttpClientFactory.CreateClient("MapManagerAPI");
        Models.Changed += StateHasChanged;
        Elements.Changed += StateHasChanged;
    }

    private void NodeClickChanged(NodeModel? node) => Models.SelectedNode = node;

    private async Task LoadActionAsync(Guid mapId)
    {
        var result = await Http.GetFromJsonAsync<IEnumerable<ActionDto>>($"api/Actions/{mapId}");

        if (result is not null && result.Any())
        {
            Actions.Clear();
            Actions.AddRange(result);
        }
        if (Actions.Any()) ActionSelected = Actions.First();

        StateHasChanged();
    }

    private async Task LoadElementModels(Guid mapId)
    {
        ElementModels.Clear();

        var elModels = await Http.GetFromJsonAsync<MessageResult<IEnumerable<ElementModelDto>>>($"api/ElementModels/map/{mapId}");
        if (elModels is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
        else if (!elModels.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {elModels.Message}", Severity.Error);
        else if(elModels.Data is null || !elModels.Data.Any())
        {
            Snackbar.Add("Không có Element model nào trong bản đồ này", Severity.Warning);
            ElementModelSelected = null;
        }
        else
        {
            ElementModels.AddRange(elModels.Data.OrderBy(el => el.Name));
            if (ElementModels.Count > 0)
            {
                ElementModelSelected = ElementModelSelected is null ? ElementModels.First() : ElementModels.FirstOrDefault(em => em.Id == ElementModelSelected.Id) ?? ElementModels.First();
            }
        }
        StateHasChanged();
    }

    private void AddAction()
    {
        if (ActionSelected is null) return;
        if (UpdateModel.Actions.Any(action => action == ActionSelected.Id))
        {
            Snackbar.Add("Action đã tồn tại", Severity.Warning);
            return;
        }

        UpdateModel.Actions = [.. UpdateModel.Actions, ActionSelected.Id];
        StateHasChanged();
    }

    private void DeleteAction(Guid actionId)
    {
        UpdateModel.Actions = UpdateModel.Actions.Where(a => actionId != a).ToArray();
        StateHasChanged();
    }

    private async Task OnNodeDoubleClick(NodeModel model)
    {
        if (model == null || (EditorState != EditorState.NavigationEdit && EditorState != EditorState.View)) return;

        NodeModelDoubleClick = model;
        if (EditorState == EditorState.NavigationEdit)
        {
            await LoadActionAsync(model.MapId);

            UpdateModel.Id = model.Id;
            UpdateModel.Name = model.Name;
            UpdateModel.X = model.X;
            UpdateModel.Y = model.Y;
            UpdateModel.Theta = model.Theta;
            UpdateModel.AllowedDeviationXy = model.AllowedDeviationXy;
            UpdateModel.AllowedDeviationTheta = model.AllowedDeviationTheta;
            UpdateModel.Actions = [];
            var actions = JsonSerializer.Deserialize<Guid[]>(model.Actions ?? "");
            if (actions is not null && actions.Length > 0)
            {
                UpdateModel.Actions = [.. actions];
            }

            updateNodeVisible = true;
        }
        else if (EditorState == EditorState.View)
        {
            await LoadElementModels(model.MapId);

            ElementCreateModel.Name = "";
            ElementCreateModel.MapId = model.MapId;
            ElementCreateModel.NodeId = model.Id;

            createElementVisible = true;
        }
        StateHasChanged();
    }

    private async Task UpdateNode()
    {
        var selectedModel = Models.FirstOrDefault(e => e.Id == UpdateModel.Id);

        if (selectedModel == null)
        {
            updateNodeVisible = false;
            StateHasChanged();
            return;
        }

        var result = await (await Http.PutAsJsonAsync($"api/Nodes", UpdateModel)).Content.ReadFromJsonAsync<MessageResult>();
        if (result is null)
        {
            Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
            return;
        }
        else if (!result.IsSuccess)
        {
            Snackbar.Add(result.Message, Severity.Error);
            return;
        }

        selectedModel.UpdateData(UpdateModel);
        updateNodeVisible = false;
        Snackbar.Add("Cập nhật thành công", Severity.Success);
        StateHasChanged();
    }

    private async Task CreateElement()
    {
        try
        {
            if (NodeModelDoubleClick is null) return;
            if (ElementModelSelected is null)
            {
                Snackbar.Add("Hãy chọn Element model!", Severity.Warning);
                return;
            }
            if (string.IsNullOrEmpty(ElementCreateModel.Name))
            {
                Snackbar.Add("Name không được để trống.", Severity.Warning);
                return;
            }

            var nameInvalid = MapEditorHelper.NameChecking(ElementCreateModel.Name);
            if (!nameInvalid.IsSuccess)
            {
                Snackbar.Add(nameInvalid.returnStr, Severity.Warning);
                return;
            }

            ElementCreateModel.ModelId = ElementModelSelected.Id;

            var create = await (await Http.PostAsJsonAsync("api/Elements", ElementCreateModel)).Content.ReadFromJsonAsync<MessageResult<ElementDto>>();
            if (create is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
            else if (!create.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {create.Message}", Severity.Error);
            else if (create.Data is null) Snackbar.Add("Tạo Element không thành công", Severity.Error);
            else
            {

                Elements.Add(new(create.Data, NodeModelDoubleClick));
                createElementVisible = false;
                Snackbar.Add("Tạo Element thành công!", Severity.Success);
            }
            StateHasChanged();
        }
        catch (AccessTokenNotAvailableException ex)
        {
            ex.Redirect();
            return;
        }
    }

    private void ElementDoubleClick(ElementModel model)
    {
        ElementUpdateModel.Id = model.Id;
        ElementUpdateModel.Name = model.Name;
        ElementUpdateModel.IsOpen = model.IsOpen;
        ElementUpdateModel.OffsetX = model.OffsetX;
        ElementUpdateModel.OffsetY = model.OffsetY;
        ElementUpdateModel.Content = model.Content;

        if (model is not null && !string.IsNullOrEmpty(model.Content))
        {
            var properties = JsonSerializer.Deserialize<List<ElementProperty>>(model.Content, JsonOptionExtends.Read);
            if (properties is not null) ElementProperties = [.. properties];
        }

        updateElementVisble = true;
        StateHasChanged();
    }

    private async Task Delete()
    {
        var parameters = new DialogParameters<ConfirmDialog>
        {
            { x => x.Content, $"Bạn có chắc chắn muốn xóa element {ElementUpdateModel.Name} đi không?" },
            { x => x.ConfirmText, "Delete" },
            { x => x.Color, Color.Secondary }
        };
        var ConfirmDelete = await Dialog.ShowAsync<ConfirmDialog>("Xoá Element", parameters);
        var result = await ConfirmDelete.Result;
        if (result is not null && result.Data is not null && bool.TryParse(result.Data.ToString(), out bool data) && data)
        {
            var response = await Http.DeleteFromJsonAsync<MessageResult>($"api/Elements/{ElementUpdateModel.Id}");
            if (response is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
            else if (!response.IsSuccess) Snackbar.Add(response.Message ?? "Lỗi chưa xác định.", Severity.Error);
            else
            {
                var elementData = Elements.FirstOrDefault(m => m.Id == ElementUpdateModel.Id);
                if (elementData is not null)
                {
                    Elements.Remove(elementData);
                    Snackbar.Add($"Xóa element thành công.", Severity.Success);
                }
                else Snackbar.Add($"Element không tồn tại.", Severity.Warning);
            }

            updateElementVisble = false;
            StateHasChanged();
        }
    }

    private void DefaultIntValueChanged(Guid id, int value)
    {
        var property = ElementProperties.FirstOrDefault(p => p.Id == id);
        if (property is null) return;
        property.DefaultValue = value.ToString();
    }

    private void DefaultDoubleValueChanged(Guid id, double value)
    {
        var property = ElementProperties.FirstOrDefault(p => p.Id == id);
        if (property is null) return;
        property.DefaultValue = value.ToString();
    }

    private void DefaultBooleanValueChanged(Guid id, bool value)
    {
        var property = ElementProperties.FirstOrDefault(p => p.Id == id);
        if (property is null) return;
        property.DefaultValue = value.ToString();
    }

    private async Task UpdateElement()
    {
        var selectedElement = Elements.FirstOrDefault(e => e.Id == ElementUpdateModel.Id);
        if (selectedElement is null) return;

        var result = await (await Http.PutAsJsonAsync($"api/Elements", new ElementUpdateModel()
        {
            Id = ElementUpdateModel.Id,
            Name = ElementUpdateModel.Name,
            IsOpen = ElementUpdateModel.IsOpen,
            OffsetX = ElementUpdateModel.OffsetX,
            OffsetY = ElementUpdateModel.OffsetY,
            Content = JsonSerializer.Serialize(ElementProperties, JsonOptionExtends.Write),
        })).Content.ReadFromJsonAsync<MessageResult<ElementDto>>();
        if (result == null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
        else if (!result.IsSuccess) Snackbar.Add(result.Message, Severity.Error);
        else if (result.Data is null) Snackbar.Add("Lỗi dữ liệu", Severity.Error);
        else
        {
            selectedElement.UpdateElement(result.Data);
            Snackbar.Add("Cập nhật thành công", Severity.Success);
        }

        updateElementVisble = false;
        StateHasChanged();
    }

    public void Dispose()
    {
        Models.Changed -= StateHasChanged;
        Elements.Changed -= StateHasChanged;
        GC.SuppressFinalize(this);
    }
}
