@page "/design/shared-scripts"
@using ScadaLink.Security
@using ScadaLink.Commons.Entities.Scripts
@using ScadaLink.Commons.Interfaces.Repositories
@using ScadaLink.TemplateEngine
@attribute [Authorize(Policy = AuthorizationPolicies.RequireDesign)]
@inject ITemplateEngineRepository TemplateEngineRepository
@inject SharedScriptService SharedScriptService
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavigationManager
@inject IDialogService Dialog

<div class="container-fluid mt-3">
    <div class="d-flex justify-content-between align-items-center mb-3">
        <h4 class="mb-0">Shared Scripts</h4>
        <button class="btn btn-primary btn-sm" @onclick='() => NavigationManager.NavigateTo("/design/shared-scripts/create")'>New Script</button>
    </div>

    <ToastNotification @ref="_toast" />

    @if (_loading)
    {
        <LoadingSpinner IsLoading="true" />
    }
    else if (_errorMessage != null)
    {
        <div class="alert alert-danger">@_errorMessage</div>
    }
    else if (_scripts.Count == 0)
    {
        <div class="text-center py-5 text-muted">
            <p class="mb-3">No shared scripts configured.</p>
            <button class="btn btn-primary btn-sm"
                    @onclick='() => NavigationManager.NavigateTo("/design/shared-scripts/create")'>
                Create your first script
            </button>
        </div>
    }
    else
    {
        <div class="mb-3" style="max-width: 320px;">
            <input class="form-control form-control-sm"
                   placeholder="Filter by name or code…"
                   @bind="_search" @bind:event="oninput" />
        </div>

        @if (!FilteredScripts.Any())
        {
            <p class="text-muted small">No shared scripts match the filter.</p>
        }

        <div class="row g-3">
            @foreach (var s in FilteredScripts)
            {
                var preview = s.Code.Length > 80
                    ? s.Code[..80] + "…"
                    : s.Code;
                var paramCount = CountSchemaProperties(s.ParameterDefinitions);
                var returnLabel = DescribeReturnType(s.ReturnDefinition);
                <div class="col-lg-6 col-12" @key="s.Id">
                    <div class="card h-100">
                        <div class="card-body">
                            <div class="d-flex justify-content-between align-items-start mb-2">
                                <h5 class="card-title mb-0">@s.Name</h5>
                                <div class="d-flex gap-1">
                                    <button class="btn btn-outline-primary btn-sm"
                                            @onclick='() => NavigationManager.NavigateTo($"/design/shared-scripts/{s.Id}/edit")'>
                                        Edit
                                    </button>
                                    <div class="dropdown">
                                        <button class="btn btn-outline-secondary btn-sm"
                                                data-bs-toggle="dropdown"
                                                aria-expanded="false"
                                                aria-label="@($"More actions for {s.Name}")">⋮</button>
                                        <ul class="dropdown-menu dropdown-menu-end">
                                            <li>
                                                <button class="dropdown-item text-danger"
                                                        @onclick="() => DeleteScript(s)">Delete</button>
                                            </li>
                                        </ul>
                                    </div>
                                </div>
                            </div>

                            <pre class="small text-muted font-monospace mb-2"
                                 style="white-space: pre-wrap; word-break: break-all;"
                                 title="@s.Code">@preview</pre>

                            <div>
                                <span class="badge bg-light text-dark me-1">@paramCount params</span>
                                <span class="badge bg-light text-dark">
                                    @(returnLabel == "void" ? "void" : $"returns {returnLabel}")
                                </span>
                            </div>
                        </div>
                    </div>
                </div>
            }
        </div>
    }
</div>

@code {
    // CentralUI-024: delegates to the shared helper so the claim type stays
    // resolved through JwtTokenService rather than a duplicated magic string.
    private Task<string> GetCurrentUserAsync()
        => AuthStateProvider.GetCurrentUsernameAsync();

    private List<SharedScript> _scripts = new();
    private bool _loading = true;
    private string? _errorMessage;
    private string _search = "";

    private ToastNotification _toast = default!;

    private IEnumerable<SharedScript> FilteredScripts =>
        string.IsNullOrWhiteSpace(_search)
            ? _scripts
            : _scripts.Where(s =>
                (s.Name?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false) ||
                (s.Code?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false));

    protected override async Task OnInitializedAsync()
    {
        await LoadDataAsync();
    }

    private async Task LoadDataAsync()
    {
        _loading = true;
        _errorMessage = null;
        try
        {
            _scripts = (await SharedScriptService.GetAllSharedScriptsAsync()).ToList();
        }
        catch (Exception ex)
        {
            _errorMessage = $"Failed to load shared scripts: {ex.Message}";
        }
        _loading = false;
    }

    private async Task DeleteScript(SharedScript script)
    {
        var confirmed = await Dialog.ConfirmAsync(
            "Delete Shared Script",
            $"Delete shared script '{script.Name}'?",
            danger: true);
        if (!confirmed) return;

        try
        {
            var user = await GetCurrentUserAsync();
            var result = await SharedScriptService.DeleteSharedScriptAsync(script.Id, user);
            if (result.IsSuccess)
            {
                _toast.ShowSuccess($"Script '{script.Name}' deleted.");
                await LoadDataAsync();
            }
            else
            {
                _toast.ShowError(result.Error);
            }
        }
        catch (Exception ex)
        {
            _toast.ShowError($"Delete failed: {ex.Message}");
        }
    }

    /// <summary>
    /// Counts the parameters declared in either format: a JSON Schema object
    /// (<c>{"type":"object","properties":{...}}</c>) or the legacy flat array.
    /// Returns 0 for null/empty/malformed input.
    /// </summary>
    private static int CountSchemaProperties(string? json)
    {
        if (string.IsNullOrWhiteSpace(json)) return 0;
        try
        {
            using var doc = System.Text.Json.JsonDocument.Parse(json);
            var root = doc.RootElement;
            if (root.ValueKind == System.Text.Json.JsonValueKind.Object
                && root.TryGetProperty("properties", out var props)
                && props.ValueKind == System.Text.Json.JsonValueKind.Object)
            {
                return props.EnumerateObject().Count();
            }
            if (root.ValueKind == System.Text.Json.JsonValueKind.Array)
                return root.GetArrayLength();
        }
        catch { /* fall through */ }
        return 0;
    }

    /// <summary>
    /// Produces a short human label for a script's return type from its JSON
    /// Schema definition: "string", "integer", "object", "string[]", etc.
    /// Treats null/empty/malformed input as "void".
    /// </summary>
    private static string DescribeReturnType(string? json)
    {
        if (string.IsNullOrWhiteSpace(json)) return "void";
        try
        {
            using var doc = System.Text.Json.JsonDocument.Parse(json);
            var root = doc.RootElement;
            if (root.ValueKind != System.Text.Json.JsonValueKind.Object) return "void";
            if (!root.TryGetProperty("type", out var typeEl)) return "object";
            var type = typeEl.GetString() ?? "object";
            if (type == "array"
                && root.TryGetProperty("items", out var items)
                && items.ValueKind == System.Text.Json.JsonValueKind.Object
                && items.TryGetProperty("type", out var itemTypeEl))
            {
                return $"{itemTypeEl.GetString() ?? "object"}[]";
            }
            return type;
        }
        catch { return "void"; }
    }
}
