@page "/design/api-methods/create"
@page "/design/api-methods/{Id:int}/edit"
@using ScadaLink.Security
@using ScadaLink.Commons.Entities.InboundApi
@using ScadaLink.Commons.Interfaces.Repositories
@attribute [Authorize(Policy = AuthorizationPolicies.RequireDesign)]
@inject IInboundApiRepository InboundApiRepository
@inject NavigationManager NavigationManager

<div class="container-fluid mt-3">
    <button class="btn btn-link text-decoration-none ps-0 mb-2" @onclick="GoBack">&larr; Back</button>

    <h4 class="mb-3">@(Id.HasValue ? "Edit API Method" : "Add API Method")</h4>

    @if (_loading)
    {
        <LoadingSpinner IsLoading="true" />
    }
    else
    {
        <div class="card">
            <div class="card-body">
                <div class="mb-3">
                    <label class="form-label">Name</label>
                    <input type="text" class="form-control" @bind="_name" disabled="@Id.HasValue" />
                </div>
                <div class="mb-3">
                    <label class="form-label">Timeout (seconds)</label>
                    <input type="number" class="form-control" @bind="_timeoutSeconds" min="1" />
                </div>
                <div class="mb-3">
                    <label class="form-label">Approved API Keys</label>
                    @if (_allKeys.Count == 0)
                    {
                        <div class="form-text">
                            No API keys configured.
                            <a href="/admin/api-keys">Create one</a> to authorize callers for this method.
                        </div>
                    }
                    else
                    {
                        <div class="border rounded p-2" style="max-height: 220px; overflow-y: auto;">
                            @foreach (var key in _allKeys)
                            {
                                var checkboxId = $"approved-key-{key.Id}";
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" id="@checkboxId"
                                           checked="@_selectedKeyIds.Contains(key.Id)"
                                           @onchange="e => ToggleKey(key.Id, (bool)e.Value!)" />
                                    <label class="form-check-label" for="@checkboxId">
                                        @key.Name
                                        @if (!key.IsEnabled)
                                        {
                                            <span class="badge bg-secondary ms-1">Disabled</span>
                                        }
                                    </label>
                                </div>
                            }
                        </div>
                        <div class="form-text">
                            Callers must present a checked key in the <code>X-API-Key</code> header to invoke this method.
                        </div>
                    }
                </div>
                <div class="mb-3">
                    <label class="form-label">Parameters</label>
                    <SchemaBuilder Mode="object"
                                   Value="@_params"
                                   ValueChanged="@(v => _params = v)" />
                </div>
                <div class="mb-3">
                    <label class="form-label">Return value</label>
                    <SchemaBuilder Mode="value"
                                   Value="@_returns"
                                   ValueChanged="@(v => _returns = v)" />
                </div>
                <div class="mb-3">
                    <label class="form-label">Script</label>
                    <MonacoEditor @ref="_editor" Value="@_script" ValueChanged="@(v => _script = v)"
                                  Language="csharp" Height="320px"
                                  DeclaredParameters="@ScriptParameterNames.Parse(_params)"
                                  DeclaredParameterShapes="@ScriptParameterNames.ParseShapes(_params)"
                                  MarkersChanged="@(m => { _markers = m; StateHasChanged(); })" />
                    <ProblemsPanel Markers="@_markers" OnNavigate="@(m => _editor?.RevealLineAsync(m.StartLineNumber, m.StartColumn) ?? Task.CompletedTask)" />
                </div>

                @if (_formError != null)
                {
                    <div class="text-danger small mb-2">@_formError</div>
                }

                <div class="d-flex gap-2">
                    <button class="btn btn-success" @onclick="Save">Save</button>
                    <button class="btn btn-outline-secondary" @onclick="GoBack">Cancel</button>
                </div>
            </div>
        </div>
    }
</div>

@code {
    [Parameter] public int? Id { get; set; }

    private bool _loading = true;
    private string _name = "", _script = "";
    private int _timeoutSeconds = 30;
    private string? _params, _returns;
    private string? _formError;
    private MonacoEditor? _editor;
    private IReadOnlyList<ScadaLink.CentralUI.ScriptAnalysis.DiagnosticMarker> _markers
        = Array.Empty<ScadaLink.CentralUI.ScriptAnalysis.DiagnosticMarker>();

    private ApiMethod? _existing;
    private List<ApiKey> _allKeys = new();
    private HashSet<int> _selectedKeyIds = new();

    protected override async Task OnInitializedAsync()
    {
        try
        {
            _allKeys = (await InboundApiRepository.GetAllApiKeysAsync()).ToList();
        }
        catch (Exception ex) { _formError = ex.Message; }

        if (Id.HasValue)
        {
            try
            {
                _existing = await InboundApiRepository.GetApiMethodByIdAsync(Id.Value);
                if (_existing != null)
                {
                    _name = _existing.Name;
                    _script = _existing.Script;
                    _timeoutSeconds = _existing.TimeoutSeconds;
                    _params = _existing.ParameterDefinitions;
                    _returns = _existing.ReturnDefinition;
                    _selectedKeyIds = ParseApprovedKeyIds(_existing.ApprovedApiKeyIds);
                }
            }
            catch (Exception ex) { _formError = ex.Message; }
        }
        _loading = false;
    }

    private static HashSet<int> ParseApprovedKeyIds(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
            return new HashSet<int>();
        return value.Split(',', StringSplitOptions.RemoveEmptyEntries)
            .Select(s => int.TryParse(s.Trim(), out var id) ? id : -1)
            .Where(id => id > 0)
            .ToHashSet();
    }

    private void ToggleKey(int keyId, bool isChecked)
    {
        if (isChecked) _selectedKeyIds.Add(keyId);
        else _selectedKeyIds.Remove(keyId);
    }

    private string? SerializeApprovedKeyIds() =>
        _selectedKeyIds.Count == 0 ? null : string.Join(",", _selectedKeyIds.OrderBy(id => id));

    private async Task Save()
    {
        _formError = null;
        if (string.IsNullOrWhiteSpace(_name) || string.IsNullOrWhiteSpace(_script))
        {
            _formError = "Name and script required.";
            return;
        }

        try
        {
            var approvedKeyIds = SerializeApprovedKeyIds();
            if (_existing != null)
            {
                _existing.Script = _script;
                _existing.TimeoutSeconds = _timeoutSeconds;
                _existing.ParameterDefinitions = _params?.Trim();
                _existing.ReturnDefinition = _returns?.Trim();
                _existing.ApprovedApiKeyIds = approvedKeyIds;
                await InboundApiRepository.UpdateApiMethodAsync(_existing);
            }
            else
            {
                var m = new ApiMethod(_name.Trim(), _script)
                {
                    TimeoutSeconds = _timeoutSeconds,
                    ParameterDefinitions = _params?.Trim(),
                    ReturnDefinition = _returns?.Trim(),
                    ApprovedApiKeyIds = approvedKeyIds
                };
                await InboundApiRepository.AddApiMethodAsync(m);
            }
            await InboundApiRepository.SaveChangesAsync();
            NavigationManager.NavigateTo("/design/external-systems");
        }
        catch (Exception ex) { _formError = ex.Message; }
    }

    private void GoBack() => NavigationManager.NavigateTo("/design/external-systems");
}
