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

{% block page_title %}DHCP-Server{% endblock %}
{% block page_sub %}
  Kea &middot; {{ subnets | length }} Subnetze &middot; {{ reservations | length }} Reservierungen
{% endblock %}

{% block content %}
{% for warning in warnings %}{{ alert(warning, 'warning') }}{% endfor %}
{% if not sysops_available %}
  {{ alert('Systemhelfer nicht verfuegbar -- Konfiguration und Dienstzustand koennen hier nur
  angezeigt, nicht geschrieben werden.', 'warning') }}
{% endif %}

<div class="grid grid--4">
  <div class="stat {{ 'stat--ok' if service.get('active') == 'active' else 'stat--warning' }}">
    <span class="stat__label">Dienst</span>
    <span class="stat__value fs-lg">{{ service.get('active', 'unbekannt') }}</span>
    <span class="stat__hint">Autostart: {{ service.get('enabled', '?') }}</span>
  </div>
  <div class="stat">
    <span class="stat__label">Programm</span>
    <span class="stat__value fs-lg">{{ service.get('binary', '?') }}</span>
    <span class="stat__hint">kea-dhcp4</span>
  </div>
  <div class="stat">
    <span class="stat__label">Reservierungen</span>
    <span class="stat__value">{{ reservations | length }}</span>
    <span class="stat__hint">automatisch + manuell</span>
  </div>
  <div class="stat">
    <span class="stat__label">Leases</span>
    <span class="stat__value">{{ leases | rejectattr('is_expired') | list | length }}</span>
    <span class="stat__hint">
      gueltig{% if leases | selectattr('is_expired') | list %},
      {{ leases | selectattr('is_expired') | list | length }} abgelaufen{% endif %}
    </span>
  </div>
</div>

{% if current_user.has('dhcp.execute') %}
  <section class="card">
    <div class="card__header">
      <div class="flex-1">
        <div class="card__title">{{ icon('power', size=17) }} Dienst</div>
        <div class="card__sub">
          Bewusst getrennt vom Speichern: eine abgelegte Konfiguration startet keinen Dienst.
        </div>
      </div>
    </div>
    <div class="card__body row row--tight">
      {% for action, label, style, question in [
          ('install', 'Kea installieren', '', 'Kea-DHCP-Server per apt installieren?'),
          ('start', 'Aktivieren und starten', 'primary', 'Dienst aktivieren und starten? Er verteilt danach IP-Adressen im Netz.'),
          ('stop', 'Stoppen und deaktivieren', 'danger', 'Dienst stoppen und deaktivieren? Clients erhalten danach keine Adressen mehr.')] %}
        <form method="post" action="{{ url_for('dhcp.service_action', action=action) }}" data-no-guard>
          <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
          <button class="btn btn--sm {{ 'btn--' ~ style if style }}" type="submit"
                  data-busy-label="Arbeitet ..." data-confirm="{{ question }}"
                  data-confirm-title="{{ label }}" data-confirm-ok="Ausfuehren"
                  data-confirm-style="{{ style or 'primary' }}">{{ label }}</button>
        </form>
      {% endfor %}
    </div>
  </section>
{% endif %}

{# -- Subnetze ---------------------------------------------------------- #}
<section class="card">
  <div class="card__header">
    <div class="flex-1">
      <div class="card__title">{{ icon('network', size=17) }} Subnetze</div>
      <div class="card__sub">
        Subnetz und Maske kommen vom laufenden System -- eingegeben wird nur der Adressbereich.
      </div>
    </div>
  </div>
  {% if subnets %}
    <div class="table-wrap">
      <table class="table table--compact">
        <thead><tr><th>Interface</th><th>Bereich</th><th>Gateway</th><th>DNS</th><th>Aktiv</th><th class="actions"></th></tr></thead>
        <tbody>
          {% for subnet in subnets %}
            <tr>
              <td class="mono fs-sm">{{ subnet.interface }}</td>
              <td class="mono fs-sm">{{ subnet.range_start }} &ndash; {{ subnet.range_end }}</td>
              <td class="mono fs-sm">{{ subnet.gateway or '&ndash;' | safe }}</td>
              <td class="mono fs-sm">{{ subnet.dns or '&ndash;' | safe }}</td>
              <td>{% if subnet.enabled %}{{ pill('aktiv', 'online') }}{% else %}{{ pill('pausiert', '') }}{% endif %}</td>
              <td class="actions">
                {% if current_user.has('dhcp.edit') %}
                  <form method="post" action="{{ url_for('dhcp.delete_subnet', subnet_id=subnet.id) }}"
                        style="display:inline" data-no-guard>
                    <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
                    <button class="btn btn--sm btn--ghost danger-text" type="submit"
                            data-confirm="Subnetz {{ subnet.range_start }}-{{ subnet.range_end }} entfernen?"
                            data-confirm-title="Subnetz entfernen" data-confirm-ok="Entfernen">
                      {{ icon('trash', size=15) }}</button>
                  </form>
                {% endif %}
              </td>
            </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>
  {% else %}
    {{ empty_state('Kein Subnetz', 'Ohne Subnetz startet Kea nicht.', 'network') }}
  {% endif %}

  {% if current_user.has('dhcp.edit') %}
    <form method="post" action="{{ url_for('dhcp.add_subnet') }}">
      <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
      <div class="card__body">
        <div class="form-grid">
          <label class="field">
            <span class="field__label">Interface</span>
            <select class="select" name="interface" required>
              {% for network in networks %}
                <option value="{{ network.interface }}">{{ network.interface }} ({{ network.cidr }})</option>
              {% endfor %}
            </select>
          </label>
          {{ field('range_start', 'Von', required=True, placeholder='192.168.1.100') }}
          {{ field('range_end', 'Bis', required=True, placeholder='192.168.1.200') }}
          {{ field('gateway', 'Gateway', value=default_gateway) }}
          {{ field('dns', 'DNS-Server', placeholder='192.168.1.1, 9.9.9.9') }}
          {{ field('comment', 'Kommentar') }}
        </div>
      </div>
      <div class="card__footer"><button class="btn btn--primary" type="submit">Subnetz anlegen</button></div>
    </form>
  {% endif %}
</section>

{# -- Globale Einstellungen --------------------------------------------- #}
{% if current_user.has('dhcp.edit') %}
  <section class="card">
    <div class="card__header">
      <div class="flex-1"><div class="card__title">{{ icon('sliders', size=17) }} Globale Einstellungen</div></div>
    </div>
    <form method="post" action="{{ url_for('dhcp.save_settings') }}">
      <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
      <div class="card__body">
        <div class="form-grid">
          {{ field('dhcp_domain', 'Domainname', value=values.dhcp_domain) }}
          {{ field('dhcp_lease_default', 'Lease-Dauer (s)', value=values.dhcp_lease_default, type='number') }}
          {{ field('dhcp_lease_max', 'Maximale Lease-Dauer (s)', value=values.dhcp_lease_max, type='number') }}
          {{ field('dhcp_output_path', 'Zielpfad der Konfiguration', value=values.dhcp_output_path,
                   extra_class='field--full') }}
        </div>
      </div>
      <div class="card__footer">
        <button class="btn btn--primary" type="submit">Speichern und schreiben</button>
      </div>
    </form>
  </section>
{% endif %}

{# -- Optionen ---------------------------------------------------------- #}
<section class="card">
  <div class="card__header">
    <div class="flex-1">
      <div class="card__title">{{ icon('puzzle', size=17) }} DHCP-Optionen</div>
      <div class="card__sub">
        {{ options | length }} Optionen &middot; Standardoptionen sind nicht loeschbar, ihr Wert
        laesst sich leeren
      </div>
    </div>
    {{ search_box('options-table', 'Option suchen ...') }}
  </div>
  <div class="table-wrap" style="max-height:420px;overflow:auto">
    <table class="table table--compact" id="options-table">
      <thead><tr><th class="numeric">Code</th><th>Name</th><th>Typ</th><th>Wert</th><th class="actions"></th></tr></thead>
      <tbody>
        {% for option in options %}
          <tr data-filter-text="{{ option.code }} {{ option.name }} {{ option.description }}">
            <td class="numeric fs-sm">{{ option.code }}</td>
            <td class="fs-sm">
              <div class="mono">{{ option.name }}</div>
              <div class="fs-xs faint">{{ option.description }}</div>
            </td>
            <td class="fs-xs mono">{{ option.type }}</td>
            <td>
              {% if current_user.has('dhcp.edit') %}
                <form method="post" action="{{ url_for('dhcp.set_option_value') }}" class="row row--tight"
                      data-no-guard>
                  <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
                  <input type="hidden" name="option_id" value="{{ option.id }}">
                  <input class="input input--mono" type="text" name="value" value="{{ option.value or '' }}"
                         style="min-width:180px" placeholder="leer = nicht senden">
                  <button class="btn btn--sm" type="submit">{{ icon('check', size=14) }}</button>
                </form>
              {% else %}
                <span class="mono fs-sm">{{ option.value or '&ndash;' | safe }}</span>
              {% endif %}
            </td>
            <td class="actions">
              {% if current_user.has('dhcp.edit') and not option.is_standard %}
                <form method="post" action="{{ url_for('dhcp.delete_custom_option', option_id=option.id) }}"
                      data-no-guard>
                  <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
                  <button class="btn btn--sm btn--ghost danger-text" type="submit"
                          data-confirm="Eigene Option {{ option.name }} loeschen?"
                          data-confirm-title="Option loeschen" data-confirm-ok="Loeschen">
                    {{ icon('trash', size=14) }}</button>
                </form>
              {% endif %}
            </td>
          </tr>
        {% endfor %}
      </tbody>
    </table>
  </div>
  {% if current_user.has('dhcp.edit') %}
    <form method="post" action="{{ url_for('dhcp.add_custom_option') }}">
      <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
      <div class="card__body">
        <div class="form-grid">
          {{ field('code', 'Code', type='number', required=True) }}
          {{ field('name', 'Name', required=True, placeholder='meine-option') }}
          <label class="field">
            <span class="field__label">Typ</span>
            <select class="select" name="type">
              {% for option_type in option_types %}<option value="{{ option_type }}">{{ option_type }}</option>{% endfor %}
            </select>
          </label>
          {{ field('description', 'Beschreibung') }}
        </div>
      </div>
      <div class="card__footer"><button class="btn" type="submit">Eigene Option anlegen</button></div>
    </form>

    {# Der Wert einer Option gilt sonst fuer alle. Genau dafuer gibt es
       ``dhcp_option_values.device_id``: ein eigener Wert je Geraet, der in
       dessen Reservierung geschrieben wird -- etwa eine Terminal-URL, die je
       Geraet anders lautet. Auswertbar war das von Anfang an, eingeben liess
       es sich nicht. #}
    <div class="card__header" style="border-top:1px solid var(--border-subtle)">
      <div class="flex-1">
        <div class="card__title">{{ icon('sliders', size=17) }} Option nur fuer ein Geraet</div>
        <div class="card__sub">
          Der Wert landet in der Reservierung dieses Geraets und ueberschreibt dort den
          globalen Wert. Leeres Feld entfernt ihn wieder.
        </div>
      </div>
    </div>
    <form method="post" action="{{ url_for('dhcp.set_device_option_value') }}">
      <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
      <div class="card__body">
        <div class="form-grid">
          <label class="field">
            <span class="field__label">Geraet<span class="req" aria-hidden="true">*</span></span>
            <select class="select" name="device_id" required>
              {% for reservation in reservations if reservation.device_id %}
                <option value="{{ reservation.device_id }}">
                  {{ reservation.name or reservation.hostname }} &mdash; {{ reservation.ip }}
                </option>
              {% endfor %}
            </select>
          </label>
          <label class="field">
            <span class="field__label">Option<span class="req" aria-hidden="true">*</span></span>
            <select class="select" name="option_id" required>
              {% for option in options %}
                <option value="{{ option.id }}">{{ option.code }} &mdash; {{ option.name }}</option>
              {% endfor %}
            </select>
          </label>
          {{ field('value', 'Wert', extra_class='field--full',
                   hint_text='Leer = Wert fuer dieses Geraet entfernen') }}
        </div>
      </div>
      <div class="card__footer">
        <button class="btn" type="submit">Fuer dieses Geraet speichern</button>
      </div>
    </form>
  {% endif %}
</section>

{# -- Reservierungen und Leases ----------------------------------------- #}
<div class="grid grid--2">
  <section class="card">
    <div class="card__header">
      <div class="flex-1">
        <div class="card__title">{{ icon('credit-card', size=17) }} Reservierungen</div>
        <div class="card__sub">
          Automatisch aus dem Geraetebestand; bei MAC-Kollision gewinnt der Geraetebestand.
          Nur Adressen aus eingerichteten Bereichen.
        </div>
      </div>
    </div>
    {% if skipped_reservations %}
      {# Nicht verschweigen, sondern benennen: vorher standen diese Eintraege in
         der Liste, obwohl Kea sie nie ausgeliefert hat. #}
      <div class="card__body pt-0">
        {{ alert(skipped_reservations | length ~ ' Geraet(e) liegen in keinem eingerichteten
        Bereich und bekommen keine Reservierung. Legen Sie den passenden Bereich an, wenn sie
        eine bekommen sollen.', 'warning') }}
        <details class="fs-xs faint">
          <summary>Welche?</summary>
          <ul class="mt-1">
            {% for entry in skipped_reservations %}
              <li><span class="mono">{{ entry.ip }}</span> &ndash; {{ entry.name }}
                ({{ entry.source }})</li>
            {% endfor %}
          </ul>
        </details>
      </div>
    {% endif %}
    <div class="table-wrap" style="max-height:360px;overflow:auto">
      <table class="table table--compact">
        <thead><tr><th>MAC</th><th>IP</th><th>Hostname</th><th>Zustand</th><th>Quelle</th>
                   <th>Eigene Optionen</th><th class="actions"></th></tr></thead>
        <tbody>
          {% for reservation in reservations %}
            <tr>
              <td class="mono fs-sm">{{ reservation.mac }}</td>
              <td class="mono fs-sm">{{ reservation.ip }}</td>
              <td class="fs-sm">{{ reservation.hostname }}</td>
              <td title="{{ reservation.state_hint }}">
                {{ pill(reservation.state | replace('_', ' '), reservation.state_style) }}
                {% if reservation.state == 'andere_adresse' and reservation.lease %}
                  <div class="fs-xs faint mono">jetzt {{ reservation.lease.address }}</div>
                {% elif reservation.state == 'aktiv' and reservation.lease %}
                  <div class="fs-xs faint">{{ reservation.lease.remaining_text }}</div>
                {% endif %}
              </td>
              <td>{{ pill(reservation.source, 'accent' if reservation.source == 'auto' else '') }}</td>
              <td class="fs-xs">
                {% set eigene = device_option_values.get(reservation.device_id) if reservation.device_id else none %}
                {% if eigene %}
                  {% for wert in eigene %}
                    <div class="mono break-all">{{ wert.code }} {{ wert.name }} = {{ wert.value }}</div>
                  {% endfor %}
                {% else %}
                  <span class="faint">&ndash;</span>
                {% endif %}
              </td>
              <td class="actions">
                {% if reservation.source == 'manuell' and current_user.has('dhcp.edit') %}
                  <form method="post" data-no-guard
                        action="{{ url_for('dhcp.delete_reservation', reservation_id=reservation.reservation_id) }}">
                    <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
                    <button class="btn btn--sm btn--ghost danger-text" type="submit"
                            data-confirm="Reservierung {{ reservation.mac }} entfernen?"
                            data-confirm-title="Reservierung entfernen" data-confirm-ok="Entfernen">
                      {{ icon('trash', size=14) }}</button>
                  </form>
                {% endif %}
              </td>
            </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>
    {% if current_user.has('dhcp.edit') %}
      <form method="post" action="{{ url_for('dhcp.add_reservation') }}">
        <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
        <div class="card__body">
          <div class="form-grid">
            {{ field('mac', 'MAC', required=True, placeholder='aa:bb:cc:dd:ee:ff') }}
            {{ field('ip', 'IP', required=True) }}
            {{ field('hostname', 'Hostname') }}
            {{ field('comment', 'Kommentar') }}
          </div>
        </div>
        <div class="card__footer"><button class="btn" type="submit">Reservierung speichern</button></div>
      </form>
    {% endif %}
  </section>

  <section class="card">
    <div class="card__header">
      <div class="flex-1">
        <div class="card__title">{{ icon('clock', size=17) }} Leases</div>
        <div class="card__sub">
          Direkt aus {{ lease_file }} gelesen &ndash; je Adresse gilt der letzte Eintrag.
        </div>
      </div>
    </div>
    {% if leases and service.get('active') != 'active' %}
      {# Sonst sieht man alte Eintraege und haelt sie fuer den aktuellen Stand:
         ohne laufenden Dienst wird keine Lease erneuert, und die Datei
         veraltet still. #}
      <div class="card__body pt-0">
        {{ alert('Der DHCP-Dienst laeuft nicht (' ~ service.get('active', 'unbekannt') ~ ').
        Es wird keine Lease erneuert -- die Liste zeigt den letzten Stand der Datei, nicht die
        aktuelle Lage. Der Grund steht im Kea-Protokoll.', 'warning') }}
      </div>
    {% endif %}
    {% if leases %}
      <div class="table-wrap" style="max-height:420px;overflow:auto">
        <table class="table table--compact">
          <thead><tr><th>IP</th><th>MAC</th><th>Hostname</th><th>Art</th><th>Zustand</th>
                     <th>Laeuft ab</th></tr></thead>
          <tbody>
            {% for lease in leases %}
              <tr>
                <td class="mono fs-sm">{{ lease.address }}</td>
                <td class="mono fs-sm">{{ lease.mac }}</td>
                <td class="fs-sm">{{ lease.hostname or '&ndash;' | safe }}</td>
                <td>
                  {% if lease.address in reserved_ips %}{{ pill('Reservierung', 'accent') }}
                  {% else %}{{ pill('dynamisch', '') }}{% endif %}
                </td>
                <td>
                  {% if lease.is_expired %}{{ pill('abgelaufen', 'warning') }}
                  {% elif lease.is_active %}{{ pill('gueltig', 'online') }}
                  {% else %}{{ pill('inaktiv', '') }}{% endif %}
                </td>
                <td class="fs-sm">{{ lease.expires_text }}
                  <div class="fs-xs faint">{{ lease.remaining_text }}</div>
                </td>
              </tr>
            {% endfor %}
          </tbody>
        </table>
      </div>
    {% else %}
      {{ empty_state('Keine Leases', 'Es sind keine vergebenen Adressen bekannt.', 'clock') }}
    {% endif %}
  </section>
</div>

{# -- Vorschau ---------------------------------------------------------- #}
<details class="card" data-persist="dhcp-preview">
  <summary class="card__header" style="cursor:pointer">
    <div class="flex-1">
      <div class="card__title">{{ icon('file-text', size=17) }} Erzeugte Kea-Konfiguration</div>
      <div class="card__sub">Vorschau &ndash; so wuerde die Datei geschrieben</div>
    </div>
    {{ icon('chevron-down', size=16) }}
  </summary>
  <pre class="log-view">{{ preview }}</pre>
  {% if current_user.has('dhcp.edit') %}
    <div class="card__footer">
      <form method="post" action="{{ url_for('dhcp.write_config') }}" data-no-guard>
        <input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
        <button class="btn btn--primary" type="submit">Datei jetzt schreiben</button>
      </form>
    </div>
  {% endif %}
</details>
{% endblock %}
