<?php
declare(strict_types=1);

/**
 * Template Party CMS - STEP2 ルータ最小実装
 * 役割：
 *  - /           → slug=index を page.json から読み、index.html で描画
 *  - /<slug>     → 該当の page.json を page.html で描画
 *  - 存在しない → 404（簡易HTML）
 *
 * 依存：
 *  - app/templating.php（tpcms_render_page(), tpcms_read_json(), tpcms_active_theme() 等）
 *  - /data/site.json, /data/site.<themeKey>.json を上書きマージ
 *  - /data/menu.json を読み込み
 */

require_once __DIR__ . '/templating.php';

/* ---------------- 便利関数 ---------------- */

/** 深い連想配列を「b 優先」でマージ（配列はキーごと上書き） */
function tpcms_array_merge_deep(array $a, array $b): array {
    foreach ($b as $k => $v) {
        if (is_array($v) && isset($a[$k]) && is_array($a[$k])) {
            $a[$k] = tpcms_array_merge_deep($a[$k], $v);
        } else {
            $a[$k] = $v;
        }
    }
    return $a;
}

/** リクエストURIから slug を取得（空/"/"は index） */
function tpcms_current_slug(): string {
    // 例: REQUEST_URI = /---0000/0904-1/about?x=1
    $uri = $_SERVER['REQUEST_URI'] ?? '/';
    $qpos = strpos($uri, '?');
    if ($qpos !== false) $uri = substr($uri, 0, $qpos);

    // サブディレクトリ配備に対応（SCRIPT_NAME からベースパスを推定して除去）
    $script = $_SERVER['SCRIPT_NAME'] ?? '';
    $base   = rtrim(str_replace('\\', '/', dirname($script)), '/'); // 例: /---0000/0904-1
    $path   = $uri;
    if ($base !== '' && strpos($path, $base) === 0) {
        $path = substr($path, strlen($base)); // → /about
    }

    // 先頭セグメントのみを slug として採用
    $path = trim($path, '/');                 // → about
    if ($path === '' || $path === false) return 'index';
    $first = explode('/', $path, 2)[0];       // → about

    // サニタイズ
    $first = preg_replace('~[^a-zA-Z0-9\-_]~', '', $first);
    return $first === '' ? 'index' : $first;
}

/** 404を簡易表示 */
function tpcms_render_404(string $slug): void {
    http_response_code(404);
    header('Content-Type: text/html; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    echo '<!doctype html><meta charset="utf-8"><title>404 Not Found</title>';
    echo '<style>body{font-family:system-ui,-apple-system,Segoe UI,Arial,sans-serif;padding:2rem;color:#333}';
    echo 'a{color:#06c}</style>';
    echo '<h1>404 Not Found</h1>';
    echo '<p>指定されたページ（<code>' . tpcms_h($slug) . '</code>）は見つかりませんでした。</p>';
    echo '<p><a href="/">トップに戻る</a></p>';
}

/* ---------------- メイン：ルーティング ---------------- */

function tpcms_route(): void {
    // ヘッダ（軽量セキュリティ・文字コード）
    header('Content-Type: text/html; charset=utf-8');
    header('X-Content-Type-Options: nosniff');

    $slug = tpcms_current_slug();

    // アクティブテーマ
    ['theme' => $themeKey, 'color' => $color] = tpcms_active_theme(); // themes/_active.json を参照（STEP2仕様） 

    // site（共通 → テーマ専用で上書き）
    $siteCommon = tpcms_read_json(tpcms_path('data', 'site.json'));
    $siteTheme  = tpcms_read_json(tpcms_path('data', 'site.' . $themeKey . '.json'));
    $site = tpcms_array_merge_deep($siteCommon, $siteTheme); // /data/site.json → /data/site.<themeKey>.json の順で上書きマージ（STEP3） 

        // ---------- CREDIT_HTML：ライセンスコードで著作表記を切替（STEP8-4） ----------
    $licArr = tpcms_read_json(tpcms_path('data', 'license.json'), []);
    $code = is_string($licArr['code'] ?? null) ? trim($licArr['code']) : '';
    $hasValid = ($code !== '') && preg_match('/^<!--(?:e?License)\d{8}[A-Za-z0-9]{5}-->$/', $code);

    $creditFile = tpcms_path('themes', $themeKey, $color, 'partials', 'credit.html');

    if ($hasValid) {
        // 有効：ライセンスのコメントタグをそのまま埋め込む（{{CREDIT_HTML}} に生挿入）
        $site['CREDIT_HTML'] = $code;
    } else {
        // 無効：テーマ同梱の credit.html を使用
        $site['CREDIT_HTML'] = is_file($creditFile) ? (string)file_get_contents($creditFile) : '';
    }

    // menu
    $menu = tpcms_read_json(tpcms_path('data', 'menu.json')); // 親子は -- で表現（STEP3）
    
    // page（標準）… /data/themes/<themeKey>/pages/<slug>.json
    $pagePathStd   = tpcms_path('data', 'themes', $themeKey, 'pages', $slug . '.json');

    // 互換（color配下）… /data/themes/<themeKey>/<color>/pages/<slug>.json
    $pagePathColor = tpcms_path('data', 'themes', $themeKey, $color, 'pages', $slug . '.json');

    $pagePath = is_file($pagePathStd) ? $pagePathStd
              : (is_file($pagePathColor) ? $pagePathColor : '');

    if ($pagePath === '' || !is_file($pagePath)) {
        tpcms_render_404($slug);
        return;
    }
    $page = tpcms_read_json($pagePath);

// ---------- CONTACT：fields[] から CONTACT_FIELDS_HTML を生成・注入 ----------
if (is_array($page['blocks'] ?? null)) {
    // ページ内に contact_form.html がある時だけ処理
    $hasContact = false;
    foreach ($page['blocks'] as $blk) {
        if (($blk['partial'] ?? '') === 'contact_form.html') { $hasContact = true; break; }
    }
    if ($hasContact) {
        $contact = tpcms_read_json(tpcms_path('data','contact.json'), []);
        $defs = is_array($contact['fields'] ?? null) ? $contact['fields'] : [];

        // 安全のため、fields[] が空なら従来の3項目をフォールバック
        if (empty($defs)) {
            $defs = [
                ['key'=>'name',    'type'=>'text',     'label'=>'お名前',       'required'=>true, 'maxlength'=>200],
                ['key'=>'email',   'type'=>'email',    'label'=>'メール',       'required'=>true, 'maxlength'=>200],
                ['key'=>'message', 'type'=>'textarea', 'label'=>'お問い合わせ内容','required'=>true, 'maxlength'=>2000],
            ];
        }
        // HTMLを構築
        $h = [];
        foreach ($defs as $f) {
            if (!is_array($f)) continue;
            $key = isset($f['key']) && is_string($f['key']) ? trim($f['key']) : '';
            if ($key === '' || !preg_match('/^[A-Za-z0-9_\-]+$/', $key)) continue;

            $type      = strtolower((string)($f['type'] ?? 'text'));
            $label     = (string)($f['label'] ?? $key);
            $required  = !empty($f['required']);
            $maxlength = isset($f['maxlength']) && is_numeric($f['maxlength']) ? (int)$f['maxlength'] : null;
            $options   = (isset($f['options']) && is_array($f['options'])) ? array_values(array_filter(array_map('strval',$f['options']), 'strlen')) : [];

            $id = 'cf_' . $key;
            $reqAttr = $required ? ' required' : '';
            $maxAttr = $maxlength ? ' maxlength="'.htmlspecialchars((string)$maxlength, ENT_QUOTES, 'UTF-8').'"' : '';
            $labelText = htmlspecialchars($label . ($required ? ' ※' : ''), ENT_QUOTES, 'UTF-8');

            // フィールドごとのHTML（table版）
            if (in_array($type, ['text','email','tel','url','number'], true)) {
                $t = in_array($type, ['text','email','tel','url','number'], true) ? $type : 'text';
                $h[] = '<tr>'
                    . '<th>'.$labelText.'</th>'
                    . '<td><input id="'.htmlspecialchars($id,ENT_QUOTES,'UTF-8').'" name="'.htmlspecialchars($key,ENT_QUOTES,'UTF-8').'" type="'.$t.'"'.$reqAttr.$maxAttr.'></td>'
                    . '</tr>';
            } elseif ($type === 'textarea') {
                $h[] = '<tr>'
                    . '<th>'.$labelText.'</th>'
                    . '<td><textarea id="'.htmlspecialchars($id,ENT_QUOTES,'UTF-8').'" name="'.htmlspecialchars($key,ENT_QUOTES,'UTF-8').'"'.$reqAttr.$maxAttr.' rows="5"></textarea></td>'
                    . '</tr>';
            } elseif ($type === 'select') {
                $opts = [];
                foreach ($options as $opt) {
                    $opts[] = '<option value="'.htmlspecialchars($opt,ENT_QUOTES,'UTF-8').'">'.htmlspecialchars($opt,ENT_QUOTES,'UTF-8').'</option>';
                }
                $h[] = '<tr>'
                    . '<th>'.$labelText.'</th>'
                    . '<td>'
                    .   '<select id="'.htmlspecialchars($id,ENT_QUOTES,'UTF-8').'" name="'.htmlspecialchars($key,ENT_QUOTES,'UTF-8').'"'.$reqAttr.'>'
                    .     '<option value="" selected hidden>選択してください</option>'
                    .     implode('', $opts)
                    .   '</select>'
                    . '</td>'
                    . '</tr>';
            } elseif ($type === 'radio') {
                $items = [];
                foreach ($options as $i => $opt) {
                    $oid = $id . '_' . $i;
                    $items[] = '<label class="choice"><input id="'.htmlspecialchars($oid,ENT_QUOTES,'UTF-8').'" type="radio" name="'.htmlspecialchars($key,ENT_QUOTES,'UTF-8').'" value="'.htmlspecialchars($opt,ENT_QUOTES,'UTF-8').'"'.$reqAttr.'> '.htmlspecialchars($opt,ENT_QUOTES,'UTF-8').'</label>';
                }
                $h[] = '<tr>'
                    . '<th>'.$labelText.'</th>'
                    . '<td><div class="choices">'.implode(' ', $items).'</div></td>'
                    . '</tr>';
            } elseif ($type === 'checkbox') {
                $items = [];
                foreach ($options as $i => $opt) {
                    $oid = $id . '_' . $i;
                    $items[] = '<label class="choice"><input id="'.htmlspecialchars($oid,ENT_QUOTES,'UTF-8').'" type="checkbox" name="'.htmlspecialchars($key,ENT_QUOTES,'UTF-8').'[]"' . ' value="'.htmlspecialchars($opt,ENT_QUOTES,'UTF-8').'"'.$reqAttr.'> '.htmlspecialchars($opt,ENT_QUOTES,'UTF-8').'</label>';
                }
                $h[] = '<tr>'
                    . '<th>'.$labelText.'</th>'
                    . '<td><div class="choices">'.implode(' ', $items).'</div></td>'
                    . '</tr>';
            }
        }
        $fieldsHtml = implode("\n", $h);

        // --- Honeypot（非表示のダミー入力）---
        // ※ type="hidden" ではなく、text を display:none にするのがポイント
        $hpRow = '<tr class="tpcms-hp" style="display:none">'
               . '<th>会社名</th>'
               . '<td><input type="text" name="_hp_company" tabindex="-1" autocomplete="off" inputmode="text" value=""></td>'
               . '</tr>';

        $fieldsHtml .= "\n" . $hpRow;

// --- Time Gate（表示→送信の最短秒数検査用トークン）---
$__tgVal = (string)time();
$tgRow = '<tr class="tpcms-tg" style="display:none">'
       . '<th>確認</th>'
       . '<td><input type="hidden" name="_tg" value="'.htmlspecialchars($__tgVal, ENT_QUOTES, 'UTF-8').'"></td>'
       . '</tr>';
$fieldsHtml .= "\n" . $tgRow;

// --- CSRF（公開フォーム）：hidden にトークンを埋め込む ---
if (session_status() !== PHP_SESSION_ACTIVE) { @session_start(); }
if (empty($_SESSION['_csrf_form'])) {
    $_SESSION['_csrf_form'] = bin2hex(random_bytes(32));
}
$__csrfVal = (string)$_SESSION['_csrf_form'];

$csrfRow = '<tr class="tpcms-csrf" style="display:none">'
        .  '<th>確認</th>'
        .  '<td><input type="hidden" name="_csrf" value="' . htmlspecialchars($__csrfVal, ENT_QUOTES, 'UTF-8') . '"></td>'
        .  '</tr>';
$fieldsHtml .= "\n" . $csrfRow;

// 送信結果アラート（エラーを最優先 → なければ成功）
$alertHtml = '';
if (isset($_GET['error'])) {
    $e = (string)$_GET['error'];
    if ($e === 'rate_limit') {
        $alertHtml = '<div class="contact-alert error">短時間に送信が続いたため一時的に制限されています。しばらく時間をおいて再度お試しください。</div>';
    } elseif ($e === 'csrf') {
        $alertHtml = '<div class="contact-alert error">フォームの有効期限が切れた可能性があります。ページを再読み込みしてから、もう一度送信してください。</div>';
    } elseif ($e === 'fast') {
        $alertHtml = '<div class="contact-alert error">送信が早すぎます。数秒待ってから、もう一度送信してください。</div>';
    }
} elseif (isset($_GET['sent']) && (string)$_GET['sent'] === '1') {
    $alertHtml = '<div class="contact-alert success">送信が完了しました。ありがとうございました。</div>';
}

        // contact_form.html の各ブロックに注入
        foreach ($page['blocks'] as $i => $blk) {
            if (($blk['partial'] ?? '') !== 'contact_form.html') continue;
            $data = is_array($blk['data'] ?? null) ? $blk['data'] : [];
            $data['CONTACT_FIELDS_HTML'] = $fieldsHtml;
            $data['CONTACT_SENT_ALERT_HTML'] = $alertHtml;
            $page['blocks'][$i]['data'] = $data;
        }
        unset($fieldsHtml, $h, $defs, $contact);
    }
}
// ---------- /CONTACT ----------

// ---------- NEWS：page に news.html / news_archive.html がある場合、NEWS_ITEMS_HTML 等を注入 ----------
if (is_array($page['blocks'] ?? null)) {
    $newsJson = tpcms_read_json(tpcms_path('data','news.json'), ['items' => [], 'top_limit' => 5, 'archive_url' => '']);

    // items を正規化→日付降順
    $all = [];
    if (is_array($newsJson['items'] ?? null)) {
        foreach ($newsJson['items'] as $r) {
            if (!is_array($r)) continue;
            $date  = is_string($r['date']  ?? null) ? $r['date']  : '';
            $title = is_string($r['title'] ?? null) ? $r['title'] : '';
            $url   = is_string($r['url']   ?? null) ? $r['url']   : '';
            if ($title === '') continue;
            $all[] = ['date'=>$date, 'title'=>$title, 'url'=>$url];
        }
    }
    usort($all, function($a,$b){ return strcmp(($b['date'] ?? ''), ($a['date'] ?? '')); });

    // トップ表示件数（1〜999）
    $limit = (int)($newsJson['top_limit'] ?? 0);
    if ($limit < 1) $limit = 1;
    if ($limit > 999) $limit = 999;

    // HTML生成（li）
    $makeLis = function(array $rows): string {
        $lis = [];
        foreach ($rows as $r) {
            $dateH  = tpcms_h((string)($r['date'] ?? ''));
            $titleH = tpcms_h((string)($r['title'] ?? ''));
            $url    = (string)($r['url'] ?? '');
            if ($url !== '') {
                if (preg_match('#^https?://#i', $url)) {
                    // 外部URLはそのまま
                    $href = $url;
                } elseif ($url[0] === '/') {
                    // 旧データなどの「/始まり」は“設置ディレクトリ”を起点に変換
                    $base = rtrim(str_replace('\\','/', dirname($_SERVER['SCRIPT_NAME'] ?? '/')), '/'); // 例: /---0000/0909-x
                    $href = ($base === '' ? '' : $base) . $url; // 例: /---0000/0909-x + /news => /---0000/0909-x/news
                } else {
                    // 相対（先頭/なし）はカレント相対で
                    $href = './' . ltrim($url, '/');
                }
                $lis[] = '<dt>'.$dateH.'</dt><dd><a href="'.tpcms_h($href).'" class="news-link">'.$titleH.'</a></dd>';
            } else {
                $lis[] = '<dt>'.$dateH.'</dt><dd><span class="news-title">'.$titleH.'</span></dd>';
            }
        }
        return implode("\n", $lis);
    };

    $htmlTop = $makeLis(array_slice($all, 0, $limit));
    $htmlAll = $makeLis($all);

    // 過去ログリンク（サブディレクトリ対応）
    $arch = is_string($newsJson['archive_url'] ?? null) ? trim($newsJson['archive_url']) : '';
    $archiveLinkHtml = '';
    if ($arch !== '') {
        $href = '';
        if (preg_match('#^https?://#', $arch)) {
            // 絶対URLはそのまま
            $href = $arch;
        } elseif (strlen($arch) && $arch[0] === '/') {
            // ドメインルートではなく“設置ディレクトリ”を起点にする
            $base = rtrim(str_replace('\\','/', dirname($_SERVER['SCRIPT_NAME'] ?? '/')), '/'); // 例: /---0000/0906-2
            $href = ($base === '' ? '' : $base) . $arch; // 例: /---0000/0906-2 + /news => /---0000/0906-2/news
        } else {
            // 相対指定（将来用）→ 設置ディレクトリ基準
            $base = rtrim(str_replace('\\','/', dirname($_SERVER['SCRIPT_NAME'] ?? '/')), '/');
            $href = ($base === '' ? '' : $base . '/') . $arch;
        }
        $archiveLinkHtml = '<a href="'.tpcms_h($href).'">お知らせ一覧</a>';
    }

    // ブロックへ注入
    foreach ($page['blocks'] as $i => $blk) {
        $partial = (string)($blk['partial'] ?? '');
        if ($partial === 'news.html') {
            $data = is_array($blk['data'] ?? null) ? $blk['data'] : [];
            $data['NEWS_ITEMS_HTML']        = $htmlTop;
            $data['NEWS_ARCHIVE_LINK_HTML'] = $archiveLinkHtml;
            $page['blocks'][$i]['data'] = $data;
        } elseif ($partial === 'news_archive.html') {
            $data = is_array($blk['data'] ?? null) ? $blk['data'] : [];
            $data['NEWS_ITEMS_HTML'] = $htmlAll; // 全件
            // アーカイブ側はリンク不要
            $page['blocks'][$i]['data'] = $data;
        }
    }
    unset($newsJson, $all, $htmlTop, $htmlAll, $archiveLinkHtml);
}


    // 描画（HEADER/FOOTER/CONTENT/NAV/META_* の差し込みは templating 側で実施：STEP2）
    $html = tpcms_render_page($slug, $page, $site, $menu);

    echo $html;
}

/* index.php から呼び出します（ここでは自動実行しない） */
