<?php
/**
 * Board Room — data/ 専用 LISTER（Ver / 区分 / 概要 / メモ）
 */
require_once dirname(__DIR__) . '/lib/boardroom-auth.php';
require_once __DIR__ . '/fl-path.php';
require_once __DIR__ . '/fl-meta.php';

boardroom_session_start();

define('APP_NAME', 'file-lister');
define('APP_VERSION', '2.1-br');

function fl_h($value)
{
    return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function fl_boardroom_rel(string $relative): string
{
    $relative = boardroom_norm_rel($relative);
    return 'data' . ($relative !== '' ? '/' . $relative : '');
}

function fl_entry_visible(string $name, string $parentRel = '', string $listingDir = ''): bool
{
    if (boardroom_is_news_archive_files_entry($name, $parentRel, $listingDir)) {
        return false;
    }
    return !boardroom_is_browse_hidden($name, $parentRel, $listingDir);
}

$base = realpath(__DIR__);
if ($base === false) {
    header('Content-Type: text/plain; charset=UTF-8');
    echo 'Cannot resolve data path.';
    exit;
}

if (isset($_GET['raw'])) {
    $raw = urldecode((string) $_GET['raw']);
    $raw_path = fl_resolve_path($raw, $base);

    if (!$raw_path || !is_file($raw_path)) {
        http_response_code(404);
        exit;
    }

    $rel = fl_relative_for_client($raw_path, $base);
    if (!boardroom_can_access(fl_boardroom_rel($rel))) {
        http_response_code(403);
        exit;
    }

    $mime = mime_content_type($raw_path) ?: 'application/octet-stream';
    header('Content-Type: ' . $mime);
    readfile($raw_path);
    exit;
}

$relative = isset($_GET['path']) ? urldecode(trim((string) $_GET['path'], '/')) : '';
$sort  = $_GET['sort'] ?? 'date';
$order = $_GET['order'] ?? 'desc';

$path = fl_resolve_path($relative, $base);
if (!$path) {
    http_response_code(404);
    exit('Invalid path');
}

if (!boardroom_can_access(fl_boardroom_rel($relative))) {
    header('Content-Type: text/html; charset=utf-8');
    ?>
<!DOCTYPE html>
<html lang="ja"><head><meta charset="utf-8"><title>Password Required</title>
<style>
body{font-family:sans-serif;max-width:420px;margin:40px auto;padding:20px}
.box{border:1px solid #ccc;padding:24px;border-radius:8px;background:#fafafa}
a{color:#06c}
</style></head><body>
<div class="box">
<h2>🔒 Password Required</h2>
<p>この資料は理事会関係者のみ閲覧できます。</p>
<p><a href="../Board-Room.php">Board Room でログイン</a></p>
<p><a href="index.php?p=<?= fl_h(rawurlencode($relative)) ?>">通常の資料一覧へ</a></p>
</div></body></html>
    <?php
    exit;
}

if (is_file($path)) {
    $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));

    if (boardroom_is_browse_hidden(basename($path))) {
        http_response_code(404);
        exit('Not found');
    }

    if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'], true)) {
        header('Content-Type: image/' . ($ext === 'jpg' ? 'jpeg' : $ext));
        readfile($path);
        exit;
    }

    if (in_array($ext, ['txt', 'log', 'md', 'json', 'xml', 'html', 'htm', 'css', 'js'], true)) {
        header('Content-Type: text/plain; charset=utf-8');
        readfile($path);
        exit;
    }

    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . basename($path) . '"');
    readfile($path);
    exit;
}

$items = [];
foreach (scandir($path) ?: [] as $item) {
    if ($item === '.' || $item === '..') continue;
    if (!fl_entry_visible($item, $relative, $path)) continue;
    $items[] = $item;
}

function fl_get_icon($item, $full)
{
    if (is_dir($full)) {
        return '📁';
    }
    $ext = strtolower(pathinfo($item, PATHINFO_EXTENSION));
    return [
        'zip'  => '🗜',
        'pdf'  => '📚',
        'png'  => '🖼',
        'jpg'  => '🖼',
        'jpeg' => '🖼',
        'txt'  => '📝',
        'php'  => '💻',
        'html' => '🌐',
    ][$ext] ?? '📄';
}

function fl_sort_dirs(array &$dirs, $dir, $sort, $order)
{
    usort($dirs, function ($a, $b) use ($dir, $sort, $order) {
        $fa = $dir . '/' . $a;
        $fb = $dir . '/' . $b;
        switch ($sort) {
            case 'name':
                $v = strcasecmp($a, $b);
                break;
            case 'size':
                $v = 0;
                break;
            default:
                $v = (filemtime($fa) ?: 0) <=> (filemtime($fb) ?: 0);
        }
        if ($v === 0) {
            $v = strcasecmp($a, $b);
        }
        return $order === 'asc' ? $v : -$v;
    });
}

function fl_render_sort_bar($sort, $order, $relative)
{
    $pathQ = $relative !== '' ? 'path=' . rawurlencode($relative) . '&amp;' : '';
    $cols = [
        'date'        => '更新日',
        'ver'         => 'Ver',
        'category'    => '区分',
        'description' => '概要',
        'memo'        => 'メモ',
        'name'        => '名前',
        'size'        => 'サイズ',
    ];

    echo '<div class="sort-bar">';
    foreach ($cols as $key => $label) {
        $active = ($sort === $key);
        $next = ($active && $order === 'asc') ? 'desc' : 'asc';
        $arrow = $active ? ($order === 'asc' ? ' ▲' : ' ▼') : '';
        echo '<a class="sort-link' . ($active ? ' active' : '') . '" href="?' . $pathQ . 'sort=' . $key . '&order=' . $next . '">' . fl_h($label) . $arrow . '</a>';
    }
    echo '</div>';
}

function fl_render_meta_cell($rel2, $ext2, $field, $value, $placeholder)
{
    $editable = fl_is_meta_editable($ext2) ? 'true' : 'false';
    $show = $value !== '' ? fl_h($value) : '';
    echo '<td class="meta-cell" data-path="' . fl_h($rel2) . '" data-field="' . fl_h($field) . '" data-editable="' . $editable . '" data-placeholder="' . fl_h($placeholder) . '">' . $show . '</td>';
}

function fl_render_crumbs($relative)
{
    if ($relative === '') {
        echo '<strong>data</strong>';
        return;
    }
    echo '<a href="?">data</a>';
    $parts = explode('/', $relative);
    $acc = '';
    foreach ($parts as $p) {
        if ($p === '') continue;
        $acc .= ($acc === '' ? '' : '/') . $p;
        echo ' / <a href="?path=' . rawurlencode($acc) . '">' . fl_h($p) . '</a>';
    }
}

$dirs = [];
$files = [];
foreach ($items as $item) {
    $full = $path . DIRECTORY_SEPARATOR . $item;
    if (is_dir($full)) {
        $dirs[] = $item;
    } else {
        $files[] = $item;
    }
}

$dir_sort = in_array($sort, ['ver', 'category', 'description', 'memo'], true) ? 'name' : $sort;
fl_sort_dirs($dirs, $path, $dir_sort, $order);
fl_sort_file_list($files, $path, $sort, $order);

header('Content-Type: text/html; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache">
<title>File Lister — <?= fl_h($relative ?: 'data') ?></title>
<style>
  body { background:#111; color:#eee; font-family:sans-serif; margin:24px; line-height:1.6; }
  .nav { margin-bottom:16px; font-size:14px; }
  .nav a { color:#4ea3ff; text-decoration:none; margin-right:12px; }
  table { width:100%; border-collapse:collapse; margin-top:12px; }
  th, td { border:1px solid #333; padding:8px 10px; font-size:14px; }
  th { background:#222; }
  a { color:#4ea3ff; text-decoration:none; }
  a:hover { text-decoration:underline; }
  .icon { font-size:20px; text-align:center; }
  .meta-cell { min-width:88px; cursor:text; vertical-align:middle; }
  .meta-cell[data-editable="false"] { cursor:default; }
  .meta-cell:empty::before { content:attr(data-placeholder); color:#666; }
  .sort-bar { margin:12px 0; display:flex; flex-wrap:wrap; gap:8px; font-size:12px; }
  .sort-link { color:#888; padding:4px 8px; border:1px solid #333; border-radius:4px; }
  .sort-link.active { color:#4ea3ff; border-color:#4ea3ff; }
  h1 { font-size:20px; color:#4ea3ff; margin:0 0 8px; }
</style>
</head>
<body>

<p class="nav">
  <a href="../Board-Room.php">Board Room</a>
  <a href="index.php<?= $relative !== '' ? '?p=' . rawurlencode($relative) : '' ?>">通常一覧</a>
</p>

<h1>File Lister</h1>
<p><?php fl_render_crumbs($relative); ?></p>

<?php fl_render_sort_bar($sort, $order, $relative); ?>

<table>
<tr>
  <th>種別</th>
  <th>名前</th>
  <th>Ver</th>
  <th>区分</th>
  <th>概要</th>
  <th>メモ</th>
  <th>サイズ</th>
  <th>更新</th>
</tr>
<?php foreach (array_merge($dirs, $files) as $item):
    $full = $path . DIRECTORY_SEPARATOR . $item;
    $icon = fl_get_icon($item, $full);
    $rel2 = fl_relative_for_client($full, $base);
    $childRel = $relative === '' ? $item : $relative . '/' . $item;
?>
<?php if (is_dir($full)): ?>
<tr>
  <td class="icon"><?= $icon ?></td>
  <td><a href="?path=<?= rawurlencode($childRel) ?>"><?= fl_h($item) ?>/</a></td>
  <td></td><td></td><td></td><td></td>
  <td>-</td>
  <td><?= date('Y-m-d H:i', filemtime($full) ?: 0) ?></td>
</tr>
<?php else:
    $ext2 = strtolower(pathinfo($full, PATHINFO_EXTENSION));
    $meta2 = fl_read_meta($full);
    $mtime2 = filemtime($full) ?: 0;
    $ver2 = fl_display_version($meta2, $item, $mtime2);
    $ver_store = $meta2['APP_VERSION'] !== '' ? $meta2['APP_VERSION'] : $ver2;
?>
<tr>
  <td class="icon"><?= $icon ?></td>
  <td><a href="?path=<?= rawurlencode($rel2) ?>"><?= fl_h($item) ?></a></td>
  <?php
    fl_render_meta_cell($rel2, $ext2, 'APP_VERSION', $ver_store, '(Ver)');
    fl_render_meta_cell($rel2, $ext2, 'APP_CATEGORY', $meta2['APP_CATEGORY'], '(区分)');
    fl_render_meta_cell($rel2, $ext2, 'APP_DESCRIPTION', $meta2['APP_DESCRIPTION'], '(概要)');
    fl_render_meta_cell($rel2, $ext2, 'APP_MEMO', $meta2['APP_MEMO'], '(メモ)');
  ?>
  <td><?= number_format(filesize($full)) ?> bytes</td>
  <td><?= date('Y-m-d H:i', $mtime2) ?></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</table>

<script>
const SAVE_META_URL = new URL('save-description.php', window.location.href).href;

document.addEventListener('click', function(e) {
    const cell = e.target.closest('.meta-cell');
    if (!cell || cell.dataset.editable !== 'true') return;
    if (cell.querySelector('input')) return;

    const oldValue = cell.innerText.trim();
    const input = document.createElement('input');
    input.type = 'text';
    input.value = oldValue;
    input.style.width = '100%';
    input.style.boxSizing = 'border-box';

    cell.textContent = '';
    cell.appendChild(input);
    input.focus();
    input.select();

    let finished = false;

    function finish(commit) {
        if (finished) return;
        finished = true;
        if (!commit) {
            cell.textContent = oldValue;
            return;
        }
        const value = input.value.trim();
        saveMeta(cell.dataset.path, cell.dataset.field, value, function() {
            cell.textContent = value;
        }, function() {
            cell.textContent = oldValue;
        });
    }

    input.addEventListener('keydown', function(ev) {
        if (ev.key === 'Enter') { ev.preventDefault(); finish(true); }
        if (ev.key === 'Escape') { ev.preventDefault(); finish(false); }
    });
    input.addEventListener('blur', function() { finish(true); });
});

function saveMeta(path, field, value, onOk, onNg) {
    const form = new FormData();
    form.append('path', path);
    form.append('field', field);
    form.append('value', value);

    fetch(SAVE_META_URL, { method: 'POST', body: form, credentials: 'same-origin' })
    .then(function(r) { return r.text(); })
    .then(function(txt) {
        if (txt.trim() === 'OK') { if (onOk) onOk(); }
        else { alert('SAVE ERROR:\n' + txt); if (onNg) onNg(); }
    })
    .catch(function() { alert('NETWORK ERROR'); if (onNg) onNg(); });
}
</script>
</body>
</html>
