<!doctype html>
<html lang="{{LANG_CODE}}" class="h-full">
<head>
  <meta charset="utf-8" />
  <title>{{T_APP_TITLE}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <script src="/admin/assets/tailwindcss.js"></script>
  <script src="/admin/assets/htmx.min.js"></script>
  <style>
    body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; }
    .nav-link.active { background: rgb(15 23 42); color: rgb(125 211 252); }
  </style>
</head>
<body class="h-full bg-slate-950 text-slate-100">
  <!--
    Single-page shell. The sidebar drives navigation via HTMX:
    each link does an `hx-get` of an HTML fragment URL that returns the
    body of the page. The fragments live under /admin/pages/ and are
    server-rendered Rust handlers that return Html<String>.
    This keeps the UI a flat directory of static files plus a small
    set of fragment endpoints — no SPA, no Node, no build step.
  -->
  <div class="min-h-full flex">
    <aside class="w-56 shrink-0 bg-slate-900 border-r border-slate-800 flex flex-col">
      <div class="px-4 py-5 border-b border-slate-800">
        <h1 class="text-base font-semibold">{{T_APP_TITLE}}</h1>
        <p id="me-display" class="text-xs text-slate-500 mt-1" hx-get="/admin/me" hx-trigger="load" hx-swap="innerHTML">…</p>
      </div>
      <nav class="flex-1 px-2 py-3 space-y-1">
        <a class="nav-link block px-3 py-1.5 text-sm rounded text-slate-300 hover:bg-slate-800"
           hx-get="/admin/pages/users" hx-target="#main" hx-push-url="#users">{{T_NAV_USERS}}</a>
        <a class="nav-link block px-3 py-1.5 text-sm rounded text-slate-300 hover:bg-slate-800"
           hx-get="/admin/pages/devices" hx-target="#main" hx-push-url="#devices">{{T_NAV_DEVICES}}</a>
        <a class="nav-link block px-3 py-1.5 text-sm rounded text-slate-300 hover:bg-slate-800"
           hx-get="/admin/pages/groups" hx-target="#main" hx-push-url="#groups">{{T_NAV_GROUPS}}</a>
        <a class="nav-link block px-3 py-1.5 text-sm rounded text-slate-300 hover:bg-slate-800"
           hx-get="/admin/pages/strategies" hx-target="#main" hx-push-url="#strategies">{{T_NAV_STRATEGIES}}</a>
        <a class="nav-link block px-3 py-1.5 text-sm rounded text-slate-300 hover:bg-slate-800"
           hx-get="/admin/pages/address-books" hx-target="#main" hx-push-url="#address-books">{{T_NAV_AB}}</a>
        <a class="nav-link block px-3 py-1.5 text-sm rounded text-slate-300 hover:bg-slate-800"
           hx-get="/admin/pages/audit" hx-target="#main" hx-push-url="#audit">{{T_NAV_AUDIT}}</a>
        <a class="nav-link block px-3 py-1.5 text-sm rounded text-slate-300 hover:bg-slate-800"
           hx-get="/admin/pages/deploy" hx-target="#main" hx-push-url="#deploy">{{T_NAV_DEPLOY}}</a>
      </nav>
      <div class="px-2 py-3 border-t border-slate-800 space-y-1">
        <a class="nav-link block px-3 py-1.5 text-sm rounded text-slate-400 hover:bg-slate-800"
           hx-get="/admin/pages/profile" hx-target="#main" hx-push-url="#profile">{{T_NAV_PROFILE}}</a>
        <button
          class="w-full text-left px-3 py-1.5 text-sm rounded text-slate-400 hover:bg-slate-800"
          hx-post="/admin/logout"
          hx-on::after-request="window.location.href = '/admin/login.html'"
        >{{T_NAV_SIGNOUT}}</button>
        <div class="pt-2">
          <label class="block text-[10px] uppercase tracking-wide text-slate-600 px-3 mb-1">{{T_LANGUAGE}}</label>
          <select
            class="w-full bg-slate-800 border border-slate-700 rounded px-2 py-1 text-xs text-slate-300"
            onchange="document.cookie='admin_lang='+this.value+'; path=/; max-age=31536000; samesite=strict'; window.location.reload();"
          >
            <option value="en"{{LANG_SEL_EN}}>English</option>
            <option value="de"{{LANG_SEL_DE}}>Deutsch</option>
            <option value="es"{{LANG_SEL_ES}}>Español</option>
            <option value="fr"{{LANG_SEL_FR}}>Français</option>
            <option value="ro"{{LANG_SEL_RO}}>Română</option>
          </select>
        </div>
      </div>
    </aside>

    <main id="main" class="flex-1 p-6 overflow-x-hidden">
      <div class="text-slate-500 text-sm">{{T_LOADING}}</div>
    </main>
  </div>

  <!-- Toast container used by all admin handlers via hx-trigger="load delay:1s" -->
  <div id="toast"
       class="fixed bottom-4 right-4 max-w-sm space-y-2 pointer-events-none"></div>

  <!-- Load fragment + highlight active link based on the URL hash.
       Sub-routes like #users/new map to dedicated fragment URLs but
       keep the parent section's nav-link highlighted. -->
  <script>
    // Hash → fragment URL for routes that aren't owned by a sidebar
    // nav-link (e.g. forms on their own page). The first path segment
    // also tells us which nav-link to highlight.
    const SUB_ROUTES = {
      '#users/new': '/admin/pages/users/new',
    };
    function topLevelHash(hash) {
      const slash = hash.indexOf('/');
      return slash === -1 ? hash : hash.slice(0, slash);
    }
    function linkForHash() {
      const hash = location.hash || '#users';
      const top = topLevelHash(hash);
      return document.querySelector('.nav-link[hx-push-url="' + top + '"]')
          || document.querySelector('.nav-link[hx-push-url="#users"]');
    }
    function refreshActive() {
      const active = linkForHash();
      document.querySelectorAll('.nav-link').forEach(a => {
        a.classList.toggle('active', a === active);
      });
    }
    // Dynamic deep-links: `#devices/<id>` and `#devices/<id>/exec`.
    // The detail / exec fragments are designed to swap into the
    // devices index page's `#devices-region` section, so when we
    // land here from a page refresh we have to chain two ajax calls:
    // first render the parent page (which provides `#devices-region`),
    // then swap the fragment into it. htmx.ajax has returned a Promise
    // since 1.9.4, so the `.then` chain is safe at our pinned 1.9.10.
    const DEEP_LINK_PATTERNS = [
      {
        re: /^#devices\/([^/]+)\/exec$/,
        parent: '/admin/pages/devices',
        fragment: id => `/admin/pages/devices/${encodeURIComponent(id)}/exec`,
      },
      {
        re: /^#devices\/([^/]+)$/,
        parent: '/admin/pages/devices',
        fragment: id => `/admin/pages/devices/${encodeURIComponent(id)}/detail`,
      },
    ];
    function loadDeepLink(hash) {
      for (const p of DEEP_LINK_PATTERNS) {
        const m = hash.match(p.re);
        if (!m) continue;
        const id = decodeURIComponent(m[1]);
        Promise.resolve(
          htmx.ajax('GET', p.parent, { target: '#main', swap: 'innerHTML' })
        ).then(() =>
          htmx.ajax('GET', p.fragment(id), {
            target: '#devices-region',
            swap: 'innerHTML',
          })
        );
        return true;
      }
      return false;
    }
    function loadFromHash() {
      const hash = location.hash || '#users';
      if (loadDeepLink(hash)) {
        refreshActive();
        return;
      }
      const subUrl = SUB_ROUTES[hash];
      if (subUrl) {
        htmx.ajax('GET', subUrl, { target: '#main', swap: 'innerHTML' });
      } else {
        const link = linkForHash();
        if (link) {
          htmx.ajax('GET', link.getAttribute('hx-get'),
                    { target: '#main', swap: 'innerHTML' });
        }
      }
      refreshActive();
    }
    window.addEventListener('hashchange', loadFromHash);
    document.body.addEventListener('htmx:afterSwap', refreshActive);
    loadFromHash();

    // Bounce to login if any HTMX request comes back 401.
    document.body.addEventListener('htmx:responseError', (evt) => {
      if (evt.detail.xhr.status === 401) {
        window.location.href = '/admin/login.html';
      }
    });

    // Close any open per-row action popover when a click happens outside it.
    // The action dropdowns are <details class="... relative"> with an
    // absolutely-positioned panel; the deploy page uses <details> too but
    // without `relative`, so the selector is specific to the popover style.
    document.addEventListener('click', (e) => {
      document.querySelectorAll('details.relative[open]').forEach(d => {
        if (!d.contains(e.target)) d.removeAttribute('open');
      });
    });

    // Read the current value of the users-search input (if present).
    // Used by usersColumnToggle/usersPageSize so a column or page-size
    // change preserves the active filter.
    function usersSearchValue() {
      const el = document.getElementById('users-search');
      return el ? el.value : '';
    }

    // Users table column-visibility toggle. The popover in the page header
    // emits checkboxes with onchange="usersColumnToggle(this)" — we POST
    // the new state to the server (which persists it in user_prefs) and
    // swap in the re-rendered table so the popover stays open.
    function usersColumnToggle(input) {
      const col = input.dataset.col;
      if (!col) return;
      htmx.ajax('POST', '/admin/pages/users/columns', {
        target: '#users-region',
        swap: 'innerHTML',
        values: {
          col: col,
          visible: input.checked ? '1' : '0',
          q: usersSearchValue(),
        },
      });
    }
    window.usersColumnToggle = usersColumnToggle;

    // Users table per-page selector. Driven by the <select> in the
    // pagination footer — POSTs to persist the choice and re-renders the
    // table at page 1 (size change shifts which rows are on which page).
    function usersPageSize(size) {
      htmx.ajax('POST', '/admin/pages/users/page-size', {
        target: '#users-region',
        swap: 'innerHTML',
        values: { size: size, q: usersSearchValue() },
      });
    }
    window.usersPageSize = usersPageSize;

    // Devices table — mirrors the users helpers above. Same persistence
    // model (per-user prefs in `user_prefs`) and the same fragment-swap
    // approach so the columns popover and search input stay put while
    // pagination/columns/page-size all preserve the active filter.
    function devicesSearchValue() {
      const el = document.getElementById('devices-search');
      return el ? el.value : '';
    }
    function devicesColumnToggle(input) {
      const col = input.dataset.col;
      if (!col) return;
      htmx.ajax('POST', '/admin/pages/devices/columns', {
        target: '#devices-region',
        swap: 'innerHTML',
        values: {
          col: col,
          visible: input.checked ? '1' : '0',
          q: devicesSearchValue(),
        },
      });
    }
    window.devicesColumnToggle = devicesColumnToggle;
    function devicesPageSize(size) {
      htmx.ajax('POST', '/admin/pages/devices/page-size', {
        target: '#devices-region',
        swap: 'innerHTML',
        values: { size: size, q: devicesSearchValue() },
      });
    }
    window.devicesPageSize = devicesPageSize;
  </script>
</body>
</html>
