{% extends "tesm_core/base.html" %}
{% from "tesm_core/macros.html" import alert, pill %}

{% set is_switch = target.kind == 'switch' %}
{% set back_url = url_for(target.back_endpoint, switch_id=target.id) if is_switch
                  else url_for(target.back_endpoint, device_id=target.id) %}

{% block page_title %}Konsole {{ target.label }}{% endblock %}
{% block page_sub %}{{ target.host }}:{{ target.port }} &middot; Sitzung wird mitgeschrieben{% endblock %}

{% block breadcrumb %}
<nav class="breadcrumb">
  <span><a href="{{ url_for('switches.overview') if is_switch else url_for('devices.overview') }}">{{ target.back_label }}</a></span>
  <span><a href="{{ back_url }}">{{ target.label }}</a></span>
  <span>Konsole</span>
</nav>
{% endblock %}

{% block content %}
{% if not host_key_ready %}
  {{ alert('Fuer dieses Ziel ist kein SSH-Host-Schluessel freigegeben. Die Konsole verweigert
  den Aufbau, bis das geschehen ist -- die Freigabe erfolgt auf der Detailseite.',
  'danger', 'Host-Schluessel fehlt') }}
  <p><a class="btn btn--primary" href="{{ back_url }}">Zur Detailseite und Schluessel freigeben</a></p>
{% endif %}

{{ alert('Die Konsole ist zeilenorientiert: ein Kommando pro Zeile, Antwort darunter. Jede
Eingabe und jede Ausgabe wird auf dem Server mitgeschrieben und im Aenderungsprotokoll
vermerkt. Bei Untaetigkeit trennt die Verbindung nach ' ~ idle_timeout ~ ' Sekunden.',
'warning', 'Mitschrift') }}

{% if not is_switch %}
  {{ alert('Jedes Kommando laeuft in einer eigenen Sitzung. Das heisst: ein "cd" wirkt nicht
  auf das naechste Kommando, und interaktive Programme (Editoren, Passwortabfragen) laufen
  hier nicht. Fuer solche Arbeiten einen richtigen SSH-Client nehmen.', 'info',
  'Ein Kommando, eine Sitzung') }}
{% endif %}

<section class="card">
  <div class="card__header">
    <div class="flex-1">
      <div class="card__title">
        {{ icon('terminal', size=17) }} {{ target.label }}
        <span id="console-state">{{ pill('verbinde ...', 'warning') }}</span>
      </div>
      <div class="card__sub" id="console-hint">Warte auf Verbindung</div>
    </div>
    <button class="btn btn--sm btn--ghost" type="button" data-copy="#console-output">
      {{ icon('copy', size=14) }} Mitschrift kopieren
    </button>
  </div>
  <pre class="log-view" id="console-output" style="max-height:56dvh"></pre>
  <div class="card__footer" style="justify-content:stretch;gap:var(--sp-2)">
    <select class="select" id="console-preset" style="max-width:280px">
      <option value="">Kommando aus der Liste ...</option>
      {% for command, label in presets %}
        <option value="{{ command }}">{{ label }}</option>
      {% endfor %}
    </select>
    <input class="input input--mono" id="console-input" placeholder="Kommando eingeben und Enter"
           autocomplete="off" spellcheck="false" style="flex:1" {% if not host_key_ready %}disabled{% endif %}>
    <button class="btn btn--primary" type="button" id="console-send"
            {% if not host_key_ready %}disabled{% endif %}>Senden</button>
  </div>
</section>

<p class="fs-xs faint">
  Pfeiltasten hoch/runter blättern durch die letzten Eingaben. <kbd>exit</kbd> beendet die Sitzung.
</p>
{% endblock %}

{% block scripts %}
<script type="module" nonce="{{ csp_nonce }}">
  const output = document.getElementById("console-output");
  const input = document.getElementById("console-input");
  const send = document.getElementById("console-send");
  const preset = document.getElementById("console-preset");
  const state = document.getElementById("console-state");
  const hint = document.getElementById("console-hint");
  const history = [];
  let historyIndex = -1;

  function append(text, kind = "") {
    const line = document.createElement("span");
    if (kind === "command") line.className = "accent-text";
    if (kind === "error") line.className = "danger-text";
    if (kind === "info") line.className = "warning-text";
    line.textContent = text.endsWith("\n") ? text : text + "\n";
    output.appendChild(line);
    output.scrollTop = output.scrollHeight;
  }

  function setState(label, kind) {
    state.innerHTML = "";
    const pill = document.createElement("span");
    pill.className = "pill" + (kind ? " pill--" + kind : "");
    pill.textContent = label;
    state.appendChild(pill);
  }

  const scheme = location.protocol === "https:" ? "wss:" : "ws:";
  const socket = new WebSocket(`${scheme}//${location.host}{{ websocket_path }}`);

  socket.addEventListener("open", () => {
    setState("verbunden", "online");
    hint.textContent = "Bereit";
  });
  socket.addEventListener("close", () => {
    setState("getrennt", "offline");
    hint.textContent = "Die Verbindung ist beendet.";
    input.disabled = true;
    send.disabled = true;
  });
  socket.addEventListener("error", () => {
    setState("Fehler", "danger");
  });
  socket.addEventListener("message", (event) => {
    let message;
    try {
      message = JSON.parse(event.data);
    } catch {
      return;
    }
    if (message.type === "output") append(message.data);
    else if (message.type === "error") { append(message.data, "error"); setState("Fehler", "danger"); }
    else if (message.type === "info") append(message.data, "info");
    else if (message.type === "ready") { append(message.data, "info"); }
    else if (message.type === "done") { hint.textContent = "Antwort in " + message.data; }
  });

  function submit() {
    const command = input.value.trim();
    if (!command || socket.readyState !== WebSocket.OPEN) return;
    append("$ " + command, "command");
    socket.send(JSON.stringify({ command }));
    history.push(command);
    historyIndex = history.length;
    input.value = "";
    hint.textContent = "Warte auf Antwort ...";
  }

  send.addEventListener("click", submit);
  input.addEventListener("keydown", (event) => {
    if (event.key === "Enter") { event.preventDefault(); submit(); }
    else if (event.key === "ArrowUp" && history.length) {
      event.preventDefault();
      historyIndex = Math.max(0, historyIndex - 1);
      input.value = history[historyIndex] ?? "";
    } else if (event.key === "ArrowDown" && history.length) {
      event.preventDefault();
      historyIndex = Math.min(history.length, historyIndex + 1);
      input.value = history[historyIndex] ?? "";
    }
  });
  preset.addEventListener("change", () => {
    if (preset.value) { input.value = preset.value; input.focus(); preset.value = ""; }
  });
  input.focus();
</script>
{% endblock %}
