<?php
declare(strict_types=1);

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

// 未ログインならログインへ
auth_require_login();

// メッセージ（?ok=1 / ?err=...）
$ok  = (isset($_GET['ok']) && (string)$_GET['ok'] === '1');
$err = isset($_GET['err']) ? (string)$_GET['err'] : '';

// --- セッション開始＆CSRFトークン発行（未発行なら作成） ---
if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}
if (empty($_SESSION['_csrf'])) {
    $_SESSION['_csrf'] = bin2hex(random_bytes(32)); // 64桁
}

// --- SQLite 接続 & テーブル（items）作成：暫定スキーマ ---
require_once __DIR__ . '/../db.php';
$__pdo = db();

// items：カテゴリ別データの最小カラム（本文は body に）
$__pdo->exec('CREATE TABLE IF NOT EXISTS items (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    category   TEXT NOT NULL,
    title      TEXT NOT NULL DEFAULT "",
    body       TEXT NOT NULL DEFAULT "",
    asset_file TEXT NOT NULL DEFAULT "",
    active     INTEGER NOT NULL DEFAULT 1,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
)');

// 既存DB互換：active列が無い環境向けに後付け（あれば例外→握りつぶし）
try { $__pdo->exec('ALTER TABLE items ADD COLUMN active INTEGER NOT NULL DEFAULT 1'); } catch (Throwable $e) {}

$__pdo->exec('CREATE TABLE IF NOT EXISTS item_values (
    id        INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id   INTEGER NOT NULL,
    field_key TEXT    NOT NULL,
    value     TEXT    NOT NULL DEFAULT "",
    UNIQUE(item_id, field_key)
)');
$__pdo->exec('CREATE INDEX IF NOT EXISTS idx_item_values_item ON item_values(item_id)');

// -------- URLパラメータ受け取り（POSTへ引き継ぎ） --------
$categorySlug = isset($_GET['category']) ? (string)$_GET['category'] : '';
$categorySlug = preg_replace('~[^A-Za-z0-9_\-]+~', '', $categorySlug); // 英数/_/-

$idParam = isset($_GET['id']) ? (string)$_GET['id'] : 'new';
$idParam = ($idParam === 'new') ? 'new' : (string)max(1, (int)$idParam); // new or 数値

$modeParam = isset($_GET['mode']) ? (string)$_GET['mode'] : '';
$modeParam = ($modeParam === 'copy') ? 'copy' : '';
$isCopy = ($modeParam === 'copy');

// 保存後プレビュー用（?file=...）
$fileParam = isset($_GET['file']) ? (string)$_GET['file'] : '';
$fileParam = preg_replace('~[^A-Za-z0-9_\.\-]+~', '', $fileParam); // ファイル名のみ許可

// フラッシュ値を取得（取得後は破棄）
$titleVal  = '';
$descVal   = '';
$activeVal = 1;
if (isset($_SESSION['_flash_form']) && is_array($_SESSION['_flash_form'])) {
    $titleVal  = (string)($_SESSION['_flash_form']['title']  ?? '');
    $descVal   = (string)($_SESSION['_flash_form']['desc']   ?? '');
    $activeVal = (int)($_SESSION['_flash_form']['active']    ?? 1);
    unset($_SESSION['_flash_form']);
}

// 既存データの読み込み（id が数値なら）
if ($idParam !== 'new' && ctype_digit((string)$idParam)) {
    $stmt = $__pdo->prepare('SELECT title, body, asset_file, active FROM items WHERE id = ?');
    $stmt->execute([(int)$idParam]);
    if ($row = $stmt->fetch()) {
        if ($titleVal === '') { $titleVal = (string)$row['title']; }
        if ($descVal  === '') { $descVal  = (string)$row['body']; }
        if ($fileParam === '' && !empty($row['asset_file'])) {
            $fileParam = (string)$row['asset_file']; // プレビュー用に既存を適用
        }
        if ($err === '') { $activeVal = (int)($row['active'] ?? 1); }
    }
}

// 画像をJPEGに再エンコード（Exif除去＆向き補正・リサイズ）して保存する
// 透明PNG/GIF/WebPは白背景にフラット化してJPEG化します。
function tpcms_reencode_to_jpeg(string $srcTmp, string $destPath, int $maxW = 1600, int $maxH = 1600, int $quality = 85): bool {
    $fi = new finfo(FILEINFO_MIME_TYPE);
    $mime = (string)($fi->file($srcTmp) ?: '');
    $isImage = in_array($mime, ['image/jpeg','image/png','image/gif','image/webp'], true);
    if (!$isImage) return false;

    // 画像読み込み
    switch ($mime) {
        case 'image/jpeg':
            $im = @imagecreatefromjpeg($srcTmp);
            break;
        case 'image/png':
            $im = @imagecreatefrompng($srcTmp);
            break;
        case 'image/gif':
            $im = @imagecreatefromgif($srcTmp);
            break;
        case 'image/webp':
            if (function_exists('imagecreatefromwebp')) {
                $im = @imagecreatefromwebp($srcTmp);
            } else {
                return false;
            }
            break;
        default:
            return false;
    }
    if (!$im) return false;

    // 向き補正（JPEGのみEXIF）
    if ($mime === 'image/jpeg' && function_exists('exif_read_data')) {
        $exif = @exif_read_data($srcTmp);
        $orientation = isset($exif['Orientation']) ? (int)$exif['Orientation'] : 0;
        if ($orientation === 3)      { $im = imagerotate($im, 180, 0); }
        elseif ($orientation === 6)  { $im = imagerotate($im, -90, 0); }
        elseif ($orientation === 8)  { $im = imagerotate($im, 90, 0); }
    }

    $w = imagesx($im);
    $h = imagesy($im);
    $scale = min($maxW / max($w,1), $maxH / max($h,1), 1);
    $nw = (int)floor($w * $scale);
    $nh = (int)floor($h * $scale);

    $dst = imagecreatetruecolor(max($nw,1), max($nh,1));
    // 透明系は白でフラット化（JPEGのため）
    $white = imagecolorallocate($dst, 255, 255, 255);
    imagefilledrectangle($dst, 0, 0, max($nw-1,0), max($nh-1,0), $white);

    imagecopyresampled($dst, $im, 0, 0, 0, 0, $nw, $nh, $w, $h);

    $ok = imagejpeg($dst, $destPath, $quality); // Exifは付与しない＝除去
    imagedestroy($im);
    imagedestroy($dst);
    if ($ok) { @chmod($destPath, 0664); }
    return $ok;
}

// -------- POST 受信（DB保存：新規/更新） --------
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // CSRF
    $csrfPost = (string)($_POST['_csrf'] ?? '');
    $csrfSess = (string)($_SESSION['_csrf'] ?? '');
    if ($csrfPost === '' || $csrfSess === '' || !hash_equals($csrfSess, $csrfPost)) {
        header('Location: ./regist.php?err=' . rawurlencode('フォームの有効期限が切れました。もう一度お試しください。') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
        exit;
    }

    // POST優先で category/id/mode を上書き
    $__catPost = (string)($_POST['category'] ?? '');
    $__catPost = preg_replace('~[^A-Za-z0-9_\-]+~', '', $__catPost);
    if ($__catPost !== '') { $categorySlug = $__catPost; }

    $__idPost = (string)($_POST['id'] ?? 'new');
    $idParam  = ($__idPost === 'new') ? 'new' : (string)max(1, (int)$__idPost);

    $__modePost = (string)($_POST['mode'] ?? '');
    $modeParam  = ($__modePost === 'copy') ? 'copy' : '';

    // 複製モードなら必ず新規保存にする（表示は元データ、保存は新規）
    $copySrcId = null;
    if ($modeParam === 'copy' && $idParam !== 'new') {
        $copySrcId = (int)$idParam; // 元データID（次ステップで画像引き継ぎに使用）
        $idParam = 'new';
    }

    // フォーム値
    $title = trim((string)($_POST['title'] ?? ''));
    $desc  = trim((string)($_POST['desc']  ?? ''));

    // 複製モード時：UIからtitle/descを受け取らないため、元データがあれば継承
    if ($title === '' || $desc === '') {
        if (isset($copySrcId) && is_int($copySrcId) && $copySrcId > 0) {
            $stCopy = $__pdo->prepare('SELECT title, body FROM items WHERE id = ?');
            $stCopy->execute([$copySrcId]);
            if ($src = $stCopy->fetch(PDO::FETCH_ASSOC)) {
                if ($title === '') { $title = (string)($src['title'] ?? ''); }
                if ($desc  === '') { $desc  = (string)($src['body']  ?? ''); }
            }
        }
    }

    // 公開/非公開（未指定は公開）
    $active = ((string)($_POST['active'] ?? '1') === '0') ? 0 : 1;

    // タイトルは必須（200文字上限）
    $len = function_exists('mb_strlen') ? mb_strlen($title, 'UTF-8') : strlen($title);
    if ($title === '' || $len > 200) {
        // 入力のフラッシュ保存（戻り先で再表示）
        $_SESSION['_flash_form'] = ['title' => $title, 'desc' => $desc, 'active' => $active];

        // カスタム項目 cf[] も保存（checkboxはカンマ連結）
        $__cfFlash = [];
        if (isset($_POST['cf']) && is_array($_POST['cf'])) {
            foreach ($_POST['cf'] as $k => $raw) {
                $key = (string)$k;
                if (!preg_match('/^[A-Za-z0-9_-]+$/', $key)) continue;
                if (is_array($raw)) {
                    $vals = array_values(array_filter(array_map('trim', $raw), 'strlen'));
                    $__cfFlash[$key] = implode(',', $vals);
                } else {
                    $__cfFlash[$key] = trim((string)$raw);
                }
            }
        }
        $_SESSION['_flash_cf'] = $__cfFlash;

        $msg = ($title === '') ? 'タイトルは必須です' : 'タイトルが長すぎます（200文字まで）';
        $q = 'err=' . rawurlencode($msg)
           . '&category=' . rawurlencode($categorySlug)
           . '&id=' . rawurlencode($idParam)
           . '&mode=' . rawurlencode($modeParam);
        header('Location: ./regist.php?' . $q);
        exit;
    }

    /* ---------- 必須項目バリデーション：配布版では無効化（タイトルのみ必須） ---------- */
    // fields.required は現行配布版では使用しません（タイトル必須は上で維持）

    // アップロード処理（画像は再エンコード、動画は原本保存）
    $fileParamForRedirect = '';
    $newAsset = null; // 今回のPOSTで新規保存したファイル名（なければ null）
    if (isset($_FILES['file1']) && is_array($_FILES['file1']) && (int)$_FILES['file1']['error'] !== UPLOAD_ERR_NO_FILE) {
        $f = $_FILES['file1'];
        if ((int)$f['error'] === UPLOAD_ERR_OK && is_uploaded_file($f['tmp_name'])) {
            $finfo = new finfo(FILEINFO_MIME_TYPE);
            $mime = (string)($finfo->file($f['tmp_name']) ?: '');
            
            // ---- サイズ上限チェック（必要に応じて調整可）----
            $size = (int)($f['size'] ?? 0);
            $maxImg = 5 * 1024 * 1024;    // 5MB
            $maxMov = 200 * 1024 * 1024;  // 200MB

            $updir = realpath(__DIR__ . '/../uploads') ?: (__DIR__ . '/../uploads');
            if (!is_dir($updir)) { @mkdir($updir, 0775, true); }

            if (in_array($mime, ['image/jpeg','image/png','image/gif','image/webp'], true)) {
                if ($size > $maxImg) {
                    header('Location: ./regist.php?err=' . rawurlencode('画像サイズが大きすぎます（上限:5MB）') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                    exit;
                }
                $bn   = date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.jpg';
                $dest = $updir . '/' . $bn;
                if (!tpcms_reencode_to_jpeg($f['tmp_name'], $dest, 1600, 1600, 85)) {
                    header('Location: ./regist.php?err=' . rawurlencode('画像の変換に失敗しました') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                    exit;
                }
                $newAsset = $bn;
            } elseif (in_array($mime, ['video/mp4','video/webm','video/quicktime'], true)) {
                if ($size > $maxMov) {
                    header('Location: ./regist.php?err=' . rawurlencode('動画サイズが大きすぎます（上限:200MB）') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                    exit;
                }
                $extMap = ['video/mp4'=>'mp4','video/webm'=>'webm','video/quicktime'=>'mov'];
                $ext = $extMap[$mime] ?? 'bin';
                $bn  = date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
                $dest = $updir . '/' . $bn;
                if (!move_uploaded_file($f['tmp_name'], $dest)) {
                    header('Location: ./regist.php?err=' . rawurlencode('アップロードに失敗しました') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                    exit;
                }
                @chmod($dest, 0664);
                $newAsset = $bn;
            } else {
                header('Location: ./regist.php?err=' . rawurlencode('このファイル形式はアップロードできません') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                exit;
            }
            $fileParamForRedirect = (string)$newAsset;
        } else {
            header('Location: ./regist.php?err=' . rawurlencode('アップロードに失敗しました') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
            exit;
        }
    }

    // --- 上書きアップ時は旧ファイルを物理削除 ---
    if ($newAsset !== null && $idParam !== 'new') {
        $stmt = $__pdo->prepare('SELECT asset_file FROM items WHERE id = ?');
        $stmt->execute([(int)$idParam]);
        $old = (string)($stmt->fetchColumn() ?: '');
        if ($old !== '' && $old !== $newAsset) {
            $path = realpath(__DIR__ . '/../uploads') ?: (__DIR__ . '/../uploads');
            $oldPath = $path . '/' . $old;
            if (is_file($oldPath)) { @unlink($oldPath); }
        }
    }

    // --- 複製モード：元データのファイルを物理コピーして引き継ぐ（今回アップが無い場合） ---
    if ($newAsset === null && isset($copySrcId) && is_int($copySrcId) && $copySrcId > 0) {
        $stmt = $__pdo->prepare('SELECT asset_file FROM items WHERE id = ?');
        $stmt->execute([$copySrcId]);
        $srcFn = (string)($stmt->fetchColumn() ?: '');
        if ($srcFn !== '') {
            $updir = realpath(__DIR__ . '/../uploads') ?: (__DIR__ . '/../uploads');
            $srcPath = $updir . '/' . $srcFn;
            if (is_file($srcPath)) {
                $ext = strtolower(pathinfo($srcFn, PATHINFO_EXTENSION));
                if ($ext === '') { $ext = 'jpg'; } // 念のため
                $newFn = date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
                $destPath = $updir . '/' . $newFn;
                if (@copy($srcPath, $destPath)) {
                    @chmod($destPath, 0664);
                    $newAsset = $newFn;               // 今回保存レコードにこのファイル名を使用
                    $fileParamForRedirect = $newFn;   // 直後のリダイレクトでプレビュー表示
                }
            }
        }
    }

    // 削除チェック（今回アップなしのときはここで旧ファイルを物理削除）
    $deleteAsset = (isset($_POST['delete_asset']) && (string)$_POST['delete_asset'] === '1');
    if ($deleteAsset && $idParam !== 'new' && $newAsset === null) {
        $stmt = $__pdo->prepare('SELECT asset_file FROM items WHERE id = ?');
        $stmt->execute([(int)$idParam]);
        $old = (string)($stmt->fetchColumn() ?: '');
        if ($old !== '') {
            $path = realpath(__DIR__ . '/../uploads') ?: (__DIR__ . '/../uploads');
            $oldPath = $path . '/' . $old;
            if (is_file($oldPath)) { @unlink($oldPath); }
        }
    }

    // 既存アセットを引き継ぐ（更新時・今回アップなし＋削除対応）
    $assetToStore = ($newAsset !== null) ? $newAsset : '';
    if ($idParam !== 'new' && $newAsset === null) {
        if (!empty($deleteAsset)) {
            $assetToStore = '';
        } else {
            $stmt = $__pdo->prepare('SELECT asset_file FROM items WHERE id = ?');
            $stmt->execute([(int)$idParam]);
            $assetToStore = (string)($stmt->fetchColumn() ?: '');
        }
    }

    // DB保存：新規 or 更新
    $now = date('c');
    if ($idParam === 'new') {
        $stmt = $__pdo->prepare('INSERT INTO items (category, title, body, asset_file, active, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)');
        $stmt->execute([$categorySlug, $title, $desc, $assetToStore, $active, $now, $now]);
        $idParam = (string)$__pdo->lastInsertId();
    } else {
        $stmt = $__pdo->prepare('UPDATE items SET category=?, title=?, body=?, asset_file=?, active=?, updated_at=? WHERE id=?');
        $stmt->execute([$categorySlug, $title, $desc, $assetToStore, $active, $now, (int)$idParam]);
    }

    // ---------- カテゴリ項目（text/textarea）の保存（単一DB・簡易版） ----------
    try {
        $savedId = (int)$idParam;
        if ($savedId > 0 && $categorySlug !== '') {
            // このカテゴリで text/textarea のキーを収集（単一DB上の fields から）
            $wl = [];
            $stCid = $__pdo->prepare('SELECT id FROM categories WHERE slug = ? LIMIT 1');
            $stCid->execute([$categorySlug]);
            if ($cid = (int)$stCid->fetchColumn()) {
                $stF = $__pdo->prepare(
                    'SELECT "key", type, options FROM fields WHERE category_id = ? AND type IN ("text","textarea","select","radio","checkbox","number","currency","date","map_iframe")'
                );
                $stF->execute([$cid]);
                $fieldRows = $stF->fetchAll(PDO::FETCH_ASSOC) ?: []; // ← 1回だけ取得して使い回す
                foreach ($fieldRows as $f) {
                    $k = (string)($f['key'] ?? '');
                    if ($k !== '') $wl[$k] = true;
                }
            }

            // --- フォールバック：fields 取得に失敗しても cf[] のキーを正規化して保存 ---
            if (empty($wl) && isset($_POST['cf']) && is_array($_POST['cf'])) {
                foreach (array_keys($_POST['cf']) as $k) { if (preg_match('/^[A-Za-z0-9_-]+$/', (string)$k)) $wl[(string)$k] = true; }
            }

            if (!empty($wl)) {
                $cf = (isset($_POST["cf"]) && is_array($_POST["cf"])) ? $_POST["cf"] : [];
                // テーブルが無ければ作成（保険）
                $__pdo->exec('CREATE TABLE IF NOT EXISTS item_values (
                    id        INTEGER PRIMARY KEY AUTOINCREMENT,
                    item_id   INTEGER NOT NULL,
                    field_key TEXT    NOT NULL,
                    value     TEXT    NOT NULL DEFAULT "",
                    UNIQUE(item_id, field_key)
                )');
                $__pdo->exec('CREATE INDEX IF NOT EXISTS idx_item_values_item ON item_values(item_id)');

                $up = $__pdo->prepare(
                    'INSERT OR REPLACE INTO item_values (item_id, field_key, value) VALUES (?, ?, ?)'
                );
                
                /* optionsの正当値セットを用意（select/radio/checkboxのみ） */
                $optMap = [];
                foreach ($fieldRows as $f) {
                    $k = (string)($f['key'] ?? '');
                    $t = (string)($f['type'] ?? '');
                    if ($k === '' || !in_array($t, ['select','radio','checkbox'], true)) continue;

                    $set = [];
                    $optsJ = (string)($f['options'] ?? '');
                    if ($optsJ !== '') {
                        $arr = json_decode($optsJ, true);
                        if (is_array($arr)) {
                            foreach ($arr as $o) {
                                $v = isset($o['value']) ? (string)$o['value'] : '';
                                if ($v !== '') $set[$v] = true;
                            }
                        }
                    }
                    $optMap[$k] = $set;
                }

                /* 保存ループ（選択肢外は破棄） */
                foreach ($wl as $key => $_) {
                    $raw = $cf[$key] ?? '';
                    if (is_array($raw)) {
                        // checkbox: 配列 → trim→空除外→正当値だけに限定→カンマ連結
                        $vals = array_values(array_filter(array_map('trim', $raw), 'strlen'));
                        if (!empty($optMap[$key])) {
                            $set  = $optMap[$key];
                            $vals = array_values(array_filter($vals, fn($v) => isset($set[$v])));
                        }
                        $val = implode(',', $vals);
                    } else {
                        // text/textarea/select/radio
                        $val = trim((string)$raw);
                        if (!empty($optMap[$key]) && $val !== '' && !isset($optMap[$key][$val])) {
                            $val = ''; // 選択肢外は破棄
                        }
                    }

                    // 長さ制限（安全のため2000文字）
                    if (function_exists('mb_substr')) {
                        $val = mb_substr($val, 0, 2000, 'UTF-8');
                    } else {
                        $val = (strlen($val) > 2000) ? substr($val, 0, 2000) : $val;
                    }

                    $up->execute([$savedId, $key, $val]);
                }
            }
        }
    } catch (Throwable $e) {
        // 将来ログ送信など
    }

    // ---------- 複製モード：assets の物理コピー（新規IDに値が未設定のキーのみ） ----------
    try {
        if (isset($copySrcId) && is_int($copySrcId) && $copySrcId > 0) {
            $savedId = (int)$idParam; // 既にINSERT済みなので数値
            if ($savedId > 0 && $categorySlug !== '') {
                // カテゴリID
                $stCidC = $__pdo->prepare('SELECT id FROM categories WHERE slug = ? LIMIT 1');
                $stCidC->execute([$categorySlug]);
                $cidC = (int)$stCidC->fetchColumn();

                if ($cidC > 0) {
                    // このカテゴリの assets キー一覧
                    $stKeys = $__pdo->prepare('SELECT "key" FROM fields WHERE category_id = ? AND type = "assets" ORDER BY ord ASC, id ASC');
                    $stKeys->execute([$cidC]);
                    $assetKeys = [];
                    foreach ($stKeys->fetchAll(PDO::FETCH_ASSOC) ?: [] as $r) {
                        $k = (string)($r['key'] ?? '');
                        if ($k !== '' && preg_match('/^[A-Za-z0-9_-]+$/', $k)) {
                            $assetKeys[] = $k;
                        }
                    }

                    if (!empty($assetKeys)) {
                        $updir = realpath(__DIR__ . '/../uploads') ?: (__DIR__ . '/../uploads');
                        if (!is_dir($updir)) { @mkdir($updir, 0775, true); }

                        $selSrc = $__pdo->prepare('SELECT value FROM item_values WHERE item_id = ? AND field_key = ? LIMIT 1');
                        $selDst = $__pdo->prepare('SELECT value FROM item_values WHERE item_id = ? AND field_key = ? LIMIT 1');
                        $saveIv = $__pdo->prepare('INSERT OR REPLACE INTO item_values (item_id, field_key, value) VALUES (?, ?, ?)');

                        foreach ($assetKeys as $akey) {
                            // すでに新アイテム側に値がある（＝直後のアップロード等で保存済み）の場合はスキップ
                            $selDst->execute([$savedId, $akey]);
                            $dstVal = (string)($selDst->fetchColumn() ?: '');
                            if ($dstVal !== '') continue;

                            // コピー元の値
                            $selSrc->execute([$copySrcId, $akey]);
                            $srcVal = (string)($selSrc->fetchColumn() ?: '');
                            if ($srcVal === '') continue;

                            $srcPath = $updir . '/' . $srcVal;
                            if (!is_file($srcPath)) continue;

                            $ext = strtolower(pathinfo($srcVal, PATHINFO_EXTENSION));
                            if ($ext === '') $ext = 'jpg'; // 念のため
                            $newName = date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
                            $dstPath = $updir . '/' . $newName;

                            if (@copy($srcPath, $dstPath)) {
                                @chmod($dstPath, 0664);
                                $saveIv->execute([$savedId, $akey, $newName]);
                            }
                        }
                    }
                }
            }
        }
    } catch (Throwable $e) {
        // 将来ログ送信など
    }

    // ---------- assets型の保存（新規/更新） ----------
    try {
        $savedId = (int)$idParam;
        if ($savedId > 0 && $categorySlug !== '') {
            // カテゴリID
            $stCidA = $__pdo->prepare('SELECT id FROM categories WHERE slug = ? LIMIT 1');
            $stCidA->execute([$categorySlug]);
            $cidA = (int)$stCidA->fetchColumn();

            if ($cidA > 0) {
                // このカテゴリの assets キー一覧
                $stA = $__pdo->prepare('SELECT "key" FROM fields WHERE category_id = ? AND type = "assets" ORDER BY ord ASC, id ASC');
                $stA->execute([$cidA]);
                $assetKeys = [];
                foreach ($stA->fetchAll(PDO::FETCH_ASSOC) ?: [] as $r) {
                    $k = (string)($r['key'] ?? '');
                    if ($k !== '' && preg_match('/^[A-Za-z0-9_-]+$/', $k)) $assetKeys[] = $k;
                }

                if (!empty($assetKeys)) {
                    $files = $_FILES['cf_file'] ?? null; // name="cf_file[KEY]"
                    $dels  = (isset($_POST['cf_del']) && is_array($_POST['cf_del'])) ? $_POST['cf_del'] : [];

                    $updir = realpath(__DIR__ . '/../uploads') ?: (__DIR__ . '/../uploads');
                    if (!is_dir($updir)) { @mkdir($updir, 0775, true); }

                    $fi = new finfo(FILEINFO_MIME_TYPE);
                    $maxImg = 5 * 1024 * 1024;    // 画像上限 5MB
                    $maxMov = 200 * 1024 * 1024;  // 動画上限 200MB
                    $videoMap = ['video/mp4'=>'mp4','video/webm'=>'webm','video/quicktime'=>'mov'];

                    $selOld = $__pdo->prepare('SELECT value FROM item_values WHERE item_id = ? AND field_key = ? LIMIT 1');
                    $saveIv = $__pdo->prepare('INSERT OR REPLACE INTO item_values (item_id, field_key, value) VALUES (?, ?, ?)');

                    foreach ($assetKeys as $akey) {
                        $newName = null;
                        $delFlag = (isset($dels[$akey]) && (string)$dels[$akey] === '1');

                        // 既存値
                        $selOld->execute([$savedId, $akey]);
                        $oldVal = (string)($selOld->fetchColumn() ?: '');

                        // 新規アップ有無
                        $tmp = $files['tmp_name'][$akey] ?? null;
                        $err = $files['error'][$akey]    ?? UPLOAD_ERR_NO_FILE;

                        if ($tmp && (int)$err === UPLOAD_ERR_OK && is_uploaded_file($tmp)) {
                            $mime = (string)($fi->file($tmp) ?: '');
                            $size = (int)($files['size'][$akey] ?? 0);

                            if (in_array($mime, ['image/jpeg','image/png','image/gif','image/webp'], true)) {
                                if ($size > $maxImg) {
                                    header('Location: ./regist.php?err=' . rawurlencode('画像サイズが大きすぎます（上限:5MB）') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                                    exit;
                                }
                                $newName = date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.jpg';
                                $dest = $updir . '/' . $newName;
                                if (!tpcms_reencode_to_jpeg($tmp, $dest, 1600, 1600, 85)) {
                                    header('Location: ./regist.php?err=' . rawurlencode('画像の変換に失敗しました') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                                    exit;
                                }
                            } elseif (isset($videoMap[$mime])) {
                                if ($size > $maxMov) {
                                    header('Location: ./regist.php?err=' . rawurlencode('動画サイズが大きすぎます（上限:200MB）') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                                    exit;
                                }
                                $ext  = $videoMap[$mime];
                                $newName = date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
                                $dest = $updir . '/' . $newName;
                                if (!move_uploaded_file($tmp, $dest)) {
                                    header('Location: ./regist.php?err=' . rawurlencode('アップロードに失敗しました') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                                    exit;
                                }
                                @chmod($dest, 0664);
                            } else {
                                header('Location: ./regist.php?err=' . rawurlencode('このファイル形式はアップロードできません') . '&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam));
                                exit;
                            }

                            // 旧実体の物理削除
                            if ($oldVal !== '' && $oldVal !== $newName) {
                                $oldPath = $updir . '/' . $oldVal;
                                if (is_file($oldPath)) { @unlink($oldPath); }
                            }

                            // 値を保存（新規/更新）
                            $saveIv->execute([$savedId, $akey, $newName]);

                        } else {
                            // 新規アップなし：削除チェックがONなら削除＆空保存
                            if ($delFlag && $oldVal !== '') {
                                $oldPath = $updir . '/' . $oldVal;
                                if (is_file($oldPath)) { @unlink($oldPath); }
                                $saveIv->execute([$savedId, $akey, '']);
                            }
                            // それ以外は既存値維持（何もしない）
                        }
                    }
                }
            }
        }
    } catch (Throwable $e) {
        // 将来ログ送信など
    }

    // フラッシュ（保存直後の再表示用）
    $_SESSION['_flash_form'] = [
        'title'  => $title,
        'desc'   => $desc,
        'active' => $active,
    ];

    // リダイレクト
    $q = 'ok=1&category=' . rawurlencode($categorySlug) . '&id=' . rawurlencode($idParam) . '&mode=' . rawurlencode($modeParam);
    if ($fileParamForRedirect !== '') { $q .= '&file=' . rawurlencode($fileParamForRedirect); }
    header('Location: ./items.php?category=' . rawurlencode($categorySlug) . '&ok=1');
    exit;
}

 // ---------- カテゴリ項目（text/textarea）の読み込み（単一DB版） ----------
 $__customFields = [];
 $__cfValues     = [];
 try {
     if ($categorySlug !== '') {
         $stCid = $__pdo->prepare('SELECT id FROM categories WHERE slug = ? LIMIT 1');
         $stCid->execute([$categorySlug]);
         $cid = (int)$stCid->fetchColumn();
         if ($cid > 0) {
             $stF = $__pdo->prepare('SELECT "key", label, type, options FROM fields WHERE category_id = ? AND type IN ("text","textarea","select","radio","checkbox","assets","number","currency","date","map_iframe") ORDER BY ord ASC, id ASC');
             $stF->execute([$cid]);
             $__customFields = $stF->fetchAll(PDO::FETCH_ASSOC) ?: [];
         }
     }
     if ($idParam !== 'new' && ctype_digit((string)$idParam)) {
         $stV = $__pdo->prepare('SELECT field_key, value FROM item_values WHERE item_id = ?');
         $stV->execute([(int)$idParam]);
         foreach ($stV->fetchAll(PDO::FETCH_ASSOC) ?: [] as $r) {
             $k = (string)($r['field_key'] ?? '');
             if ($k !== '') { $__cfValues[$k] = (string)($r['value'] ?? ''); }
         }
     }
 } catch (Throwable $e) {
     $__customFields = [];
     $__cfValues     = [];
 }

// --- エラー時のフラッシュ再表示（カスタム項目） ---
if (isset($_SESSION['_flash_cf']) && is_array($_SESSION['_flash_cf'])) {
    foreach ($_SESSION['_flash_cf'] as $k => $v) { $__cfValues[(string)$k] = (string)$v; }
    unset($_SESSION['_flash_cf']);
}

?>
<!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 regist">

<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>
  <p class="meta">カテゴリ: <span class="mono"><?= htmlspecialchars($categorySlug, ENT_QUOTES, 'UTF-8') ?></span><?= $isCopy ? '（複製）' : '' ?></p>

  <?php if ($ok): ?>
    <p class="ok">保存しました</p>
  <?php endif; ?>
  <?php if ($err !== ''): ?>
    <p class="err"><?= htmlspecialchars($err, ENT_QUOTES, 'UTF-8') ?></p>
  <?php endif; ?>

  <form method="post" action="./regist.php" enctype="multipart/form-data">
    <input type="hidden" name="_csrf" value="<?= htmlspecialchars($_SESSION['_csrf'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
    <input type="hidden" name="category" value="<?= htmlspecialchars($categorySlug, ENT_QUOTES, 'UTF-8') ?>">
    <input type="hidden" name="id"        value="<?= htmlspecialchars($idParam, ENT_QUOTES, 'UTF-8') ?>">
    <input type="hidden" name="mode"      value="<?= htmlspecialchars($modeParam, ENT_QUOTES, 'UTF-8') ?>">

    <div class="row">
      <label class="label" for="title">タイトル</label>
      <input class="input" id="title" type="text" name="title" value="<?= htmlspecialchars($titleVal, ENT_QUOTES, 'UTF-8') ?>" required>
    </div>

    <?php if (!empty($__customFields)): ?>

        <?php foreach ($__customFields as $__f): ?>
          <?php
            $__key   = (string)($__f['key']   ?? '');
            $__label = (string)($__f['label'] ?? $__key);
            $__type  = (string)($__f['type']  ?? 'text');
            $__optsJ = (string)($__f['options'] ?? '');
            if ($__key === '') continue;
            $___v = (string)($__cfValues[$__key] ?? '');

            // options: [{"value":"v","label":"L"}, ...]
            $__opts = [];
            if ($__optsJ !== '') {
              $tmp = json_decode($__optsJ, true);
              if (is_array($tmp)) {
                foreach ($tmp as $o) {
                  $v = isset($o['value']) ? (string)$o['value'] : '';
                  $l = isset($o['label']) ? (string)$o['label'] : $v;
                  if ($v !== '') $__opts[] = ['v' => $v, 'l' => $l];
                }
              }
            }
          ?>
          <?php if ($__type === 'text'): ?>
            <div class="row">
              <label class="label" for="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></label>
              <input class="input" id="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>" type="text" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>]" value="<?= htmlspecialchars($___v, ENT_QUOTES, 'UTF-8') ?>">
            </div>

          <?php elseif ($__type === 'textarea'): ?>
            <div class="row">
              <label class="label" for="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></label>
              <textarea class="input" id="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>]"><?= htmlspecialchars($___v, ENT_QUOTES, 'UTF-8') ?></textarea>
            </div>

          <?php elseif ($__type === 'select'): ?>
            <div class="row">
              <label class="label" for="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></label>
              <select class="input" id="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>]">
                <option value="">選択してください</option>
                <?php foreach ($__opts as $o): ?>
                  <option value="<?= htmlspecialchars($o['v'], ENT_QUOTES, 'UTF-8') ?>" <?= ($o['v'] === $___v ? 'selected' : '') ?>>
                    <?= htmlspecialchars($o['l'], ENT_QUOTES, 'UTF-8') ?>
                  </option>
                <?php endforeach; ?>
              </select>
            </div>

          <?php elseif ($__type === 'radio'): ?>
            <div class="row">
              <span class="label"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></span>
              <?php foreach ($__opts as $o): ?>
                <label>
                  <input type="radio" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>]" value="<?= htmlspecialchars($o['v'], ENT_QUOTES, 'UTF-8') ?>" <?= ($o['v'] === $___v ? 'checked' : '') ?>>
                  <?= htmlspecialchars($o['l'], ENT_QUOTES, 'UTF-8') ?>
                </label>
              <?php endforeach; ?>
            </div>

          <?php elseif ($__type === 'checkbox'): ?>
            <?php
              $__vals   = array_filter(array_map('trim', explode(',', $___v)));
              $__valSet = [];
              foreach ($__vals as $__one) { $__valSet[$__one] = true; }
            ?>
            <div class="row">
              <span class="label"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></span>
              <?php foreach ($__opts as $o): ?>
                <label>
                  <input type="checkbox" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>][]" value="<?= htmlspecialchars($o['v'], ENT_QUOTES, 'UTF-8') ?>" <?= (isset($__valSet[$o['v']]) ? 'checked' : '') ?>>
                  <?= htmlspecialchars($o['l'], ENT_QUOTES, 'UTF-8') ?>
                </label>
              <?php endforeach; ?>
            </div>

          <?php elseif ($__type === 'number'): ?>
            <?php
              // options: {"min":0,"max":999,"step":1}
              $__numMin = $__numMax = $__numStep = '';
              if ($__optsJ !== '') {
                $tmp = json_decode($__optsJ, true);
                if (is_array($tmp)) {
                  if (isset($tmp['min']) && is_numeric($tmp['min'])) $__numMin  = ' min="'.htmlspecialchars((string)$tmp['min'], ENT_QUOTES, 'UTF-8').'"';
                  if (isset($tmp['max']) && is_numeric($tmp['max'])) $__numMax  = ' max="'.htmlspecialchars((string)$tmp['max'], ENT_QUOTES, 'UTF-8').'"';
                  if (isset($tmp['step']) && (is_numeric($tmp['step']) || (is_string($tmp['step']) && $tmp['step']==='any')))
                    $__numStep = ' step="'.htmlspecialchars((string)$tmp['step'], ENT_QUOTES, 'UTF-8').'"';
                }
              }
              if ($__numStep === '') $__numStep = ' step="1"';
            ?>
            <div class="row">
              <label class="label" for="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></label>
              <input class="input" id="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>" type="number" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>]" value="<?= htmlspecialchars($___v, ENT_QUOTES, 'UTF-8') ?>"<?= $__numMin . $__numMax . $__numStep ?>>
            </div>

          <?php elseif ($__type === 'currency'): ?>
            <?php
              // options: {"min":0,"max":999999,"step":0.01} など
              $__curMin = $__curMax = $__curStep = '';
              if ($__optsJ !== '') {
                $tmp = json_decode($__optsJ, true);
                if (is_array($tmp)) {
                  if (isset($tmp['min']) && is_numeric($tmp['min'])) $__curMin  = ' min="'.htmlspecialchars((string)$tmp['min'], ENT_QUOTES, 'UTF-8').'"';
                  if (isset($tmp['max']) && is_numeric($tmp['max'])) $__curMax  = ' max="'.htmlspecialchars((string)$tmp['max'], ENT_QUOTES, 'UTF-8').'"';
                  if (isset($tmp['step']) && (is_numeric($tmp['step']) || (is_string($tmp['step']) && $tmp['step']==='any')))
                    $__curStep = ' step="'.htmlspecialchars((string)$tmp['step'], ENT_QUOTES, 'UTF-8').'"';
                }
              }
              if ($__curStep === '') $__curStep = ' step="0.01"';
            ?>
            <div class="row">
              <label class="label" for="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></label>
              <input class="input" id="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>" type="number" inputmode="decimal" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>]" value="<?= htmlspecialchars($___v, ENT_QUOTES, 'UTF-8') ?>"<?= $__curMin . $__curMax . $__curStep ?>>
            </div>

          <?php elseif ($__type === 'date'): ?>
            <div class="row">
              <label class="label" for="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></label>
              <input class="input" id="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>" type="date" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>]" value="<?= htmlspecialchars($___v, ENT_QUOTES, 'UTF-8') ?>">
            </div>

          <?php elseif ($__type === 'map_iframe'): ?>
            <div class="row">
              <label class="label" for="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($__label, ENT_QUOTES, 'UTF-8') ?></label>
              <textarea class="input" id="cf_<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>" name="cf[<?= htmlspecialchars($__key, ENT_QUOTES, 'UTF-8') ?>]" placeholder="Googleマップ等の埋め込みHTMLを貼り付け"><?= htmlspecialchars($___v, ENT_QUOTES, 'UTF-8') ?></textarea>
            </div>

          <?php elseif ($__type === 'assets'): ?>
            <?php
              // 既存値（編集時のプレビュー用）：item_values にファイル名（拡張子含む）が入っている想定
              $__filev = (string)($___v ?? '');
              $__safeKey = htmlspecialchars($__key, ENT_QUOTES, 'UTF-8');
              $__safeLbl = htmlspecialchars($__label !== '' ? $__label : $__key, ENT_QUOTES, 'UTF-8');
              $__inputId = 'cf_file_' . $__safeKey; // ユニークID
            ?>
            <div class="row" data-uploader="<?= $__safeKey ?>">
              <label class="label" for="<?= $__inputId ?>">
                画像・動画｜<?= $__safeLbl ?>
              </label>
              <input
                id="<?= $__inputId ?>"
                class="input-file"
                type="file"
                name="cf_file[<?= $__safeKey ?>]"
                accept="image/*,video/*"
                hidden>
              <div class="dropzone" role="button" tabindex="0" aria-controls="<?= $__inputId ?>">ここをクリック/ドラッグ</div>

              <div class="filename" aria-live="polite">
                <?= ($__filev !== '' ? htmlspecialchars($__filev, ENT_QUOTES, 'UTF-8') : '') ?>
              </div>

              <div class="image">
                <?php if ($__filev !== ''):
                  $ext = strtolower(pathinfo($__filev, PATHINFO_EXTENSION));
                  $src = '../uploads/' . rawurlencode($__filev);
                  if (in_array($ext, ['jpg','jpeg','png','gif','webp'], true)) {
                    echo '<img src="' . $src . '" alt="">';
                  } elseif (in_array($ext, ['mp4','webm','mov'], true)) {
                    echo '<video src="' . $src . '" controls playsinline></video>';
                  }
                endif; ?>
              </div>

              <?php if ($__filev !== ''): ?>
                <div class="mt05rem">
                  <label>
                    <input type="checkbox" name="cf_del[<?= $__safeKey ?>]" value="1">
                    このファイルを削除
                  </label>
                </div>
              <?php endif; ?>
            </div>

          <?php endif; ?>
        <?php endforeach; ?>

    <?php endif; ?>

    <?php /* [UI撤去] 固定 file1 ブロック（legacy）。将来互換のため items.asset_file / POST側の処理は残置。assets 型で運用します。 */ ?>

    <div class="row">
      <label class="label">公開設定</label>
      <label><input type="radio" name="active" value="1" <?= ($activeVal === 1 ? 'checked' : '') ?>> 公開</label>
      <label><input type="radio" name="active" value="0" <?= ($activeVal === 0 ? 'checked' : '') ?>> 非公開</label>
    </div>

    <div>
      <button class="btn1 mb1rem" type="submit">保存する</button>
    </div>
  </form>

</div><!-- /#container -->

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

<script>
(function(){
  function setupRow(row){
    const dz = row.querySelector('.dropzone');
    const input = row.querySelector('input[type=file]');
    const fn = row.querySelector('.filename');
    const box = row.querySelector('.image');
    if(!dz || !input) return;

    const update = (file)=>{
      if(!file) return;
      if (fn) fn.textContent = file.name;
      if (box) {
        box.innerHTML = '';
        const url = URL.createObjectURL(file);
        if (file.type && file.type.startsWith('image/')) {
          const img = new Image();
          img.src = url;
          img.alt = '';
          box.appendChild(img);
        } else if (file.type && file.type.startsWith('video/')) {
          const v = document.createElement('video');
          v.src = url;
          v.controls = true;
          v.playsInline = true;
          box.appendChild(v);
        }
      }
    };

    dz.addEventListener('click', ()=> input.click());
    dz.addEventListener('keydown', (e)=>{
      if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); input.click(); }
    });

    input.addEventListener('change', ()=> update(input.files && input.files[0]));

    dz.addEventListener('dragover', (e)=>{ e.preventDefault(); dz.classList.add('dragover'); });
    dz.addEventListener('dragleave', ()=> dz.classList.remove('dragover'));
    dz.addEventListener('drop', (e)=>{
      e.preventDefault();
      dz.classList.remove('dragover');
      if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0]) {
        // ここで input.files に代入することで、そのままフォームPOST可能
        input.files = e.dataTransfer.files;
        update(input.files[0]);
      }
    });
  }

  document.querySelectorAll('.row[data-uploader]').forEach(setupRow);
})();
</script>

</body>
</html>
