<?php
declare(strict_types=1);

require_once __DIR__ . '/../helpers.php';
require_once __DIR__ . '/../auth/_auth.php';
require_once __DIR__ . '/../db.php';
require_once __DIR__ . '/../app/fields.php';
require_once __DIR__ . '/../security/_csrf.php';

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

// ---- DB（カテゴリ/フィールド取得の下準備） ----
db_migrate_categories(); // 存在していれば何もしない

$pdo = db();

// カテゴリ一覧（新しい順）
$cats = [];
try {
    $st = $pdo->query('SELECT id, name, slug FROM categories WHERE is_active = 1 ORDER BY created_at DESC, id DESC');
    $cats = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
} catch (Throwable $e) {
    $cats = [];
}

// 選択中カテゴリ（GET優先→POSTの順に取得）
$categorySlug = '';
if (isset($_GET['category'])) {
    $categorySlug = (string)$_GET['category'];
} elseif (isset($_POST['category'])) {
    $categorySlug = (string)$_POST['category'];
}
$categorySlug = preg_replace('~[^A-Za-z0-9_\-]+~', '', $categorySlug);

// モード（export / import）
$mode = '';
if (isset($_GET['mode']))      { $mode = (string)$_GET['mode']; }
elseif (isset($_POST['mode'])) { $mode = (string)$_POST['mode']; }
$mode = in_array($mode, ['export','import'], true) ? $mode : '';

// $cat を定義（セッション→GETの順で拾う。ここでセッションへは書き戻さない）
$cat = '';
if (isset($_SESSION['_csv_import_category']) && is_string($_SESSION['_csv_import_category'])) {
    $cat = (string)$_SESSION['_csv_import_category'];
} elseif ($categorySlug !== '') {
    $cat = $categorySlug;
}

// 選択カテゴリID
$categoryId = 0;
if ($categorySlug !== '') {
    $stCid = $pdo->prepare('SELECT id FROM categories WHERE slug = ? LIMIT 1');
    $stCid->execute([$cat]);
    $categoryId = (int)$stCid->fetchColumn();
}

$cat = (string)($_SESSION['_csv_import_category'] ?? '');

// カスタムフィールド一覧（選択カテゴリのみ）
$fieldRows = [];
$fieldMap  = []; // key => ['label'=>..., 'type'=>...]
if ($categoryId > 0) {
    $fieldRows = fields_fetch_all($categoryId); // ord ASC, id ASC
    foreach ($fieldRows as $r) {
        $k = (string)($r['key'] ?? '');
        if ($k !== '') {
            $fieldMap[$k] = [
                'label' => (string)($r['label'] ?? $k),
                'type'  => (string)($r['type']  ?? 'text'),
            ];
        }
    }
}

// 基本列（エクスポートUI用／インポート側の候補にも利用）
$baseColumns = [
    ['val' => '__id',         'label' => 'ID',       'default' => true],
    ['val' => '__title',      'label' => 'タイトル', 'default' => true],
    ['val' => '__created_at', 'label' => '作成日',   'default' => true],
];

// -------------------------------------
//  エクスポート実行（POST）
// -------------------------------------
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['_action'] ?? '') === 'export') {
    csrf_check_or_die();

    if ($categoryId <= 0) {
        header('Location: ./csv.php?err=' . rawurlencode('カテゴリを選択してください。'));
        exit;
    }

    // 選択列（なければ基本列デフォルトにフォールバック）
    $cols = (array)($_POST['cols'] ?? []);
    $cols = array_values(array_filter(array_map('strval', $cols), 'strlen'));

    if (!$cols) {
        foreach ($baseColumns as $b) {
            if (!empty($b['default'])) $cols[] = (string)$b['val'];
        }
    }

    // 列の検証と見出し（label）準備
    $header = [];
    $fieldsForQuery = []; // 選択された field:<key> の key 一覧
    foreach ($cols as $c) {
        if ($c === '__id') {
            $header[] = 'ID';
        } elseif ($c === '__title') {
            $header[] = 'タイトル';
        } elseif ($c === '__created_at') {
            $header[] = '作成日';
        } elseif (strpos($c, 'field:') === 0) {
            $key = substr($c, 6);
            if (preg_match('/^[A-Za-z0-9_-]+$/', $key) && isset($fieldMap[$key])) {
                $header[] = (string)($fieldMap[$key]['label'] ?? $key);
                $fieldsForQuery[] = $key;
            }
        }
    }

    if (!$header) {
        header('Location: ./csv.php?category=' . rawurlencode($categorySlug) . '&err=' . rawurlencode('出力する列がありません。'));
        exit;
    }

    // CSVヘッダ出力
    $fname = 'tpcms_' . ($categorySlug !== '' ? $categorySlug : 'all') . '_' . date('Ymd_His') . '.csv';
    header('Content-Type: text/csv; charset=UTF-8');
    header('Content-Disposition: attachment; filename="' . $fname . '"');
    header('Pragma: public');
    header('Cache-Control: max-age=0');

    // UTF-8 BOM
    echo "\xEF\xBB\xBF";

    $out = fopen('php://output', 'w');
    if (!is_resource($out)) { exit; }

    // 1行目：見出し
    fputcsv($out, $header);

    // アイテム本体（カテゴリ絞り込み、ID昇順）
    $stmtItems = $pdo->prepare('SELECT id, title, created_at FROM items WHERE category = ? ORDER BY id ASC');
    $stmtItems->execute([$categorySlug]);

    // item_values 取得（行ごとに）
    $stmtVals = $pdo->prepare('SELECT field_key, value FROM item_values WHERE item_id = ?');

    while ($row = $stmtItems->fetch(PDO::FETCH_ASSOC)) {
        $line = [];
        $vals = [];
        if (!empty($fieldsForQuery)) {
            $stmtVals->execute([(int)$row['id']]);
            foreach ($stmtVals->fetchAll(PDO::FETCH_ASSOC) ?: [] as $v) {
                $fk = (string)($v['field_key'] ?? '');
                $vv = (string)($v['value'] ?? '');
                if ($fk !== '') $vals[$fk] = $vv;
            }
        }

        foreach ($cols as $c) {
            if ($c === '__id') {
                $line[] = (string)$row['id'];
            } elseif ($c === '__title') {
                $line[] = (string)$row['title'];
            } elseif ($c === '__created_at') {
                $line[] = (string)$row['created_at'];
            } elseif (strpos($c, 'field:') === 0) {
                $key = substr($c, 6);
                $line[] = (string)($vals[$key] ?? '');
            }
        }

        fputcsv($out, $line);
        if (function_exists('ob_get_level')) {
            if (ob_get_level() > 0) { @ob_flush(); }
        }
        flush();
    }

    fclose($out);
    exit;
}

// -------------------------------------
//  インポート：プレビュー（POST）
// -------------------------------------
$importError   = '';
$previewHeader = [];
$previewRows   = []; // 先頭数行のサンプル

if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['_action'] ?? '') === 'import_preview') {
    csrf_check_or_die();

    if (!isset($_FILES['csvfile']) || !is_array($_FILES['csvfile']) || (int)$_FILES['csvfile']['error'] === UPLOAD_ERR_NO_FILE) {
        $importError = 'CSVファイルを選択してください。';
    } elseif ((int)$_FILES['csvfile']['error'] !== UPLOAD_ERR_OK || !is_uploaded_file($_FILES['csvfile']['tmp_name'])) {
        $importError = 'CSVのアップロードに失敗しました。';
    } else {
        $tmp = $_FILES['csvfile']['tmp_name'];

        // 先頭〜数百KBだけ読み込み（文字コード判定＆変換用）
        $raw = (string)file_get_contents($tmp);
        if ($raw === '') {
            $importError = 'CSVの内容が空でした。';
        } else {
            // BOM除去（UTF-8）
            if (strncmp($raw, "\xEF\xBB\xBF", 3) === 0) {
                $raw = substr($raw, 3);
            }
            // 文字コード：UTF-8でなければSJIS-win/CP932を優先的にUTF-8へ
            if (!mb_check_encoding($raw, 'UTF-8')) {
                $raw = mb_convert_encoding($raw, 'UTF-8', 'SJIS-win,CP932,UTF-8');
            }

            // 解析用にメモリ上の一時ストリームへ
            $fp = fopen('php://temp', 'r+');
            fwrite($fp, $raw);
            rewind($fp);

            // 1行目（見出し）
            $hdr = fgetcsv($fp);
            if ($hdr === false) {
                $importError = 'CSVの見出し行を読み取れませんでした。';
            } else {
                $previewHeader = array_map(static fn($v) => (string)$v, $hdr);

                // サンプル行（最大5行）
                $max = 5;
                while ($max-- > 0 && ($row = fgetcsv($fp)) !== false) {
                    $previewRows[] = array_map(static fn($v) => (string)$v, $row);
                }
            }
            fclose($fp);
            $_SESSION['_csv_import_raw'] = $raw;                  // 正規化済みCSV（UTF-8, BOM無し）
            $_SESSION['_csv_import_category'] = $categorySlug;    // 選択カテゴリの記録

        }
    }
}

// -------------------------------------
//  インポート：列マッピング保存（確認表示のみ・DB未反映）
// -------------------------------------
$mapSaved  = false;
$mapError  = '';
$mapHeader = []; // POST受けの header[]
$mapPairs  = []; // [['header' => ..., 'target' => ..., 'target_label' => ...], ...]

if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['_action'] ?? '') === 'map_save') {
    csrf_check_or_die();

    // 入力値（見出しと割当）
    $mapHeader = array_values(array_map('strval', (array)($_POST['header'] ?? [])));
    $mapRaw    = (array)($_POST['map'] ?? []);

    // 許容ターゲット一覧
    $allowed = ['__id' => 'ID', '__title' => 'タイトル', '__created_at' => '作成日'];
    foreach ($fieldMap as $k => $meta) {
        $allowed['field:' . $k] = (string)($meta['label'] ?? $k);
    }

    // 重複割当のチェック（空は除外）
    $used = [];
    foreach ($mapRaw as $i => $t) {
        $t = (string)$t;
        if ($t === '') continue;
        if (!isset($allowed[$t])) {
            $mapError = '不正な割り当て値が含まれています。';
            break;
        }
        if (isset($used[$t])) {
            $mapError = '同じ項目に複数の列を割り当てることはできません：' . $allowed[$t];
            break;
        }
        $used[$t] = true;
    }

    if ($mapError === '') {
        // 表示用ペアを生成
        foreach ($mapHeader as $i => $hname) {
            $tgt = (string)($mapRaw[$i] ?? '');
            if ($tgt === '') continue;
            $mapPairs[] = [
                'header'       => $hname,
                'target'       => $tgt,
                'target_label' => (string)($allowed[$tgt] ?? $tgt),
            ];
        }
        $mapSaved = true; // 確認表示フラグ（DB未反映）
        $_SESSION['_csv_import_header'] = $mapHeader;  // CSV 1行目の見出し配列
        $_SESSION['_csv_import_map']    = $mapRaw;     // 見出し→割当先の対応（index基準）
    }
}

// 自動割当の補助（見出し→既知ラベル一致）
$autoMap = function(string $name) use ($fieldMap): string {
    $n = trim($name);
    if ($n === 'ID') return '__id';
    if ($n === 'タイトル') return '__title';
    if ($n === '作成日') return '__created_at';
    // フィールドの label と完全一致なら割当
    foreach ($fieldMap as $key => $meta) {
        $lab = (string)($meta['label'] ?? $key);
        if ($lab !== '' && $lab === $n) {
            return 'field:' . $key;
        }
    }
    return '';
};

$drySummary = null;    // ['total'=>, 'ok'=>, 'ng'=>]
$dryErrors  = [];      // [['row'=>行番号, 'errors'=>[['col'=>列名,'msg'=>メッセージ],...]], ...]
$dryRan     = false;

if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['_action'] ?? '') === 'dry_run') {
    csrf_check_or_die();
    $dryRan = true;

    $raw = (string)($_SESSION['_csv_import_raw'] ?? '');
    $hdr = isset($_POST['header']) ? array_values(array_map('strval', (array)$_POST['header'])) : (array)($_SESSION['_csv_import_header'] ?? []);
    $map = isset($_POST['map'])    ? (array)($_POST['map'])                                   : (array)($_SESSION['_csv_import_map'] ?? []);
    $cat = (string)($_SESSION['_csv_import_category'] ?? '');

$__hasFieldTargets = false; foreach ($map as $__t) { if (is_string($__t) && $__t !== '' && substr($__t,0,6)==='field:') { $__hasFieldTargets = true; break; } }
if ($raw === '' || !$hdr || !$map || ($__hasFieldTargets && ($cat === '' || $cat !== $categorySlug))) {
    $importError = 'プレビューまたは列マッピングが未完了です。先にCSVをプレビュー→列マッピングを保存してください。';
} else {
        // カテゴリのフィールド定義（required / options / type）
        $fieldMeta = [];
        foreach ($fieldRows as $f) {
            $k = (string)$f['key'];
            if ($k === '') continue;
            $fieldMeta['field:'.$k] = [
                'label'    => (string)($f['label'] ?? $k),
                'type'     => (string)($f['type'] ?? 'text'),
                'required' => (int)($f['required'] ?? 0) === 1,
                'options'  => $f['options'] ? json_decode((string)$f['options'], true) : null,
            ];
        }

        // 解析
        $fp = fopen('php://temp', 'r+'); fwrite($fp, $raw); rewind($fp);
        fgetcsv($fp); // ヘッダ捨て
        $rowNo = 1; $ok = 0; $ng = 0; $maxErr = 50;

        while (($cols = fgetcsv($fp)) !== false) {
            $rowNo++;
            $rowErrs = [];

            // CSV行 → 割当先キー => 値 に並べ替え
            $values = [];
            foreach ($hdr as $i => $name) {
                $tgt = (string)($map[$i] ?? '');
                if ($tgt === '') continue;
                $values[$tgt] = (string)($cols[$i] ?? '');
            }

            // 基本列チェック（regist.phpの仕様に合わせる）
            // タイトル：必須・200文字以内
            $title = trim((string)($values['__title'] ?? ''));
            if ($title === '') {
                $rowErrs[] = ['col'=>'タイトル','msg'=>'必須です'];
            } elseif (function_exists('mb_strlen') && mb_strlen($title, 'UTF-8') > 200) {
                $rowErrs[] = ['col'=>'タイトル','msg'=>'200文字以内にしてください'];
            }

            // 作成日：任意。あるならstrtotime通過
            $created = (string)($values['__created_at'] ?? '');
            if ($created !== '' && strtotime($created) === false) {
                $rowErrs[] = ['col'=>'作成日','msg'=>'日時の形式が不正です'];
            }

            // ID：任意。あるなら整数
            $idv = (string)($values['__id'] ?? '');
            if ($idv !== '' && !ctype_digit($idv)) {
                $rowErrs[] = ['col'=>'ID','msg'=>'整数で指定してください'];
            }

            // カスタムフィールド
            foreach ($fieldMeta as $tgt => $meta) {
                $val = (string)($values[$tgt] ?? '');
                $lab = (string)($meta['label'] ?? $tgt);
                $type = (string)($meta['type'] ?? 'text');
                $req = !empty($meta['required']);

                if ($req && $val === '') {
                    $rowErrs[] = ['col'=>$lab,'msg'=>'必須です'];
                    continue;
                }
                if ($val === '') continue;

                if (in_array($type, ['select','radio','checkbox'], true)) {
                    $opts = [];
                    if (is_array($meta['options'])) {
                        foreach ($meta['options'] as $o) { $opts[(string)($o['value'] ?? '')] = true; }
                    }
                    if ($type === 'checkbox') {
                        foreach (array_filter(array_map('trim', explode(',', $val)), 'strlen') as $one) {
                            if ($one !== '' && !isset($opts[$one])) {
                                $rowErrs[] = ['col'=>$lab,'msg'=>'未定義の選択肢：'.$one];
                            }
                        }
                    } else {
                        if (!isset($opts[$val])) {
                            $rowErrs[] = ['col'=>$lab,'msg'=>'未定義の選択肢です'];
                        }
                    }
                } elseif (in_array($type, ['number','currency'], true)) {
                    if (!is_numeric($val)) {
                        $rowErrs[] = ['col'=>$lab,'msg'=>'数値を指定してください'];
                    }
                } elseif ($type === 'date') {
                    if (strtotime($val) === false) {
                        $rowErrs[] = ['col'=>$lab,'msg'=>'日付の形式が不正です'];
                    }
                }
                // assets / map_iframe は形式チェックなし（次段で拡張可）
            }

            if ($rowErrs) { $ng++; if (count($dryErrors) < $maxErr) { $dryErrors[] = ['row'=>$rowNo,'errors'=>$rowErrs]; } }
            else { $ok++; }
        }
        fclose($fp);

        $drySummary = ['total' => $ok + $ng, 'ok' => $ok, 'ng' => $ng];
        $_SESSION['_csv_import_dry_ready'] = (($drySummary['ng'] ?? 0) === 0);
    }
}

// -------------------------------------
//  インポート：適用（DB反映：INSERT/UPDATE）
// -------------------------------------
$applyOk  = false;
$applyMsg = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['_action'] ?? '') === 'import_apply') {
    csrf_check_or_die();

    // 前提データ（セッション＋POST再送）
    $raw = (string)($_SESSION['_csv_import_raw'] ?? '');
    $hdr = isset($_POST['header']) ? array_values(array_map('strval', (array)$_POST['header'])) : (array)($_SESSION['_csv_import_header'] ?? []);
    $map = isset($_POST['map'])    ? (array)($_POST['map'])                                   : (array)($_SESSION['_csv_import_map'] ?? []);
    $cat = (string)($_SESSION['_csv_import_category'] ?? '');
    if ($cat === '' && $categorySlug !== '') { $cat = $categorySlug; $_SESSION['_csv_import_category'] = $cat; }
    $ready = (bool)($_SESSION['_csv_import_dry_ready'] ?? false);

    if ($raw === '' || !$hdr || !$map) {
        $importError = '前提データが不足しています。CSVプレビューからやり直してください。';
    } elseif (!$ready && (string)($_POST['force'] ?? '0') !== '1') {
        $importError = 'ドライランでエラーが解消されていません。先にエラーを0件にしてください。';
    } else {
        // カテゴリIDの再確認
        $stCid = $pdo->prepare('SELECT id FROM categories WHERE slug = ? LIMIT 1');
        $stCid->execute([$categorySlug]);
        $cidNow = (int)$stCid->fetchColumn();
        if ($cidNow <= 0) {
            $importError = 'カテゴリが見つかりません。もう一度カテゴリを選択してください。';
        } else {
            // フィールド定義（item_values 保存に使用）
            $fieldMeta = [];
            foreach ($fieldRows as $f) {
                $k = (string)$f['key'];
                if ($k === '') continue;
                $fieldMeta['field:'.$k] = [
                    'label'    => (string)($f['label'] ?? $k),
                    'type'     => (string)($f['type'] ?? 'text'),
                    'required' => (int)($f['required'] ?? 0) === 1,
                    'options'  => $f['options'] ? json_decode((string)$f['options'], true) : null,
                ];
            }

            // パース開始
            $fp = fopen('php://temp', 'r+'); fwrite($fp, $raw); rewind($fp);
            fgetcsv($fp); // ヘッダを捨てる

            // 準備：SQL
            $pdo->beginTransaction();
            try {
                $stFind   = $pdo->prepare('SELECT id, category, created_at FROM items WHERE id = ? AND category = ? LIMIT 1');
                $stInsert = $pdo->prepare('INSERT INTO items (category, title, body, asset_file, active, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)');
                $stUpdate = $pdo->prepare('UPDATE items SET title = ?, updated_at = ? WHERE id = ? AND category = ?');

                // item_values（アップサート）
                $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)');
                $stIvUp   = $pdo->prepare('INSERT OR REPLACE INTO item_values (item_id, field_key, value) VALUES (?, ?, ?)');

                $nowIso = date('c');
                $ins = 0; $upd = 0;

                $activeAtInsert = ((string)($_POST['force'] ?? '0') === '1') ? 0 : 1; // force=1なら下書き（非公開）

                while (($cols = fgetcsv($fp)) !== false) {
                    // 行を map に沿って key=>value へ
                    $values = [];
                    foreach ($hdr as $i => $name) {
                        $tgt = (string)($map[$i] ?? '');
                        if ($tgt === '') continue;
                        $values[$tgt] = (string)($cols[$i] ?? '');
                    }

                    // 固定列
                    $idCsv   = (string)($values['__id'] ?? '');          // 任意
                    $title   = trim((string)($values['__title'] ?? ''));  // 必須（ドライランで検証済みの想定）
                    $created = (string)($values['__created_at'] ?? '');   // 任意
                    $createdOk = ($created !== '' && strtotime($created) !== false) ? $created : null;

                    // UPDATE or INSERT の判断（id が整数かつ該当レコードがあれば UPDATE）
                    $itemId = null;
                    if ($idCsv !== '' && ctype_digit($idCsv)) {
                        $stFind->execute([(int)$idCsv, $cat]);
                        if ($stFind->fetch(PDO::FETCH_ASSOC)) {
                            $itemId = (int)$idCsv;
                            $stUpdate->execute([$title, $nowIso, $itemId, $cat]);
                            $upd++;
                        }
                    }
                    if ($itemId === null) {
                        $stInsert->execute([$cat, $title, '', '', $activeAtInsert, ($createdOk ?? $nowIso), $nowIso]);
                        $itemId = (int)$pdo->lastInsertId();
                        $ins++;
                    }

                    // カスタムフィールドを保存（割当があるキーのみ）
                    foreach ($values as $tgt => $val) {
                        if (strpos($tgt, 'field:') !== 0) continue;
                        $key = substr($tgt, 6);
                        if ($key === '' || !isset($fieldMeta['field:'.$key])) continue;

                        // 値はドライランで検証済み前提。トリム＋上限だけ再適用
                        $v = trim((string)$val);
                        if (function_exists('mb_substr')) { $v = mb_substr($v, 0, 2000, 'UTF-8'); }
                        else { $v = (strlen($v) > 2000) ? substr($v, 0, 2000) : $v; }

                        $stIvUp->execute([$itemId, $key, $v]);
                    }
                }

                $pdo->commit();
                $applyOk  = true;
                $applyMsg = 'インポート適用が完了しました（新規: ' . $ins . ' 件 / 更新: ' . $upd . ' 件）。';
            } catch (Throwable $e) {
                $pdo->rollBack();
                $importError = 'インポートの適用に失敗しました。もう一度お試しください。';
            }
            fclose($fp);
        }
    }
}

// -------------------------------------
//  ここから画面表示（UI）
// -------------------------------------
?><!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>CSV エクスポート／インポート（プレビュー＆列マッピング）｜Template Party CMS</title>
  <link rel="stylesheet" href="<?= h(asset_url('/admin/assets/admin.css', true)) ?>">
</head>
<body class="csv">
<?php include __DIR__ . '/_header.php'; ?>

<div id="container">
  <h1>CSV エクスポート／インポート</h1>

  <form method="get" action="./csv.php" class="mb1rem">
    <input type="hidden" name="mode" value="<?= h($mode) ?>">
    <input type="hidden" name="category" value="<?= h($categorySlug) ?>">
    <label class="inline-block" style="margin-right:1rem;">
      <input type="radio" name="mode" value="export" <?= ($mode==='export'?'checked':'') ?> onchange="this.form.submit()"> エクスポート
    </label>
    <label class="inline-block">
      <input type="radio" name="mode" value="import" <?= ($mode==='import'?'checked':'') ?> onchange="this.form.submit()"> インポート
    </label>
  </form>
  <?php if ($mode===''): ?>
    <p class="small muted">※ まず「エクスポート」または「インポート」を選択してください。</p>
  <?php endif; ?>

  <?php if (isset($_GET['err']) && (string)$_GET['err'] !== ''): ?>
    <p class="err"><?= h((string)$_GET['err']) ?></p>
  <?php endif; ?>

  <!-- カテゴリ選択 -->
  <form method="get" action="./csv.php" class="mb1rem">
    <input type="hidden" name="mode" value="<?= h($mode) ?>">
    <label for="js-cat">カテゴリを選択：</label>
    <select id="js-cat" class="input" name="category" onchange="this.form.submit()">
      <option value="">（選択してください）</option>
      <?php foreach ($cats as $c): ?>
        <?php $slug = (string)($c['slug'] ?? ''); $name = (string)($c['name'] ?? $slug); ?>
        <option value="<?= h($slug) ?>" <?= ($slug === $categorySlug ? 'selected' : '') ?>>
          <?= h($name) ?>（<?= h($slug) ?>）
        </option>
      <?php endforeach; ?>
    </select>
    <?php if (!$cats): ?>
      <p class="small">アクティブなカテゴリがありません。<a href="./categories.php">カテゴリ管理</a>から作成してください。</p>
    <?php endif; ?>
  </form>

<?php if ($mode === 'export'): ?>

  <!-- エクスポート：列選択 -->
  <?php if ($categoryId > 0): ?>
    <form method="post" action="./csv.php" class="mb1rem" id="js-cols-form">
      <?= csrf_input_tag() ?>
      <input type="hidden" name="_action"  value="export">
      <input type="hidden" name="category" value="<?= h($categorySlug) ?>">

      <h2>エクスポート：出力する列</h2>

      <fieldset class="row" style="border:1px solid #ccc;padding:1rem;border-radius:8px;">
        <legend class="small">基本列</legend>
        <?php foreach ($baseColumns as $b): ?>
          <label class="inline-block" style="margin-right:1rem;">
            <input type="checkbox" name="cols[]" value="<?= h($b['val']) ?>" <?= (!empty($b['default']) ? 'checked' : '') ?>>
            <?= h($b['label']) ?>
          </label>
        <?php endforeach; ?>
      </fieldset>

      <?php if ($fieldRows): ?>
        <fieldset class="row" style="border:1px solid #ccc;padding:1rem;border-radius:8px;">
          <legend class="small">カスタムフィールド</legend>
          <label style="display:block;margin:0 0 0.6rem 0;">
            <input type="checkbox" id="js-check-all-fields"> 全てチェックする
          </label>
          <?php foreach ($fieldRows as $f): ?>
            <?php
              $key   = (string)($f['key']   ?? '');
              $label = (string)($f['label'] ?? $key);
              $type  = (string)($f['type']  ?? 'text');
              if ($key === '') continue;
              $val = 'field:' . $key;
            ?>
            <label class="inline-block" style="margin:0 1rem 0.6rem 0;">
              <input type="checkbox" name="cols[]" value="<?= h($val) ?>">
              <?= h($label) ?><span class="small muted">（<?= h($type) ?>）</span>
            </label>
          <?php endforeach; ?>
        </fieldset>
      <?php else: ?>
        <p class="small">このカテゴリにはフィールドが未定義です。<a href="./fields.php?category_id=<?= (int)$categoryId ?>">項目設計</a>へ。</p>
      <?php endif; ?>

      <div class="mb1rem">
        <button type="submit" class="btn1">CSVをエクスポート</button>
      </div>

      <p class="small muted">
        形式：UTF-8（BOM付）/ カンマ区切り / ダブルクォート囲み、1行目は見出し（label）。<br>
        ※ チェックが入っている列のみ出力します。大量件数はストリーミング出力します。
      </p>
      <script>
      (function(){
        var form = document.getElementById('js-cols-form');
        if(!form) return;
        var master = document.getElementById('js-check-all-fields');
        if(!master) return;

        master.addEventListener('change', function(){
          var cbs = form.querySelectorAll('input[type="checkbox"][name="cols[]"]');
          cbs.forEach(function(cb){
            // 基本列は除外し、"field:..." のみ一括切り替え
            if (cb.value && cb.value.indexOf('field:') === 0) {
              cb.checked = master.checked;
            }
          });
        });
      })();
      </script>
    </form>
  <?php else: ?>
    <p>カテゴリを選択すると、出力する列の選択ができます。</p>
  <?php endif; ?>

<?php endif; ?>

  <hr class="mb1rem">

<?php if ($mode === 'import'): ?>

  <!-- インポート：プレビュー（ヘッダ＋数行） -->
  <?php if ($mode==='import' && empty($previewHeader) && empty($mapSaved) && empty($drySummary)): ?>
  <form method="post" action="./csv.php?category=<?= h($categorySlug) ?>" enctype="multipart/form-data" class="mb1rem">
    <?= csrf_input_tag() ?>
    <input type="hidden" name="mode" value="import">
    <input type="hidden" name="_action" value="import_preview">
    <h2>インポート：CSVプレビュー</h2>
    <div class="row">
      <label class="label" for="csvfile">CSVファイル</label>
      <input class="input" id="csvfile" type="file" name="csvfile" accept=".csv,text/csv">
    </div>
    <div>
      <button type="submit" class="btn1">プレビューを表示</button>
    </div>
    <p class="small muted">※ プレビューは見出し行と先頭数行のみを表示します。DBにはまだ反映しません。</p>
  </form>
  <?php endif; ?>

  <?php if ($importError !== ''): ?>
    <p class="err"><?= h($importError) ?></p>
  <?php endif; ?>

  <?php if ($previewHeader && empty($mapSaved) && empty($drySummary)): ?>
    <h3>見出し（1行目）</h3>
    <div class="table-wrap">
      <table class="ta1">
        <thead><tr>
          <?php foreach ($previewHeader as $hcell): ?>
            <th><?= h($hcell) ?></th>
          <?php endforeach; ?>
        </tr></thead>
        <?php if ($previewRows): ?>
          <tbody>
            <?php foreach ($previewRows as $prow): ?>
              <tr>
                <?php foreach ($prow as $cell): ?>
                  <td><?= h($cell) ?></td>
                <?php endforeach; ?>
              </tr>
            <?php endforeach; ?>
          </tbody>
        <?php endif; ?>
      </table>
    </div>

    <!-- 列マッピング（保存＝確認表示のみ、DB未反映） -->
    <form method="post" action="./csv.php?category=<?= h($categorySlug) ?>" class="mb1rem">
      <?= csrf_input_tag() ?>
      <input type="hidden" name="mode" value="import">
      <input type="hidden" name="_action" value="map_save">
      <h2>インポート：列マッピング</h2>
      <p class="small muted">※ ここでは割り当ての保存（確認表示）のみ行います。DB反映やドライランは次ステップで実装します。</p>

      <div class="table-wrap">
        <table class="ta1">
          <thead>
            <tr><th>CSVの列名</th><th>割り当て先</th></tr>
          </thead>
          <tbody>
            <?php foreach ($previewHeader as $i => $hname): ?>
              <?php
                $auto = $autoMap($hname);
              ?>
              <tr>
                <td>
                  <?= h($hname) ?>
                  <input type="hidden" name="header[<?= (int)$i ?>]" value="<?= h($hname) ?>">
                </td>
                <td>
                  <select class="input" name="map[<?= (int)$i ?>]">
                    <option value="">（無視）</option>
                    <option value="__id"         <?= ($auto==='__id'?'selected':'') ?>>ID</option>
                    <option value="__title"      <?= ($auto==='__title'?'selected':'') ?>>タイトル</option>
                    <option value="__created_at" <?= ($auto==='__created_at'?'selected':'') ?>>作成日</option>
                    <?php foreach ($fieldMap as $k => $meta): ?>
                      <?php $lab = (string)($meta['label'] ?? $k); $val = 'field:' . $k; ?>
                      <option value="<?= h($val) ?>" <?= ($auto===$val?'selected':'') ?>>
                        <?= h($lab) ?>（field:<?= h($k) ?>）
                      </option>
                    <?php endforeach; ?>
                  </select>
                </td>
              </tr>
            <?php endforeach; ?>
          </tbody>
        </table>
      </div>

      <div class="mb1rem">
        <button type="submit" class="btn1">割り当てを保存（確認表示）</button>
      </div>
    </form>
  <?php endif; ?>

  <?php if ($mapSaved && empty($drySummary) && isset($_SESSION['_csv_import_raw'])): ?>
    <form method="post" action="./csv.php?category=<?= h($categorySlug) ?>" class="mb1rem">
      <?= csrf_input_tag() ?>
      <input type="hidden" name="mode" value="import">
      <input type="hidden" name="_action" value="dry_run">

      <?php
      // 直近の列マッピングをセッションから再送（POSTでも完結）
      $__hdr = $_SESSION['_csv_import_header'] ?? [];
      $__map = $_SESSION['_csv_import_map'] ?? [];
      if (is_array($__hdr)) {
          foreach ($__hdr as $i => $name) {
              echo '<input type="hidden" name="header['.(int)$i.']" value="'.h((string)$name).'">';
              $tgt = (string)($__map[$i] ?? '');
              echo '<input type="hidden" name="map['.(int)$i.']" value="'.h($tgt).'">';
          }
      }
      ?>

      <button type="submit" class="btn1">ドライランを実行（検証のみ）</button>
    </form>
  <?php endif; ?>

  <?php if ($mapError !== ''): ?>
    <p class="err"><?= h($mapError) ?></p>
  <?php elseif ($mapSaved): ?>
    <p class="ok">割り当て内容を保存しました（確認用表示のみ。まだDBには反映しません）。</p>
    <?php if ($mapPairs): ?>
      <div class="table-wrap">
        <table class="ta1">
          <thead><tr><th>CSVの列名</th><th>割り当て先</th></tr></thead>
          <tbody>
            <?php foreach ($mapPairs as $p): ?>
              <tr>
                <td><?= h($p['header'] ?? '') ?></td>
                <td><?= h($p['target_label'] ?? '') ?></td>
              </tr>
            <?php endforeach; ?>
          </tbody>
        </table>
      </div>
    <?php else: ?>
      <p class="small">すべて「（無視）」として保存されました。</p>
    <?php endif; ?>
  <?php endif; ?>

  <?php if (!empty($drySummary)): ?>
    <h2>ドライラン結果</h2>
    <p>総行数：<?= (int)$drySummary['total'] ?> ／ OK：<?= (int)$drySummary['ok'] ?> ／ エラー：<?= (int)$drySummary['ng'] ?></p>
  <?php if (!empty($drySummary) && (int)$drySummary['ng'] === 0): ?>
    <p class="err">まだ適用は完了していません。内容に問題がなければ、下の「適用（DB反映）」ボタンを押してください。</p>
  <?php endif; ?>

  <?php if (!empty($drySummary) && (int)$drySummary['ng'] > 0): ?>
    <p class="err">まだ適用は完了していません。エラーを無視して適用していい場合は、以下の適用ボタンを押して下さい。</p>
  <?php endif; ?>

    <?php if ($dryErrors): ?>
      <div class="table-wrap">
        <table class="ta1">
          <thead><tr><th>行番号</th><th>内容</th></tr></thead>
          <tbody>
            <?php foreach ($dryErrors as $e): ?>
              <tr>
                <td><?= (int)$e['row'] ?></td>
                <td>
                  <ul>
                    <?php foreach ($e['errors'] as $er): ?>
                      <li><?= h((string)$er['col']) ?>：<?= h((string)$er['msg']) ?></li>
                    <?php endforeach; ?>
                  </ul>
                </td>
              </tr>
            <?php endforeach; ?>
          </tbody>
        </table>
        <p class="small muted">※ エラーは最大50件まで表示します。</p>
      </div>
    <?php else: ?>
      <p class="ok">エラーは見つかりませんでした。</p>
    <?php endif; ?>
  <?php endif; ?>

  <?php if (!empty($drySummary)): ?>
    <?php if ((int)$drySummary['ng'] === 0): ?>
      <!-- エラー0件：通常適用 -->
      <form method="post" action="./csv.php?category=<?= h($categorySlug) ?>" class="mb1rem">
        <?= csrf_input_tag() ?>
        <input type="hidden" name="mode" value="import">
        <input type="hidden" name="_action" value="import_apply">
        <?php
        // ドライラン時のマッピングをPOSTで再送（セッション不整合対策）
        $__hdr = $_SESSION['_csv_import_header'] ?? [];
        $__map = $_SESSION['_csv_import_map'] ?? [];
        if (is_array($__hdr)) {
            foreach ($__hdr as $i => $name) {
                echo '<input type="hidden" name="header['.(int)$i.']" value="'.h((string)$name).'">';
                $tgt = (string)($__map[$i] ?? '');
                echo '<input type="hidden" name="map['.(int)$i.']" value="'.h($tgt).'">';
            }
        }
        ?>
        <button type="submit" class="btn1">適用（DB反映）</button>
      </form>
    <?php else: ?>
      <!-- エラーあり：警告を無視して適用（force=1） -->
      <div class="mb1rem small muted">※エラーがありますが、必要ならそのまま取り込めます（不足項目は空のまま登録されます）。</div>
      <form method="post" action="./csv.php?category=<?= h($categorySlug) ?>" class="mb1rem" onsubmit="return confirm('警告を無視して取り込みます。よろしいですか？');">
        <?= csrf_input_tag() ?>
        <input type="hidden" name="mode" value="import">
        <input type="hidden" name="_action" value="import_apply">
        <input type="hidden" name="force" value="1">
        <?php
        $__hdr = $_SESSION['_csv_import_header'] ?? [];
        $__map = $_SESSION['_csv_import_map'] ?? [];
        if (is_array($__hdr)) {
            foreach ($__hdr as $i => $name) {
                echo '<input type="hidden" name="header['.(int)$i.']" value="'.h((string)$name).'">';
                $tgt = (string)($__map[$i] ?? '');
                echo '<input type="hidden" name="map['.(int)$i.']" value="'.h($tgt).'">';
            }
        }
        ?>
        <button type="submit" class="btn1">警告を無視して適用</button>
      </form>
    <?php endif; ?>
  <?php endif; ?>

  <?php if (!empty($applyOk) && $applyOk): ?>
    <p class="ok"><?= h($applyMsg) ?></p>
  <?php endif; ?>

<?php endif; ?>

</div>

<script src="<?= h(asset_url('/admin/assets/admin.js', true)) ?>"></script>
</body>
</html>
