<?php
declare(strict_types=1);

/**
 * ============================================================
 *  お知らせ（一覧・詳細）プラグイン  -  データ層＋描画
 * ------------------------------------------------------------
 *  無料版MVP：固定リスト1つ「お知らせ（news）」。
 *    項目＝日付・タイトル・本文・画像（固定）。
 *  詳細URL：?p=news&id=123（クエリ方式・mod_rewrite不要）。
 *  一覧は「お知らせ一覧」パーツとして任意ページに置ける。
 *
 *  ※ コア規約に準拠：
 *     - SQLite旧互換（INSERT OR REPLACE / CREATE TABLE IF NOT EXISTS）
 *     - PDOプリペアド / h() / safe_richtext / cms_safe_url
 *     - 設定値は settings、本文出力は「パーツ」＝フレーム着せ替え互換
 * ============================================================
 */

/** 当面はこの固定リスト1つ（無料版）。フル版で複数リスト対応に拡張予定。 */
const LISTNEWS_KEY  = 'news';   // list_items.list_key
const LISTNEWS_SLUG = 'news';   // 詳細URL ?p=news&id=... のベースslug

/* ============================================================
 *  マイグレーション（テーブル作成＋初回シード）。必ず冪等。
 * ============================================================ */
function listnews_migrate(PDO $pdo): void {
    $pdo->exec(
        "CREATE TABLE IF NOT EXISTS list_items (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            list_key   TEXT NOT NULL DEFAULT 'news',
            title      TEXT NOT NULL DEFAULT '',
            body       TEXT NOT NULL DEFAULT '',
            image      TEXT NOT NULL DEFAULT '',
            item_date  TEXT NOT NULL DEFAULT '',
            status     TEXT NOT NULL DEFAULT 'public',
            sort       INTEGER NOT NULL DEFAULT 0,
            created_at TEXT NOT NULL DEFAULT (datetime('now')),
            updated_at TEXT NOT NULL DEFAULT (datetime('now'))
        )"
    );
    $pdo->exec("CREATE INDEX IF NOT EXISTS idx_list_items ON list_items(list_key, status, item_date)");

    // 初回のみ：デモ記事＋「お知らせ」ページを用意（無料版が箱出しで動くように）
    $installed = $pdo->query("SELECT value FROM settings WHERE key='listnews_installed'")->fetchColumn();
    if ($installed === '1') return;

    $demo = [
        ['2026-06-10', '夏季キャンペーンを開始しました',     "本日より夏のキャンペーンを開始しました。\n期間中はお得な特典をご用意しております。ぜひこの機会にご利用ください。", 'summer-campaign.jpg'],
        ['2026-06-01', '梅雨の営業時間について',           "6月の営業時間は通常どおりです。\n雨の日も元気に営業しております。お気軽にお越しください。"],
        ['2026-05-20', 'ウェブサイトをリニューアルしました', "このたびWebサイトを新しくしました。\nこれからも役立つ情報を発信していきます。どうぞよろしくお願いいたします。"],
        ['2026-05-12', 'スタッフを募集しています',          '一緒に働いてくださる仲間を募集しています。詳しくはお問い合わせください。'],
        ['2026-05-01', 'ゴールデンウィーク休業のお知らせ',   '5月3日〜5月6日まで休業いたします。ご不便をおかけしますが、よろしくお願いいたします。'],
        ['2026-04-18', '新しいサービスを追加しました',       'お客様のご要望にお応えして、新しいサービスを始めました。ぜひご覧ください。'],
        ['2026-04-02', '春の感謝祭を開催します',            "日頃のご愛顧に感謝して、春の感謝祭を開催します。\n皆様のご来場を心よりお待ちしております。"],
    ];
    $ins = $pdo->prepare(
        "INSERT INTO list_items(list_key,title,body,image,item_date,status,sort) VALUES('news',?,?,?,?,'public',?)"
    );
    foreach ($demo as $i => $d) {
        // 並び：title=$d[1], body=$d[2], item_date=$d[0], sort=$i（image=''、status='public' は固定）
        $ins->execute([$d[1], $d[2], $d[3] ?? '', $d[0], $i]);
    }

    // 「お知らせ」一覧ページ（slug=news）を用意。既にあれば作らない。
    $exists = $pdo->prepare("SELECT id FROM pages WHERE slug='news'");
    $exists->execute();
    $pageExisted = (bool)$exists->fetchColumn();
    if (!$pageExisted) {
        $maxSort = (int)$pdo->query("SELECT COALESCE(MAX(sort),0) FROM pages")->fetchColumn();
        $pdo->prepare(
            "INSERT INTO pages(slug,title,meta_description,parent,in_nav,sort) VALUES('news','お知らせ','最新のお知らせ一覧です。','',1,?)"
        )->execute([$maxSort + 1]);
        $pid = (int)$pdo->lastInsertId();
        $blockData = json_encode(
            ['h_level' => 'h2', 'h_text' => 'お知らせ', 'h_deco' => 'NEWS', 'count' => 'すべて'],
            JSON_UNESCAPED_UNICODE
        );
        $pdo->prepare("INSERT INTO blocks(page_id,part,data,sort) VALUES(?, 'news-list', ?, 0)")
            ->execute([$pid, $blockData]);
    }

    // グローバルメニューに「お知らせ」を追加（既に news へのページリンクが無い時だけ）。
    // 訪問者がトップのリンクだけでなくメニューからもお知らせ一覧へ行けるように。
    // ★ページが既にあった＝この配布物が自分で「お知らせ」ページを用意している（demo-pack など）。
    //   導線も向こうが設計しているはずなので、メニューには口を出さない。
    //   （例：不動産Free版はメニューに出さず、トップのお知らせ欄とフッターから辿らせている）
    $hasNewsMenu = $pdo->query("SELECT COUNT(*) FROM menu_items WHERE link_type='page' AND link='news'")->fetchColumn();
    if (!$pageExisted && !$hasNewsMenu) {
        $mSort = (int)$pdo->query("SELECT COALESCE(MAX(sort),0) FROM menu_items")->fetchColumn();
        $pdo->prepare(
            "INSERT INTO menu_items(label,link_type,link,parent_id,show_pc,show_mobile,is_button,sort) VALUES('お知らせ','page','news',NULL,1,1,0,?)"
        )->execute([$mSort + 1]);
    }

    // ホームの「静的お知らせ」ブロックを、動的の「お知らせ一覧（3件＋一覧リンク）」に
    // 同じ位置で差し替える（無料版のトップ→一覧→詳細を一本の流れで見せるため）。
    // 静的newsブロックが無ければ何もしない（既にカスタム済みのホームを壊さない）。
    $home = $pdo->query("SELECT id FROM pages WHERE slug='home'")->fetch();
    if ($home) {
        $hid = (int)$home['id'];
        $row = $pdo->query("SELECT id, sort FROM blocks WHERE page_id=" . $hid . " AND part='news' ORDER BY sort, id LIMIT 1")->fetch();
        if ($row) {
            $sort = (int)$row['sort'];
            $pdo->prepare("DELETE FROM blocks WHERE id=?")->execute([(int)$row['id']]);
            $homeBlock = json_encode(
                ['h_level' => 'h2', 'h_text' => 'お知らせ', 'h_deco' => 'NEWS', 'count' => '3', 'more_text' => 'お知らせ一覧へ'],
                JSON_UNESCAPED_UNICODE
            );
            $pdo->prepare("INSERT INTO blocks(page_id,part,data,sort) VALUES(?, 'news-list', ?, ?)")
                ->execute([$hid, $homeBlock, $sort]);
        }
    }

    $pdo->prepare("INSERT OR REPLACE INTO settings(key,value) VALUES('listnews_installed','1')")->execute();
}

/* ============================================================
 *  データアクセス
 * ============================================================ */

/** お知らせ一覧（新しい順）。$publicOnly=false で非公開も含む（管理画面用）。 */
function listnews_items(bool $publicOnly = true, int $limit = 0): array {
    $sql    = "SELECT * FROM list_items WHERE list_key = ?";
    $params = [LISTNEWS_KEY];
    if ($publicOnly) $sql .= " AND status = 'public'";
    $sql .= " ORDER BY item_date DESC, id DESC";
    if ($limit > 0) { $sql .= " LIMIT ?"; $params[] = $limit; }
    $st = db()->prepare($sql);
    $st->execute($params);
    return $st->fetchAll();
}

/** 1件取得（無ければ null） */
function listnews_find(int $id): ?array {
    $st = db()->prepare("SELECT * FROM list_items WHERE id = ? AND list_key = ?");
    $st->execute([$id, LISTNEWS_KEY]);
    $row = $st->fetch();
    return $row ?: null;
}

/** 詳細ページのURL（?p=news&id=123） */
function listnews_detail_url(int $id): string {
    return page_url(LISTNEWS_SLUG) . '&id=' . $id;
}

/** 日付の表示整形（YYYY-MM-DD → YYYY.MM.DD。それ以外は素のまま） */
function listnews_format_date(string $d): string {
    $d = trim($d);
    if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $d, $m)) return $m[1] . '.' . $m[2] . '.' . $m[3];
    return $d;
}

/* ============================================================
 *  描画：一覧パーツ（cms_register_part_renderer で 'news-list' に登録）
 *  → template.html へ渡すコンテキストを配列で返す（HEAD/SECTION_ATTRはコア側が用意）
 * ============================================================ */
function listnews_render_list_part(array $data, array $def, array $ctx): array {
    $countRaw = (string)($data['count'] ?? '5');
    $limit = ($countRaw === 'すべて' || $countRaw === '') ? 0 : (int)$countRaw;

    // 公開記事を全件取得→表示分だけ切り出す（残りがあれば「すべて見る」リンクを出す）
    $all   = listnews_items(true, 0);
    $total = count($all);
    $shown = $limit > 0 ? array_slice($all, 0, $limit) : $all;

    $rows = [];
    foreach ($shown as $it) {
        $img = (string)$it['image'];
        $rows[] = [
            'url'       => listnews_detail_url((int)$it['id']),
            'date'      => listnews_format_date((string)$it['item_date']),
            'title'     => (string)$it['title'],
            'has_image' => $img !== '',
            'image_url' => $img !== '' ? upload_url($img) : '',
        ];
    }

    // 「すべて見る」リンク：表示件数より実記事が多く、かつ文言が設定されている時だけ。
    // リンク先はお知らせ一覧ページ（?p=news）。
    $moreText = trim((string)($data['more_text'] ?? ''));
    $hasMore  = ($limit > 0 && $total > $limit && $moreText !== '');

    return [
        'HAS_ITEMS' => $rows !== [],
        'ITEMS'     => $rows,
        'HAS_MORE'  => $hasMore,
        'MORE_TEXT' => $moreText,
        'MORE_URL'  => page_url(LISTNEWS_SLUG),
    ];
}

/* ============================================================
 *  描画：詳細ページ（cms_register_route で公開ルーティングに登録）
 *  ?p=news&id=123 のとき、現在のフレームのガワで包んで返す。
 * ============================================================ */
function listnews_route(string $slug): ?string {
    if ($slug !== LISTNEWS_SLUG) return null;
    $id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
    if ($id <= 0) return null; // id無し → 通常のページ表示（一覧ページ）に任せる

    $it = listnews_find($id);
    if (!$it || (string)($it['status'] ?? 'public') !== 'public') {
        http_response_code(404);
        $back = h(page_url(LISTNEWS_SLUG));
        $html = '<section class="news-detail-parts"><div class="news-detail-inner">'
              . '<h1 class="news-detail-title">記事が見つかりません</h1>'
              . '<p>このお知らせは削除されたか、公開されていません。</p>'
              . '<p class="news-detail-back news-detail-back-bottom"><a href="' . $back . '">← お知らせ一覧へ戻る</a></p>'
              . '</div></section>';
        return render_site_shell(LISTNEWS_SLUG, 'お知らせ', $html, '', false);
    }

    $content = listnews_detail_html($it);
    // メタディスクリプション：本文先頭120文字（タグ・改行を除去）
    $excerpt = trim(preg_replace('/\s+/', ' ', strip_tags((string)$it['body'])));
    $excerpt = mb_substr($excerpt, 0, 120);
    return render_site_shell(LISTNEWS_SLUG, (string)$it['title'], $content, $excerpt, false);
}

/** 詳細ページの本文HTML（フレーム非依存・cms_safe な出力） */
function listnews_detail_html(array $it): string {
    $title = h((string)$it['title']);
    $date  = h(listnews_format_date((string)$it['item_date']));
    $img   = (string)$it['image'] !== ''
        ? '<figure class="news-detail-figure"><img src="' . h(upload_url((string)$it['image'])) . '" alt=""></figure>'
        : '';
    $body  = safe_richtext((string)$it['body']); // エスケープ＋改行<br>＋安全タグのみ
    $back  = h(page_url(LISTNEWS_SLUG));

    return '<section class="news-detail-parts"><div class="news-detail-inner">'
         . '<p class="news-detail-back"><a href="' . $back . '">← お知らせ一覧へ戻る</a></p>'
         . ($date !== '' ? '<p class="news-detail-date">' . $date . '</p>' : '')
         . '<h1 class="news-detail-title">' . $title . '</h1>'
         . $img
         . '<div class="news-detail-body">' . $body . '</div>'
         . '<p class="news-detail-back news-detail-back-bottom"><a href="' . $back . '">← お知らせ一覧へ戻る</a></p>'
         . '</div></section>';
}
