<?php
declare(strict_types=1);
session_start();

/**
 * json_editor.php
 * Drop this in the same folder as index.html, with /data/*.json beside it.
 * Edits your section JSON files via browser (structured editor + raw JSON).
 */

/* =========================
   CONFIG
   ========================= */
$dataDir = __DIR__ . DIRECTORY_SEPARATOR . 'data';
$allowedFiles = [
  'general.json'  => 'General',
  'security.json' => 'Security',
  'parking.json'  => 'Parking',
  'feature.json'  => 'Feature',
  'personal.json' => 'Personal',
  'other.json'    => 'Other',
];

// Optional: set a password to protect the editor (recommended on public servers).
$editorPassword = ''; // e.g. 'ChangeMe123!' (leave blank to disable)

/* =========================
   AUTH (optional)
   ========================= */
if ($editorPassword !== '') {
  if (isset($_POST['logout'])) {
    unset($_SESSION['json_editor_authed']);
    header("Location: " . strtok($_SERVER["REQUEST_URI"], '?'));
    exit;
  }
  $authed = ($_SESSION['json_editor_authed'] ?? false) === true;
  if (!$authed) {
    $err = '';
    if (isset($_POST['login_password'])) {
      if (hash_equals($editorPassword, (string)$_POST['login_password'])) {
        $_SESSION['json_editor_authed'] = true;
        header("Location: " . strtok($_SERVER["REQUEST_URI"], '?'));
        exit;
      } else {
        $err = 'Wrong password.';
      }
    }
    echo "<!doctype html><html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>JSON Editor Login</title>
      <style>
        body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial;background:#0e1014;color:#e9eef7;display:grid;place-items:center;height:100vh;}
        .card{width:min(520px,92vw);background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);border-radius:14px;box-shadow:0 18px 40px rgba(0,0,0,.45);padding:18px;}
        h1{margin:0 0 14px;font-size:18px;}
        input{width:100%;height:42px;border-radius:12px;border:1px solid rgba(255,255,255,.16);background:rgba(0,0,0,.35);color:#e9eef7;padding:0 12px;outline:none;}
        button{height:42px;border-radius:12px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.10);color:#e9eef7;padding:0 14px;font-weight:700;margin-top:12px;width:100%;}
        .err{color:#ff9090;margin:10px 0 0;font-size:13px;}
        .muted{opacity:.75;font-size:12px;margin-top:10px;line-height:1.3;}
      </style></head><body>
      <form class='card' method='post'>
        <h1>JSON Editor</h1>
        <input type='password' name='login_password' placeholder='Password' autofocus>
        <button type='submit'>Login</button>"
        . ($err ? "<div class='err'>".$err."</div>" : "")
        . "<div class='muted'>Tip: Set <code>\$editorPassword</code> in the file to protect this editor.</div>
      </form></body></html>";
    exit;
  }
}

/* =========================
   HELPERS
   ========================= */
function h(string $s): string { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); }
function isAllowed(string $f, array $allowed): bool { return array_key_exists($f, $allowed); }

function readJsonFile(string $path): array {
  if (!is_file($path)) return ["apps" => []];
  $raw = file_get_contents($path);
  if ($raw === false) return ["apps" => []];
  $data = json_decode($raw, true);
  if (!is_array($data)) return ["apps" => []];
  if (!isset($data["apps"]) || !is_array($data["apps"])) $data["apps"] = [];
  return $data;
}

function normalizeApps(array $apps): array {
  $out = [];
  foreach ($apps as $app) {
    if (!is_array($app)) continue;
    $item = [
      "name"   => trim((string)($app["name"] ?? "")),
      "url"    => trim((string)($app["url"] ?? "")),
      "icon"   => trim((string)($app["icon"] ?? "")),
      "tint"   => trim((string)($app["tint"] ?? "")),
      "hint"   => trim((string)($app["hint"] ?? "")),
      "target" => trim((string)($app["target"] ?? "_blank")),
    ];
    if ($item["name"] === "" && $item["url"] === "" && $item["icon"] === "" && $item["hint"] === "") {
      continue;
    }
    // Light sanity: allow only common targets
    if (!in_array($item["target"], ["_blank","_self","_top","_parent"], true)) $item["target"] = "_blank";
    $out[] = $item;
  }
  return $out;
}

function backupFile(string $path): void {
  if (!is_file($path)) return;
  $dir = dirname($path);
  $base = basename($path);
  $stamp = date('Ymd-His');
  $bak = $dir . DIRECTORY_SEPARATOR . $base . ".bak-" . $stamp;
  @copy($path, $bak);
}

function writeJsonFile(string $path, array $data): bool {
  $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
  if ($json === false) return false;
  $json .= "\n";
  return file_put_contents($path, $json, LOCK_EX) !== false;
}

/* =========================
   ROUTING / STATE
   ========================= */
if (!is_dir($dataDir)) {
  @mkdir($dataDir, 0755, true);
}

$file = $_GET['f'] ?? 'general.json';
$file = is_string($file) ? $file : 'general.json';
if (!isAllowed($file, $allowedFiles)) $file = 'general.json';

$path = $dataDir . DIRECTORY_SEPARATOR . $file;

$notice = '';
$error  = '';

$mode = $_GET['mode'] ?? 'structured';
$mode = ($mode === 'raw') ? 'raw' : 'structured';

/* =========================
   SAVE HANDLERS
   ========================= */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $action = $_POST['action'] ?? '';

  if ($action === 'save_structured') {
    $apps = $_POST['apps'] ?? [];
    if (!is_array($apps)) $apps = [];
    $apps = normalizeApps($apps);

    $data = ["apps" => $apps];
    backupFile($path);
    if (writeJsonFile($path, $data)) {
      $notice = "Saved: {$file}";
    } else {
      $error = "Failed to save: {$file} (check permissions).";
    }

  } elseif ($action === 'save_raw') {
    $raw = (string)($_POST['raw_json'] ?? '');
    $decoded = json_decode($raw, true);

    if (!is_array($decoded)) {
      $error = "Raw JSON is invalid.";
    } else {
      if (!isset($decoded["apps"]) || !is_array($decoded["apps"])) $decoded["apps"] = [];
      $decoded["apps"] = normalizeApps($decoded["apps"]);
      backupFile($path);
      if (writeJsonFile($path, $decoded)) {
        $notice = "Saved: {$file}";
      } else {
        $error = "Failed to save: {$file} (check permissions).";
      }
    }

  } elseif ($action === 'create_file') {
    // Allow creating missing allowed file
    if (!is_file($path)) {
      $data = ["apps" => []];
      if (writeJsonFile($path, $data)) {
        $notice = "Created: {$file}";
      } else {
        $error = "Failed to create: {$file} (check permissions).";
      }
    } else {
      $notice = "{$file} already exists.";
    }

  } elseif ($action === 'delete_row') {
    // Not used (handled client-side) but kept safe.
  }
}

$data = readJsonFile($path);
$apps = $data["apps"] ?? [];
if (!is_array($apps)) $apps = [];

$rawJsonPretty = json_encode(["apps" => normalizeApps($apps)], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
if ($rawJsonPretty === false) $rawJsonPretty = "{\n  \"apps\": []\n}";

/* =========================
   UI
   ========================= */
?><!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" />
  <title>JSON Section Editor</title>
  <style>
    :root{
      --bg:#0e1014; --panel:rgba(255,255,255,.06); --stroke:rgba(255,255,255,.12);
      --text:#e9eef7; --muted:rgba(233,238,247,.75); --shadow:0 18px 40px rgba(0,0,0,.45);
      --radius:14px; --pad:14px;
      --good:#7dffb2; --bad:#ff9090; --warn:#ffd37d;
      --field:rgba(0,0,0,.35);
      --btn:rgba(255,255,255,.10);
      --btn2:rgba(0,0,0,.28);
    }
    *{box-sizing:border-box}
    body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial;background:
      radial-gradient(1100px 700px at 35% 10%, #2c3342, transparent 60%),
      radial-gradient(900px 600px at 85% 15%, #243b55, transparent 55%),
      linear-gradient(145deg, #1b1e24, var(--bg));
      color:var(--text); min-height:100vh; padding:16px;
    }
    .wrap{max-width:1200px;margin:0 auto;display:grid;gap:12px;}
    .bar{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between;}
    .card{background:var(--panel);border:1px solid var(--stroke);border-radius:var(--radius);box-shadow:var(--shadow);padding:var(--pad);}
    h1{margin:0;font-size:18px;letter-spacing:.2px}
    .muted{opacity:.78;font-size:12px;line-height:1.35}
    select,input[type="text"],textarea{
      border-radius:12px;border:1px solid rgba(255,255,255,.16);background:var(--field);color:var(--text);
      height:40px;padding:0 12px;outline:none;width:100%;
    }
    textarea{height:420px;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.35;resize:vertical}
    .btn{
      height:40px;border-radius:12px;border:1px solid rgba(255,255,255,.16);
      background:var(--btn);color:var(--text);padding:0 14px;font-weight:800;cursor:pointer;
      touch-action:manipulation;
    }
    .btn.secondary{background:var(--btn2)}
    .btn.danger{background:rgba(255,0,0,.12);border-color:rgba(255,0,0,.25)}
    .btn:active{transform:translateY(1px)}
    .row{display:grid;grid-template-columns: 1.2fr 2fr 2fr 1.5fr 1.5fr .9fr .5fr;gap:8px;align-items:center}
    .row.header{font-size:12px;opacity:.8;font-weight:800}
    .row input{height:38px;border-radius:10px}
    .pill{
      display:inline-flex;align-items:center;gap:8px;padding:8px 10px;border-radius:12px;border:1px solid rgba(255,255,255,.12);
      background:rgba(0,0,0,.22);font-size:12px;
    }
    .msg{padding:10px 12px;border-radius:12px;border:1px solid rgba(255,255,255,.14);background:rgba(0,0,0,.25);font-size:13px}
    .msg.good{border-color:rgba(125,255,178,.35);color:var(--good)}
    .msg.bad{border-color:rgba(255,144,144,.35);color:var(--bad)}
    .msg.warn{border-color:rgba(255,211,125,.35);color:var(--warn)}
    .top-controls{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
    .grid{display:grid;gap:10px}
    .tools{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between}
    .small{font-size:12px;opacity:.8}
    .spacer{height:6px}
    @media (max-width: 980px){
      .row{grid-template-columns: 1fr 1fr; grid-auto-rows:auto}
      .row.header{display:none}
    }
  </style>
</head>
<body>
  <div class="wrap">

    <div class="card">
      <div class="bar">
        <div>
          <h1>JSON Section Editor</h1>
          <div class="muted">Edits your <code>/data/*.json</code> app lists. Saves backups as <code>.bak-YYYYMMDD-HHMMSS</code>.</div>
        </div>

        <div class="top-controls">
          <form method="get" style="display:flex;gap:10px;align-items:center;margin:0">
            <select name="f" onchange="this.form.submit()" style="min-width:220px">
              <?php foreach ($allowedFiles as $fn => $label): ?>
                <option value="<?=h($fn)?>" <?=($fn===$file?'selected':'')?>>
                  <?=h($label)?> (<?=h($fn)?>)
                </option>
              <?php endforeach; ?>
            </select>
            <select name="mode" onchange="this.form.submit()" style="min-width:160px">
              <option value="structured" <?=($mode==='structured'?'selected':'')?>>Structured</option>
              <option value="raw" <?=($mode==='raw'?'selected':'')?>>Raw JSON</option>
            </select>
          </form>

          <?php if ($editorPassword !== ''): ?>
            <form method="post" style="margin:0">
              <button class="btn secondary" type="submit" name="logout" value="1">Logout</button>
            </form>
          <?php endif; ?>
        </div>
      </div>

      <div class="spacer"></div>

      <?php if ($notice): ?><div class="msg good"><?=h($notice)?></div><?php endif; ?>
      <?php if ($error): ?><div class="msg bad"><?=h($error)?></div><?php endif; ?>

      <?php if (!is_file($path)): ?>
        <div class="msg warn" style="margin-top:10px">
          <b><?=h($file)?></b> does not exist yet in <code>/data</code>.
          <form method="post" style="margin-top:10px">
            <button class="btn" type="submit" name="action" value="create_file">Create File</button>
          </form>
        </div>
      <?php endif; ?>
    </div>

    <?php if ($mode === 'raw'): ?>
      <div class="card">
        <form method="post">
          <input type="hidden" name="action" value="save_raw" />
          <textarea name="raw_json" spellcheck="false"><?=h($rawJsonPretty ?: "{\n  \"apps\": []\n}")?></textarea>
          <div class="tools">
            <div class="pill"><span class="small">File:</span> <code><?=h($file)?></code></div>
            <div style="display:flex;gap:10px;flex-wrap:wrap">
              <button class="btn secondary" type="button" onclick="prettyFormat()">Pretty Format</button>
              <button class="btn" type="submit">Save</button>
            </div>
          </div>
        </form>
      </div>

    <?php else: ?>
      <div class="card">
        <form method="post" id="structuredForm">
          <input type="hidden" name="action" value="save_structured" />

          <div class="grid" id="rows">
            <div class="row header">
              <div>Name</div>
              <div>URL</div>
              <div>Icon (URL or data URI)</div>
              <div>Tint (CSS gradient/colour)</div>
              <div>Hint (tooltip)</div>
              <div>Target</div>
              <div></div>
            </div>

            <?php
              $appsNorm = normalizeApps($apps);
              if (count($appsNorm) === 0) $appsNorm = [["name"=>"","url"=>"","icon"=>"","tint"=>"","hint"=>"","target"=>"_blank"]];
              foreach ($appsNorm as $i => $app):
            ?>
              <div class="row app-row">
                <input type="text" name="apps[<?=$i?>][name]"   placeholder="Name"   value="<?=h((string)$app['name'])?>">
                <input type="text" name="apps[<?=$i?>][url]"    placeholder="https://..." value="<?=h((string)$app['url'])?>">
                <input type="text" name="apps[<?=$i?>][icon]"   placeholder="Icon URL or data:image/..." value="<?=h((string)$app['icon'])?>">
                <input type="text" name="apps[<?=$i?>][tint]"   placeholder="optional" value="<?=h((string)$app['tint'])?>">
                <input type="text" name="apps[<?=$i?>][hint]"   placeholder="optional" value="<?=h((string)$app['hint'])?>">
                <input type="text" name="apps[<?=$i?>][target]" placeholder="_blank" value="<?=h((string)$app['target'])?>">
                <button class="btn danger" type="button" onclick="removeRow(this)">X</button>
              </div>
            <?php endforeach; ?>
          </div>

          <div class="tools" style="margin-top:12px">
            <div class="pill"><span class="small">File:</span> <code><?=h($file)?></code></div>
            <div style="display:flex;gap:10px;flex-wrap:wrap">
              <button class="btn secondary" type="button" onclick="addRow()">Add App</button>
              <button class="btn secondary" type="button" onclick="fillTargets()">Fix Targets</button>
              <button class="btn" type="submit">Save</button>
            </div>
          </div>

          <div class="muted" style="margin-top:10px">
            Fields supported by your dashboard: <code>name</code>, <code>url</code>, <code>icon</code>, <code>tint</code>, <code>hint</code>, <code>target</code>.
            Leave <code>icon</code> blank to use the first letter badge.
          </div>
        </form>
      </div>
    <?php endif; ?>

    <div class="card">
      <div class="muted">
        <b>Quick tip:</b> If your dashboard page can’t load JSON when opened as <code>file://</code>, run it via a local web server or your hosting.
        This editor already runs on your server, so saving here is safe.
      </div>
    </div>

  </div>

  <script>
    function prettyFormat(){
      const ta = document.querySelector('textarea[name="raw_json"]');
      if(!ta) return;
      try{
        const obj = JSON.parse(ta.value);
        ta.value = JSON.stringify(obj, null, 2) + "\n";
      }catch(e){
        alert("JSON is invalid: " + e.message);
      }
    }

    function removeRow(btn){
      const row = btn.closest('.app-row');
      if(!row) return;
      const rows = document.querySelectorAll('.app-row');
      if(rows.length <= 1){
        // Keep at least one row
        row.querySelectorAll('input').forEach(i => i.value = '');
        row.querySelector('input[name*="[target]"]').value = "_blank";
        return;
      }
      row.remove();
      renumber();
    }

    function addRow(){
      const rowsWrap = document.getElementById('rows');
      const rows = rowsWrap.querySelectorAll('.app-row');
      const idx = rows.length;

      const div = document.createElement('div');
      div.className = 'row app-row';
      div.innerHTML = `
        <input type="text" name="apps[${idx}][name]" placeholder="Name" value="">
        <input type="text" name="apps[${idx}][url]" placeholder="https://..." value="">
        <input type="text" name="apps[${idx}][icon]" placeholder="Icon URL or data:image/..." value="">
        <input type="text" name="apps[${idx}][tint]" placeholder="optional" value="">
        <input type="text" name="apps[${idx}][hint]" placeholder="optional" value="">
        <input type="text" name="apps[${idx}][target]" placeholder="_blank" value="_blank">
        <button class="btn danger" type="button" onclick="removeRow(this)">X</button>
      `;
      rowsWrap.appendChild(div);
    }

    function renumber(){
      const rows = document.querySelectorAll('.app-row');
      rows.forEach((row, idx) => {
        row.querySelectorAll('input').forEach(input => {
          input.name = input.name.replace(/apps\[\d+\]/, 'apps[' + idx + ']');
        });
      });
    }

    function fillTargets(){
      document.querySelectorAll('input[name*="[target]"]').forEach(i => {
        const v = (i.value || '').trim();
        if(!v) i.value = "_blank";
      });
      alert("Targets filled where blank.");
    }
  </script>
</body>
</html>