@page "/serial"
@rendermode InteractiveServer
@implements IDisposable

@using IOModuleTestBlazor.Services
@inject ISerialPortService SerialService

<PageTitle>Serial Terminal</PageTitle>

<div class="container-fluid">
    <div class="row">
        <div class="col-12">
            <h1>Serial Terminal</h1>

            @* ── Connection Bar ──────────────────────────────────────────────────── *@
            <div class="card mb-3">
                <div class="card-body py-2">
                    <div class="d-flex align-items-center gap-2 flex-wrap">

                        @if (SerialService.IsOpen)
                        {
                            <span class="badge bg-success">● Connected</span>
                            <span class="font-monospace fw-semibold">@SerialService.PortName</span>
                            <span class="text-muted">115200 baud</span>
                        }
                        else
                        {
                            <span class="badge bg-danger">● Disconnected</span>
                        }

                        <select class="form-select form-select-sm" style="width: auto;"
                                @bind="_selectedPort" disabled="@SerialService.IsOpen">
                            @if (!_availablePorts.Any())
                            {
                                <option value="">No ports found</option>
                            }
                            @foreach (var port in _availablePorts)
                            {
                                <option value="@port">@port</option>
                            }
                        </select>

                        <button class="btn btn-sm btn-outline-secondary" @onclick="RefreshPorts"
                                disabled="@SerialService.IsOpen">
                            Refresh
                        </button>

                        @if (SerialService.IsOpen)
                        {
                            <button class="btn btn-sm btn-danger" @onclick="Disconnect">Disconnect</button>
                        }
                        else
                        {
                            <button class="btn btn-sm btn-success" @onclick="Connect"
                                    disabled="@string.IsNullOrEmpty(_selectedPort)">
                                Connect
                            </button>
                        }

                        @if (_connectError is not null)
                        {
                            <div class="w-100 mt-1">
                                <span class="badge bg-danger">Connect failed:</span>
                                <span class="text-danger small ms-1">@_connectError</span>
                            </div>
                        }
                    </div>
                </div>
            </div>

            @* ── Terminal Output ─────────────────────────────────────────────────── *@
            <div class="card mb-3">
                <div class="card-header d-flex justify-content-between align-items-center">
                    <h5 class="mb-0">Output</h5>
                    <button class="btn btn-sm btn-outline-secondary" @onclick="ClearTerminal">Clear</button>
                </div>
                <div class="card-body p-0">
                    <pre id="terminal-output"
                         class="mb-0 p-3"
                         style="height: 400px; overflow-y: auto; background: #1e1e1e; color: #d4d4d4; font-size: 0.85rem;">@GetTerminalText()</pre>
                </div>
            </div>

            @* ── Command Input ───────────────────────────────────────────────────── *@
            <div class="card mb-3">
                <div class="card-body">
                    <div class="d-flex gap-2">
                        <input class="form-control font-monospace"
                               placeholder="Enter command (e.g. r 0000 4)"
                               @bind="_commandInput"
                               @bind:event="oninput"
                               @onkeydown="OnKeyDown"
                               disabled="@(!SerialService.IsOpen)" />
                        <button class="btn btn-primary" @onclick="SendCommand"
                                disabled="@(!SerialService.IsOpen || string.IsNullOrWhiteSpace(_commandInput))">
                            Send
                        </button>
                    </div>

                    @* ── Quick Commands ──────────────────────────────────────────── *@
                    <div class="mt-2 d-flex gap-2 flex-wrap">
                        <span class="text-muted small align-self-center">Quick:</span>
                        @foreach (var (label, cmd) in _quickCommands)
                        {
                            <button class="btn btn-sm btn-outline-secondary font-monospace"
                                    @onclick="() => SendQuickCommand(cmd)"
                                    disabled="@(!SerialService.IsOpen)">
                                @label
                            </button>
                        }
                    </div>
                </div>
            </div>

        </div>
    </div>
</div>

@code {
    private string _selectedPort = string.Empty;
    private string _commandInput = string.Empty;
    private string? _connectError;
    private List<string> _availablePorts = new();
    private List<string> _displayLines = new();

    private static readonly (string Label, string Cmd)[] _quickCommands =
    [
        ("?",            "?"),
        ("r 0000 4",     "r 0000 4"),
        ("r 0000 10",    "r 0000 10"),
        ("w 0000 AB CD", "w 0000 AB CD"),
        ("w 0000 FF FF", "w 0000 FF FF"),
    ];

    protected override void OnInitialized()
    {
        RefreshPorts();
        SerialService.DataReceived += OnDataReceived;
        _displayLines = SerialService.GetLines().ToList();
    }

    private void RefreshPorts()
    {
        _availablePorts = SerialService.GetPortNames().ToList();
        if (_availablePorts.Count > 0 && string.IsNullOrEmpty(_selectedPort))
            _selectedPort = _availablePorts[0];
    }

    private async Task Connect()
    {
        _connectError = null;
        try
        {
            await Task.Run(() => SerialService.Open(_selectedPort, 115200));
        }
        catch (Exception ex)
        {
            _connectError = ex.Message;
        }
    }

    private async Task Disconnect()
    {
        await Task.Run(() => SerialService.Close());
    }

    private void SendCommand()
    {
        if (string.IsNullOrWhiteSpace(_commandInput)) return;
        SerialService.WriteLine(_commandInput.Trim());
        _commandInput = string.Empty;
        RefreshLines();
    }

    private void SendQuickCommand(string cmd)
    {
        SerialService.WriteLine(cmd);
        RefreshLines();
    }

    private void OnKeyDown(KeyboardEventArgs e)
    {
        if (e.Key == "Enter") SendCommand();
    }

    private void OnDataReceived()
    {
        InvokeAsync(() =>
        {
            RefreshLines();
            StateHasChanged();
        });
    }

    private void RefreshLines()
        => _displayLines = SerialService.GetLines().ToList();

    private void ClearTerminal()
    {
        _displayLines.Clear();
        StateHasChanged();
    }

    private string GetTerminalText()
        => string.Join("\n", _displayLines);

    public void Dispose()
        => SerialService.DataReceived -= OnDataReceived;
}
