<?php
declare(strict_types=1);

require_once __DIR__ . '/_auth.php';
tpcms_require_admin();

/* ---------- ROWS/単項目の旧ファイルを物理削除（保存完了後に実行） ---------- */
// JS から rows_deleted_files[] が POST されていれば /uploads 配下の該当 basename を削除。
// 外部URL(http/https)や /uploads/ 以外、パストラバーサルは安全側で無視します。
if ($_SERVER['REQUEST_METHOD'] === 'POST'
    && !empty($_POST['rows_deleted_files'])
    && is_array($_POST['rows_deleted_files'])) {

  $__rows_deleted_files = array_values(array_filter(array_map(function($v){ return (string)$v; }, $_POST['rows_deleted_files'])));

  // 保存処理が完了した後で確実に走るよう shutdown に登録
  register_shutdown_function(function() use ($__rows_deleted_files) {
    $uploadsDir = realpath(__DIR__ . '/../uploads');
    if ($uploadsDir === false) return;

    $seen = [];
    foreach ($__rows_deleted_files as $raw) {
      $v = trim($raw);
      if ($v === '' || preg_match('~^https?://~i', $v)) continue; // 外部URLは対象外

      if (strpos($v, '/uploads/') === 0) {
        $v = substr($v, 9); // '/uploads/' を除去
      }
      $bn = basename(str_replace('\\','/', $v));
      if ($bn === '' || isset($seen[$bn])) continue;
      $seen[$bn] = true;

      $path = $uploadsDir . DIRECTORY_SEPARATOR . $bn;
      $real = realpath($path);
      if ($real && strpos($real, $uploadsDir) === 0 && is_file($real)) {
        @unlink($real);
      }
    }
  });
}

require_once __DIR__ . '/../config.php';

// フォールバック（存在する場合は既存実装を優先）
if (!function_exists('tpcms_h')) {
    function tpcms_h(?string $s): string { return htmlspecialchars($s ?? '', ENT_QUOTES, 'UTF-8'); }
}
if (!function_exists('tpcms_json_read')) {
    function tpcms_json_read(string $path, $default = []) {
        if (is_file($path)) {
            $json = file_get_contents($path);
            $data = json_decode((string)$json, true);
            if (is_array($data) || is_object($data)) return $data;
        }
        return $default;
    }
}
if (!function_exists('tpcms_json_write')) {
    function tpcms_json_write(string $path, $data): bool {
        $dir = dirname($path);
        if (!is_dir($dir) && !@mkdir($dir, 0777, true)) {
            return false;
        }

        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
        if ($json === false) {
            return false; // JSON化失敗（不正なUTF-8など）
        }

        $tmp = $path . '.tmp';
        $bytes = @file_put_contents($tmp, $json, LOCK_EX);
        if ($bytes === false || $bytes < strlen($json)) {
            @unlink($tmp);
            return false; // 部分書き込み/0バイト書き込みを失敗扱いに
        }

        $ok = @rename($tmp, $path); // アトミック置換
        if (!$ok) {
            @unlink($tmp);
            return false;
        }

        @chmod($path, 0664);
        clearstatcache(true, $path);
        return true;
    }
}

// ------------------------------------------------------------
// アクティブテーマとフォーム定義の取得
// ------------------------------------------------------------
$active = tpcms_json_read(__DIR__ . '/../themes/_active.json', ['theme' => 'beginner9', 'color' => 'white']); // 例：{"theme":"beginner9","color":"white"}
$theme  = (string)($active['theme'] ?? 'beginner9');
$color  = (string)($active['color'] ?? 'white');
$themeKey = $theme;

$formdefPath = __DIR__ . '/../themes/' . $theme . '/' . $color . '/theme.formdef.json';
$formdef = tpcms_json_read($formdefPath, [
    'site_fields'   => [],
    'site_sections' => []
]);

$siteFields = is_array($formdef['site_fields'] ?? null) ? $formdef['site_fields'] : [];

// 現在値（共通）：/data/site.json
$siteCommonPath = __DIR__ . '/../data/site.json';
$siteCommon = tpcms_json_read($siteCommonPath, []);

// テーマ専用（site_sections）の現在値：/data/site.<themeKey>.json
$siteThemePath = __DIR__ . '/../data/site.' . $themeKey . '.json';
$siteTheme = tpcms_json_read($siteThemePath, []);
$siteSections = is_array($formdef['site_sections'] ?? null) ? $formdef['site_sections'] : [];

// ------------------------------------------------------------
// POST 保存（共通設定のみ）
// ------------------------------------------------------------
$saved = false;
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
tpcms_require_post_csrf();
    $posted = $_POST['site'] ?? [];
    $postedTheme = $_POST['theme'] ?? [];
	
    // 削除フラグ（共通／テーマ専用）
    $delSite  = $_POST['del_site']  ?? [];
    $delTheme = $_POST['del_theme'] ?? [];

    // uploads 実体削除用ヘルパ
    $uploadsDir = realpath(__DIR__ . '/../uploads') ?: (__DIR__ . '/../uploads');
    $tryDelete = function ($name) use ($uploadsDir) {
        if (!is_string($name) || $name === '') return;
        if (preg_match('~^https?://~i', $name)) return; // 外部URLは削除しない
        $target = $uploadsDir . '/' . basename($name);  // 保存は basename 想定
        if (is_file($target)) { @unlink($target); }
    };

    if (!is_array($posted)) { $posted = []; }

    // 既存値をベースに上書き（未知キーは温存）
    $updated = $siteCommon;
    $updatedTheme = $siteTheme;

    foreach ($siteFields as $__def) {
        $k = (string)($__def['key'] ?? '');
        if ($k === '') continue;

        $type = (string)($__def['type'] ?? 'text');
        $req  = !empty($__def['required']);
        $maxl = isset($__def['maxlength']) ? (int)$__def['maxlength'] : null;

        // 削除指示がある場合は旧値を削除して空に
        $old  = is_scalar($siteCommon[$k] ?? null) ? (string)$siteCommon[$k] : '';
        if ($type === 'file' && isset($posted[$k.'__delete']) && (string)$posted[$k.'__delete'] === '1') {
            // admin.js が生成する __delete チェック対応（del_site より優先）
            $tryDelete($old);
            $val = '';
        } elseif (isset($delSite[$k])) {
            if ($type === 'file') { $tryDelete($old); }
            $val = '';
        } else {
            if ($type === 'checkbox') {
                $val = isset($posted[$k]) ? '1' : '';
            } else {
                $raw = $posted[$k] ?? '';
                $val = is_scalar($raw) ? (string)$raw : '';
                $val = trim($val);
            }
        }

        // maxlength
        if ($maxl !== null && $maxl > 0) {
            if (function_exists('mb_substr')) {
                $val = mb_substr($val, 0, $maxl, 'UTF-8');
            } else {
                $val = substr($val, 0, $maxl);
            }
        }

        // required
        if ($req && $val === '') {
            $errors[] = "「{$k}」は必須です。";
        }

        // 型ごとの最小整形（現時点は text/textarea/file のみ想定）
        if ($type === 'file') {
            // 画像/動画/URL いずれも文字列として保存（実ファイルは別の統一アップロードUIで扱う方針）
            // 例：basename（uploads内）または https://... のURL を保存
            // 追加の検証は後続STEPで実装可
        } else {
            // text/textarea：特別な処理はなし
        }

        $updated[$k] = $val;
    }

// --- site_sections → $updatedTheme を構築（保存前に実行） ---
if (!is_array($postedTheme)) { $postedTheme = []; }

foreach ($siteSections as $__sec) {
    $secKey = (string)($__sec['key'] ?? '');
    if ($secKey === '') continue;
    $themeSecKey = 'THEME_' . $secKey;

    $secPosted = $postedTheme[$themeSecKey] ?? [];
    if (!is_array($secPosted)) $secPosted = [];

    $fields = is_array($__sec['fields'] ?? null) ? $__sec['fields'] : [];
    foreach ($fields as $__def) {
        $k    = (string)($__def['key'] ?? '');
        if ($k === '') continue;

        $type = (string)($__def['type'] ?? 'text');
        $req  = !empty($__def['required']);
        $maxl = isset($__def['maxlength']) ? (int)$__def['maxlength'] : null;

        // 削除指示がある場合は旧値を削除して空に
        $oldTheme = '';
        if (isset($siteTheme[$themeSecKey]) && is_array($siteTheme[$themeSecKey])) {
            $oldTheme = is_scalar($siteTheme[$themeSecKey][$k] ?? null) ? (string)$siteTheme[$themeSecKey][$k] : '';
        }
        if ($type === 'file' && isset($secPosted[$k.'__delete']) && (string)$secPosted[$k.'__delete'] === '1') {
            // admin.js の __delete チェック対応（del_theme より優先）
            $tryDelete($oldTheme);
            $val = '';
        } elseif (isset($delTheme[$themeSecKey]) && isset($delTheme[$themeSecKey][$k])) {
            if ($type === 'file') { $tryDelete($oldTheme); }
            $val = '';
        } else {
            if ($type === 'table') {
                // 新UI対応：theme[THEME_...][<key>][thead|cols|rows] を受け付ける（互換で旧形式もOK）
                $raw = $secPosted[$k] ?? [];
                $val = [];

                if (is_array($raw)) {
                    // 新形式（thead/cols/rows のいずれかがある）
                    if (isset($raw['rows']) || isset($raw['cols']) || isset($raw['thead'])) {
                        // rows
                        $rows = [];
                        if (isset($raw['rows']) && is_array($raw['rows'])) {
                            foreach ($raw['rows'] as $_r) {
                                if (!is_array($_r)) continue;
                                $clean = [];
                                foreach ($_r as $_ck => $_cv) {
                                    $clean[(string)$_ck] = is_scalar($_cv) ? trim((string)$_cv) : '';
                                }
                                $rows[] = $clean;
                            }
                        }
                        // cols
                        $cols = [];
                        if (isset($raw['cols']) && is_array($raw['cols'])) {
                            foreach ($raw['cols'] as $_c) {
                                $cols[] = [
                                    'key'   => (string)($_c['key'] ?? ''),
                                    'label' => (string)($_c['label'] ?? ''),
                                ];
                            }
                        }
                        // thead
                        $thead = [];
                        if (isset($raw['thead']) && is_array($raw['thead'])) {
                            $thead['top']  = !empty($raw['thead']['top']);
                            $thead['left'] = !empty($raw['thead']['left']);
                        }
                        $val = ['cols' => $cols, 'thead' => $thead, 'rows' => $rows];

                    } else {
                        // 互換：旧形式（[ [col=>val,...], ... ]）
                        foreach ($raw as $__row) {
                            if (!is_array($__row)) continue;
                            $clean = [];
                            foreach ($__row as $__ck => $__cv) {
                                $clean[(string)$__ck] = is_scalar($__cv) ? trim((string)$__cv) : '';
                            }
                            $val[] = $clean;
                        }
                    }
                }
            } else {
                if ($type === 'checkbox') {
                    $val = isset($secPosted[$k]) ? '1' : '';
                } else {
                    $raw = $secPosted[$k] ?? '';
                    $val = is_scalar($raw) ? (string)$raw : '';
                    $val = trim($val);
                }
            }
        }

        if ($maxl !== null && $maxl > 0) {
            if (function_exists('mb_substr')) {
                $val = mb_substr($val, 0, $maxl, 'UTF-8');
            } else {
                $val = substr($val, 0, $maxl);
            }
        }
        if ($req && $val === '') {
            $errors[] = "「{$secKey}.{$k}」は必須です。";
        }

        if (!isset($updatedTheme[$themeSecKey]) || !is_array($updatedTheme[$themeSecKey])) {
            $updatedTheme[$themeSecKey] = [];
        }
        $updatedTheme[$themeSecKey][$k] = $val;
    }
}

    if (empty($errors)) {
        $ok1 = tpcms_json_write($siteCommonPath, $updated);
        $ok2 = tpcms_json_write($siteThemePath,  $updatedTheme);
        if ($ok1 && $ok2) {
            $saved = true;
            $siteCommon = $updated;
            $siteTheme  = $updatedTheme; // 再表示に反映
        } else {
            $errors[] = '保存に失敗しました。書き込み権限を確認してください。';
        }
    }
}

// ------------------------------------------------------------
// HTML 出力
// ------------------------------------------------------------
?><!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>サイト設定（共通）｜Template Party CMS</title>
  <link rel="stylesheet" href="./assets/admin.css">
</head>
<body class="admin site">

<header>
<div>
<a href="./">ダッシュボード</a>
<a href="../" target="_blank" rel="noopener">公開側トップ</a>
</div>
<div>
<a href="./logout.php">ログアウト</a>
</div>
</header>

  <div id="container">
    <h1>サイト基本設定</h1>

    <?php if (!empty($errors)): ?>
      <div class="err">
        <strong>エラー：</strong>
        <ul style="margin:.4rem 0 .2rem .9rem;">
          <?php foreach ($errors as $e): ?>
            <li><?= tpcms_h($e) ?></li>
          <?php endforeach; ?>
        </ul>
      </div>
    <?php elseif ($saved): ?>
      <div class="notice">保存しました。</div>
    <?php endif; ?>

    <div class="card">
      <form method="post" action="./site.php" id="siteForm" data-form="site">
      <input type="hidden" name="_csrf" value="<?= tpcms_h(tpcms_csrf_token()) ?>">

        <!-- 共通設定（site_fields） -->
        <h2>共通設定</h2>

        <section class="sec collapsed" data-sec="SITE_COMMON" id="sec_SITE_COMMON">
          <button type="button" class="sec-toggle" data-sec-toggle="SITE_COMMON">
            <span>共通設定の編集</span>
          </button>
          <div class="sec-body">

<?php
// site_fields を順に描画（text / textarea / file を中心に最小表示）
foreach ($siteFields as $__def) {
    $key   = (string)($__def['key'] ?? '');
    if ($key === '') continue;
    $type  = (string)($__def['type'] ?? 'text');
    $label = (string)($__def['label'] ?? $key);
    $req   = !empty($__def['required']);
    $ph    = (string)($__def['placeholder'] ?? '');
    $max   = isset($__def['maxlength']) ? 'maxlength="'.(int)$__def['maxlength'].'"' : '';
    $val   = $siteCommon[$key] ?? '';
    $__imw = (int)($__def['image_max_width']  ?? 0);
    $__imh = (int)($__def['image_max_height'] ?? 0);
    $__iq  = (int)($__def['image_quality']    ?? 0);
    $__attrResize = '';
    if ($__imw > 0) $__attrResize .= ' data-image-max-width="'.$__imw.'"';
    if ($__imh > 0) $__attrResize .= ' data-image-max-height="'.$__imh.'"';
    if ($__iq  > 0) { $__iq = max(1, min(100, $__iq)); $__attrResize .= ' data-image-quality="'.$__iq.'"'; }

    echo '        <div class="row"'.$__attrResize.'>'.PHP_EOL;
    if ($type === 'checkbox') {
        echo '          <label class="label" style="display:none"></label>'.PHP_EOL;
    } else {
        echo '          <label class="label">'.tpcms_h($label).($req ? '（必須）' : '').'</label>'.PHP_EOL;
    }

    if ($type === 'textarea') {
        echo '          <textarea name="site['.tpcms_h($key).']" rows="3" '.$max.' placeholder="'.tpcms_h($ph).'"'.
             '>'.tpcms_h(is_scalar($val)? (string)$val : '').'</textarea>'.PHP_EOL;

} elseif ($type === 'file') {
    if ($ph === '') { $ph = 'ファイル名'; }
    // テキスト欄のみ（ドロップゾーン／サムネはJSに任せる）
    $safe = is_scalar($val) ? (string)$val : '';
    $uid  = 'up_site_' . preg_replace('/[^a-z0-9_]+/i', '_', (string)$key);

    echo '          <input id="'.tpcms_h($uid).'" type="text" name="site['.tpcms_h($key).']" value="'.tpcms_h($safe).'" class="input js-upload-url as-plain-text" placeholder="'.tpcms_h($ph).'" data-file="1" readonly aria-readonly="true" tabindex="-1">'.PHP_EOL;

    // （ヒント文を削除）


    } elseif ($type === 'checkbox') {
        $checked = !empty($val) && $val !== '0';
        echo '          <label class="inline"><input type="checkbox" name="site['.tpcms_h($key).']" value="1'.($checked ? '" checked' : '"').'> '.tpcms_h($label).($req ? '（必須）' : '').'</label>'.PHP_EOL;

    } else { // text / その他は text 扱い（最小）

        echo '          <input type="text" name="site['.tpcms_h($key).']" value="'.tpcms_h(is_scalar($val)? (string)$val : '').'" '.
             $max.' placeholder="'.tpcms_h($ph).'">'.PHP_EOL;
    }

    if (!empty($__def['help_html'])) {
        $help = (string)$__def['help_html'];
        $help = strip_tags($help, '<a><strong><em><code><ul><ol><li>');
        echo '          <div class="help">'.$help.'</div>'.PHP_EOL;
    }

    echo '        </div>'.PHP_EOL;
}

?>

          </div><!-- /.sec-body -->
        </section>

        <!-- テーマ専用設定（site_sections） -->
        <h2>テーマ専用設定</h2>

<?php
foreach ($siteSections as $__sec) {
    $secKey   = (string)($__sec['key'] ?? '');
    if ($secKey === '') continue;
    $secLabel = (string)($__sec['label'] ?? $secKey);
    $secHelp  = (string)($__sec['help_html'] ?? '');
    $fields   = is_array($__sec['fields'] ?? null) ? $__sec['fields'] : [];

    // 保存規則に合わせたキー名
    $themeSecKey = 'THEME_' . $secKey;
    $cur         = is_array($siteTheme[$themeSecKey] ?? null) ? $siteTheme[$themeSecKey] : [];
    $secId       = 'sec_' . preg_replace('/[^a-z0-9_]+/i', '_', $themeSecKey);
?>
        <section class="sec collapsed" data-sec="<?= tpcms_h($themeSecKey) ?>" id="<?= tpcms_h($secId) ?>">
          <button type="button" class="sec-toggle" data-sec-toggle="<?= tpcms_h($themeSecKey) ?>">
            <span><?= tpcms_h($secLabel) ?>の編集</span>
          </button>

<?php if ($secHelp !== ''): 
      $help = strip_tags($secHelp, '<a><strong><em><code><ul><ol><li>');
?>
          <div class="help" style="margin:.4rem .8rem 0;"><?= $help ?></div>
<?php endif; ?>

          <div class="sec-body">
<?php
    // 各フィールドを簡易描画（text / textarea / file の最小対応）
    foreach ($fields as $__def) {
        $k    = (string)($__def['key'] ?? '');
        if ($k === '') continue;
        $type = (string)($__def['type'] ?? 'text');
        $lab  = (string)($__def['label'] ?? $k);
        $req  = !empty($__def['required']);
        $ph   = (string)($__def['placeholder'] ?? '');
        $max  = isset($__def['maxlength']) ? 'maxlength="'.(int)$__def['maxlength'].'"' : '';
        $val  = $cur[$k] ?? '';
        $__imw = (int)($__def['image_max_width']  ?? 0);
        $__imh = (int)($__def['image_max_height'] ?? 0);
        $__iq  = (int)($__def['image_quality']    ?? 0);
        $__attrResize = '';
        if ($__imw > 0) $__attrResize .= ' data-image-max-width="'.$__imw.'"';
        if ($__imh > 0) $__attrResize .= ' data-image-max-height="'.$__imh.'"';
        if ($__iq  > 0) { $__iq = max(1, min(100, $__iq)); $__attrResize .= ' data-image-quality="'.$__iq.'"'; }

        echo '            <div class="row"'.$__attrResize.'>'.PHP_EOL;
        if ($type === 'checkbox') {
            echo '              <label class="label" style="display:none"></label>'.PHP_EOL;
        } else {
            echo '              <label class="label">'.tpcms_h($lab).($req ? '（必須）' : '').'</label>'.PHP_EOL;
        }

        if ($type === 'textarea') {
            echo '              <textarea name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" rows="3" '.$max.' placeholder="'.tpcms_h($ph).'">'.
                 tpcms_h(is_scalar($val)? (string)$val : '').'</textarea>'.PHP_EOL;

        } elseif ($type === 'file') {
            if ($ph === '') { $ph = 'ファイル名'; }
            // テキスト欄のみ（ドロップゾーン／サムネはJSに任せる）
            $safe = is_scalar($val) ? (string)$val : '';
            $uid  = 'up_sec_' . preg_replace('/[^a-z0-9_]+/i', '_', (string)$themeSecKey . '_' . (string)$k);

            echo '              <input id="'.tpcms_h($uid).'" type="text" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="'.tpcms_h($safe).'" class="input js-upload-url as-plain-text" placeholder="'.tpcms_h($ph).'" data-file="1" readonly aria-readonly="true" tabindex="-1">'.PHP_EOL;

    } elseif ($type === 'checkbox') {
        $checked = !empty($val) && $val !== '0';
        echo '              <label class="inline"><input type="checkbox" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="1"'.($checked ? ' checked' : '').'> '.tpcms_h($lab).($req ? '（必須）' : '').'</label>'.PHP_EOL;

        } elseif ($type === 'radio') {
            // radio: 未設定なら先頭を checked。保存値があれば一致するものを checked
            $options = is_array($__def['options'] ?? null) ? $__def['options'] : [];
            $hasKey  = array_key_exists($k, $cur ?? []);
            $i = 0;
            foreach ($options as $op) {
                // 2通りの定義を許容: ["value","label"] / {"value":..,"label":..}
                if (is_array($op) && array_key_exists(0, $op)) {
                    $oval = (string)($op[0] ?? '');
                    $olab = (string)($op[1] ?? $oval);
                } else {
                    $oval = (string)($op['value'] ?? '');
                    $olab = (string)($op['label'] ?? $oval);
                }
                $checked = $hasKey ? ((string)$val === $oval) : ($i === 0);
                $id = 'r_' . preg_replace('/[^a-z0-9_]+/i','_', (string)$themeSecKey . '_' . (string)$k . '_' . $i);
                echo '              <label class="inline" for="'.tpcms_h($id).'">'.PHP_EOL;
                echo '                <input id="'.tpcms_h($id).'" type="radio" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="'.tpcms_h($oval).'"'.($checked ? ' checked' : '').'> '.tpcms_h($olab).PHP_EOL;
                echo '              </label>'.PHP_EOL;
                $i++;
            }

        } elseif ($type === 'select') {
            $options = (isset($__def['options']) && is_array($__def['options'])) ? $__def['options'] : [];
            echo '              <select name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']"'.($req ? ' required' : '').'>'.PHP_EOL;
            foreach ($options as $op) {
                $v = (string)($op['value'] ?? '');
                $l = (string)($__def['labels'][$v] ?? $op['label'] ?? $v); // label優先
                $sel = ((string)$val === $v) ? ' selected' : '';
                echo '                <option value="'.tpcms_h($v).'"'.$sel.'>'.tpcms_h($l).'</option>'.PHP_EOL;
            }
            echo '              </select>'.PHP_EOL;

        } elseif ($type === 'table') {
            // ---- tpcms-form: table（列の追加/削除対応）----
            $k     = (string)($__def['key'] ?? '');
            $cols  = is_array($__def['cols'] ?? null) ? $__def['cols'] : [];
            $thead = $__def['thead'] ?? ['top' => false, 'left' => false];

            // 現在値（保存済み）取得（旧形式/新形式 両対応）
            $tval   = $cur[$k] ?? [];
            $___tbl = is_array($tval) ? $tval : [];

            // rows 決定：1) 新形式 rows[], 2) 旧形式「配列＝行リスト」、3) 定義 rows ぶん空行
            $___rows = [];
            if (isset($___tbl['rows']) && is_array($___tbl['rows'])) {
                // 新形式
                $___rows = array_values($___tbl['rows']);
            } elseif ($___tbl && array_keys($___tbl) === range(0, count($___tbl) - 1)) {
                // 旧形式（0..Nの数値キーのみ＝行リスト）
                foreach ($___tbl as $_r) {
                    if (!is_array($_r)) continue;
                    $clean = [];
                    foreach ($_r as $_ck => $_cv) {
                        $clean[(string)$_ck] = is_scalar($_cv) ? (string)$_cv : '';
                    }
                    $___rows[] = $clean;
                }
            } else {
                // 初期行（定義 rows）
                $want = (int)($__def['rows'] ?? 0);
                for ($ri = 0; $ri < max(0, $want); $ri++) $___rows[] = [];
            }

            // thead（保存済みがあれば優先）
            $___top  = !empty($thead['top']);
            $___left = !empty($thead['left']);
            if (isset($___tbl['thead']) && is_array($___tbl['thead'])) {
                $___top  = !empty($___tbl['thead']['top']);
                $___left = !empty($___tbl['thead']['left']);
            }

            // cols のアクティブ構成：保存 > 定義 > 先頭行推定
            $___colsActive = [];
            if (isset($___tbl['cols']) && is_array($___tbl['cols']) && $___tbl['cols']) {
                $___colsActive = array_values($___tbl['cols']);
            } elseif ($cols) {
                $___colsActive = array_values($cols);
            } elseif (isset($___rows[0]) && is_array($___rows[0])) {
                foreach (array_keys($___rows[0]) as $ck) {
                    $___colsActive[] = ['key' => (string)$ck, 'label' => ''];
                }
            }

            // name のベース（外側の<label>は既に出力済みなので、ここでは .tpcms-table のみを描画）
            $___base = 'theme['.$themeSecKey.']['.$k.']';
            ?>
            <div class="tpcms-table" data-base="<?= tpcms_h($___base) ?>">

              <div style="margin-bottom:.4rem">
                <label><input type="checkbox" name="<?= tpcms_h($___base) ?>[thead][top]"  value="1" <?= $___top  ? 'checked' : '' ?>> 上部見出し</label>
                <input type="hidden" name="<?= tpcms_h($___base) ?>[thead][left]" value="0">
                <label style="margin-left:1rem;"><input type="checkbox" name="<?= tpcms_h($___base) ?>[thead][left]" value="1" <?= $___left ? 'checked' : '' ?>> 左見出し</label>
              </div>

              <?php // ★ 列設定はUIに出さず hidden で往復保存 ?>
              <?php foreach ($___colsActive as $ci => $cc):
                $ck  = (string)($cc['key']   ?? '');
                $cl  = (string)($cc['label'] ?? '');
                if ($ck === '') continue; ?>
                <input type="hidden" name="<?= tpcms_h($___base) ?>[cols][<?= $ci ?>][key]"   value="<?= tpcms_h($ck) ?>">
                <input type="hidden" name="<?= tpcms_h($___base) ?>[cols][<?= $ci ?>][label]" value="<?= tpcms_h($cl) ?>">
              <?php endforeach; ?>

              <div class="tpcms-table-grid" style="display:flex;gap:.5rem;flex-wrap:nowrap;overflow-x:auto">
                <?php foreach ($___colsActive as $c):
                  $ck = (string)($c['key'] ?? '');
                  $cl = (string)($c['label'] ?? $ck);
                  if ($ck === '') continue; ?>
                  <div class="tpcms-table-col" data-col-key="<?= tpcms_h($ck) ?>">
                    <div class="muted" style="font-weight:bold;"><?= tpcms_h($cl) ?></div>
                    <?php foreach ($___rows as $rIdx => $rVal):
                      $cellName   = $___base.'[rows]['.$rIdx.']['.$ck.']';
                      $cellValRaw = $rVal[$ck] ?? '';
                      $cellVal    = is_scalar($cellValRaw) ? (string)$cellValRaw : ''; ?>
                      <input class="input" type="text"
                             name="<?= htmlspecialchars($cellName, ENT_QUOTES, 'UTF-8') ?>"
                             value="<?= tpcms_h($cellVal) ?>"
                             style="margin:.25rem 0;">
                    <?php endforeach; ?>
                  </div>
                <?php endforeach; ?>
              </div>

              <div style="margin-top:.5rem; display:flex; gap:.5rem; flex-wrap:wrap;">
                <div>
                  <button class="btn js-table-add-row mb1rem" type="button">＋ 行を追加</button>
                  <button class="btn js-table-del-row delete-color" type="button">− 最終行を削除</button>
                </div>
                <div>
                  <button class="btn js-table-add-col mb1rem" type="button">＋ 列を追加</button>
                  <button class="btn js-table-del-col delete-color" type="button">− 最終列を削除</button>
                </div>
              </div>

            </div>
            <?php

        } else {

            // text / その他は text として最小表示
            echo '              <input type="text" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="'.tpcms_h(is_scalar($val)? (string)$val : '').'" '.$max.' placeholder="'.tpcms_h($ph).'">'.PHP_EOL;
        }

        if (!empty($__def['help_html'])) {
            $fhelp = (string)$__def['help_html'];
            $fhelp = strip_tags($fhelp, '<a><strong><em><code><ul><ol><li>');
            echo '              <div class="help">'.$fhelp.'</div>'.PHP_EOL;
        }

        echo '            </div>'.PHP_EOL;
    } // end fields
?>
          </div><!-- /.sec-body -->
        </section>
<?php
} // end foreach sections
?>

        <!-- 下部の保存 -->
        <div class="btnbar">
          <button type="submit" class="btn1">保存</button>
        </div>
      </form>
    </div>
  </div>

  <script src="./assets/admin.js"></script>

<script>
(function(){
  var THEME_KEY = <?= json_encode($themeKey) ?>;
  var PREFIX = 'site.sec.' + THEME_KEY + '.';

  // 初期化（デフォルトは閉じる／保存で「1=開」のときだけ開く）
  document.querySelectorAll('.sec[data-sec]').forEach(function(sec){
    var id = sec.getAttribute('data-sec');
    var open = localStorage.getItem(PREFIX + id);
    if (open !== '1') { sec.classList.add('collapsed'); }
  });

  // トグル
  document.addEventListener('click', function(e){
    var btn = e.target.closest('.sec-toggle');
    if (!btn) return;
    var id = btn.getAttribute('data-sec-toggle');
    var sec = btn.closest('.sec');
    if (!sec || !id) return;

    var collapsed = sec.classList.toggle('collapsed');
    localStorage.setItem(PREFIX + id, collapsed ? '0' : '1');
  }, false);
})();
</script>

<script>
/* 置換検知（admin.js の実装に依存せず動く汎用版）
   - アップロード開始前に「現在値」を控える
   - 送信時に「控えた値」と「現在値」が違えば rows_deleted_files[] に積む
*/
(function(){
  function markPrevByInput(inp){
    if (!inp) return;
    inp.dataset.prevUploadValue = (inp.value || '').trim();
  }

  // dropzone（data-upload-drop）経由の操作開始時に以前の値を控える
  document.addEventListener('click', function(e){
    var dz = e.target.closest && e.target.closest('[data-upload-drop]');
    if (!dz) return;
    var sel = dz.getAttribute('data-upload-target') || dz.getAttribute('data-upload-drop');
    if (!sel) return;
    var inp = dz.closest('form')?.querySelector(sel);
    markPrevByInput(inp);
  });
  document.addEventListener('dragenter', function(e){
    var dz = e.target.closest && e.target.closest('[data-upload-drop]');
    if (!dz) return;
    var sel = dz.getAttribute('data-upload-target') || dz.getAttribute('data-upload-drop');
    if (!sel) return;
    var inp = dz.closest('form')?.querySelector(sel);
    markPrevByInput(inp);
  }, true);

  // テキストの file 入力にフォーカスした場合も控えておく（保険）
  document.addEventListener('focusin', function(e){
    var inp = e.target && e.target.matches && e.target.matches('input[type="text"][data-file="1"]') ? e.target : null;
    if (inp) markPrevByInput(inp);
  });

  // フォーム送信時に差分あれば rows_deleted_files[] を積む
  document.addEventListener('submit', function(e){
    var form = e.target && e.target.tagName === 'FORM' ? e.target : null;
    if (!form) return;
    form.querySelectorAll('input[type="text"][data-file="1"]').forEach(function(inp){
      var prev = (inp.dataset.prevUploadValue || '').trim();
      var now  = (inp.value || '').trim();
      if (!prev || prev === now) return;
      if (/^https?:\/\//i.test(prev)) return; // 外部URLは対象外
      var h = document.createElement('input');
      h.type = 'hidden';
      h.name = 'rows_deleted_files[]';
      h.value = prev;
      form.appendChild(h);
    });
  });
})();
</script>

<script>
// site.php: admin.js が生成する「__delete」チェック → rows_deleted_files[] に反映
document.addEventListener('change', function (e) {
  var cb = e.target;
  if (!cb || cb.type !== 'checkbox' || !cb.name || !/__delete$/.test(cb.name)) return;

  var form = cb.closest('form');
  if (!form) return;

  // 対応する file 入力（data-file="1" ＆ __delete を外したname）
  var baseName = cb.name.replace(/__delete$/, '');
  var inp = form.querySelector('input[type="text"][data-file="1"][name="' + baseName.replace(/"/g, '\\"') + '"]');
  if (!inp) return;

  var mark = form.querySelector('input[type="hidden"][name="rows_deleted_files[]"][data-del-for="' + baseName.replace(/"/g, '\\"') + '"]');
  if (cb.checked) {
    var v = (inp.value || '').trim();
    if (!v || /^https?:\/\//i.test(v)) return; // 外部URLは削除対象外
    if (!mark) {
      mark = document.createElement('input');
      mark.type = 'hidden';
      mark.name = 'rows_deleted_files[]';
      mark.setAttribute('data-del-for', baseName);
      form.appendChild(mark);
    }
    mark.value = v;
  } else {
    if (mark) mark.remove();
  }
});
</script>

<script>
// --- table: 行・列の追加/削除 ---
document.addEventListener('click', function (ev) {
  var btn;

  // ===== 行を追加 =====
  btn = ev.target.closest('.js-table-add-row');
  if (btn) {
    ev.preventDefault();
    var wrap = btn.closest('.tpcms-table'); if (!wrap) return;
    var base = wrap.getAttribute('data-base');

    // 既存列ごとに、次の行インデックスで空セルを1つ追加
    wrap.querySelectorAll('.tpcms-table-col').forEach(function (col) {
      var key = col.getAttribute('data-col-key');
      var inputs = col.querySelectorAll('input.input');
      var next = inputs.length; // 0始まりの次インデックス
      var name = base + '[rows][' + next + '][' + key + ']';
      var inp = document.createElement('input');
      inp.className = 'input';
      inp.type = 'text';
      inp.name = name;
      inp.value = '';
      inp.style.margin = '.25rem 0';
      col.appendChild(inp);
    });
    return;
  }

  // ===== 最終行を削除 =====
  btn = ev.target.closest('.js-table-del-row');
  if (btn) {
    ev.preventDefault();
    var wrap = btn.closest('.tpcms-table'); if (!wrap) return;

    // 各列の最終入力を1つずつ削除（0行未満にはしない）
    var canDel = true;
    wrap.querySelectorAll('.tpcms-table-col').forEach(function (col) {
      if (col.querySelectorAll('input.input').length === 0) canDel = false;
    });
    if (!canDel) return;
    wrap.querySelectorAll('.tpcms-table-col').forEach(function (col) {
      var inputs = col.querySelectorAll('input.input');
      var last = inputs[inputs.length - 1];
      if (last) last.remove();
    });
    return;
  }

  // ===== 列を追加 =====
  btn = ev.target.closest('.js-table-add-col');
  if (btn) {
    ev.preventDefault();
    var wrap = btn.closest('.tpcms-table'); if (!wrap) return;
    var base = wrap.getAttribute('data-base');

    var grid = wrap.querySelector('.tpcms-table-grid');
    if (!grid) return;

    // 既存キー集合
    var keys = Array.prototype.map.call(
      wrap.querySelectorAll('.tpcms-table-col'),
      function(col){ return col.getAttribute('data-col-key') || ''; }
    );

    // 新キーを COL1.. で自動採番（重複回避）
    var n = 1, newKey;
    do { newKey = 'COL' + n++; } while (keys.indexOf(newKey) !== -1);

    // 既存行数（任意の列の入力数）を取得
    var rowsCount = 0;
    var anyCol = wrap.querySelector('.tpcms-table-col');
    if (anyCol) rowsCount = anyCol.querySelectorAll('input.input').length;

    // 表示用ラベル：新しい列N
    var label = '新しい列' + (keys.length + 1);

    // --- hidden: cols[][key|label] を末尾に生成
    var idx = keys.length; // 新列は末尾インデックス
    var hKey   = document.createElement('input');
    var hLabel = document.createElement('input');
    hKey.type='hidden';   hLabel.type='hidden';
    hKey.name   = base + '[cols][' + idx + '][key]';
    hLabel.name = base + '[cols][' + idx + '][label]';
    hKey.value   = newKey;
    hLabel.value = label;
    wrap.appendChild(hKey); wrap.appendChild(hLabel);

    // --- 見た目の列DOMを生成
    var col = document.createElement('div');
    col.className = 'tpcms-table-col';
    col.setAttribute('data-col-key', newKey);

    var head = document.createElement('div');
    var sampleHead = grid.querySelector('.tpcms-table-col > div');
    if (sampleHead) {
      head.className = sampleHead.className || 'muted';
      var st = sampleHead.getAttribute('style');
      if (st) head.setAttribute('style', st);
    } else {
      head.className = 'muted';
      head.style.fontWeight = 'bold';
    }
    head.textContent = label;
    col.appendChild(head);

    for (var r=0; r<rowsCount; r++) {
      var inp = document.createElement('input');
      inp.className = 'input';
      inp.type = 'text';
      inp.name = base + '[rows][' + r + '][' + newKey + ']';
      inp.value = '';
      inp.style.margin = '.25rem 0';
      col.appendChild(inp);
    }

    grid.appendChild(col);
    return;
  }

  // ===== 最終列を削除 =====
  btn = ev.target.closest('.js-table-del-col');
  if (btn) {
    ev.preventDefault();
    var wrap = btn.closest('.tpcms-table'); if (!wrap) return;
    var base = wrap.getAttribute('data-base');

    var cols = wrap.querySelectorAll('.tpcms-table-col');
    if (!cols || cols.length <= 1) return; // 最低1列は残す

    var lastCol = cols[cols.length - 1];
    var lastKey = lastCol.getAttribute('data-col-key') || '';

    // hidden の cols[<idx>] を末尾インデックスで削除
    var lastIdx = cols.length - 1;
    var sel = [
      'input[type="hidden"][name="'+ base + '[cols]['+ lastIdx +'][key]"]',
      'input[type="hidden"][name="'+ base + '[cols]['+ lastIdx +'][label]"]'
    ].join(',');
    wrap.querySelectorAll(sel).forEach(function(h){ h.parentNode && h.parentNode.removeChild(h); });

    // 列DOMを削除
    lastCol.parentNode.removeChild(lastCol);
    return;
  }
}, false);
</script>

</body>
</html>
