<?php
/**
 * File Lister — standalone English UI (selector session → metadata table)
 */
if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

require_once __DIR__ . '/fl-config.php';
require_once __DIR__ . '/fl-path.php';
require_once __DIR__ . '/fl-meta.php';

define('APP_NAME', 'file-lister');
define('APP_VERSION', '2.1');
define('APP_DESCRIPTION', 'Metadata editor for Selector-Lister');

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

$embed = isset($_GET['embed']) && $_GET['embed'] === '1';

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

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 . DIRECTORY_SEPARATOR . $a;
        $fb = $dir . DIRECTORY_SEPARATOR . $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_sort_by_tree_order(array $paths): array
{
    usort($paths, function ($a, $b) {
        $pa = explode('/', $a);
        $pb = explode('/', $b);
        $len = min(count($pa), count($pb));
        for ($i = 0; $i < $len; $i++) {
            if ($pa[$i] === $pb[$i]) {
                continue;
            }
            return strcasecmp($pa[$i], $pb[$i]);
        }
        return count($pa) <=> count($pb);
    });
    return $paths;
}

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_sort_bar($sort, $order, $relative, $embed)
{
    $pathQ = $relative !== '' ? 'path=' . rawurlencode($relative) . '&amp;' : '';
    if ($embed) {
        $pathQ = 'embed=1&amp;' . $pathQ;
    }
    $cols = [
        'date'        => 'Updated',
        'ver'         => 'Ver',
        'category'    => 'Category',
        'description' => 'Summary',
        'memo'        => 'Memo',
        'name'        => 'Name',
        'size'        => '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_listing_table($abs, $prefixRel, $base, $sort, $order, $embed)
{
    $is_single_file = is_file($abs);

    if ($is_single_file) {
        $items = [basename($abs)];
        $listingDir = dirname($abs);
    } else {
        $items = [];
        foreach (scandir($abs) ?: [] as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }
            $items[] = $item;
        }
        $listingDir = $abs;
    }

    $dirs = [];
    $files = [];
    foreach ($items as $item) {
        $full = $listingDir . 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, $listingDir, $dir_sort, $order);
    fl_sort_file_list($files, $listingDir, $sort, $order);

    $pathQ = $embed ? 'embed=1&amp;' : '';

    echo '<table class="listing-table"><tr>';
    echo '<th>Type</th><th>Name</th><th>Ver</th><th>Category</th><th>Summary</th><th>Memo</th><th>Size</th><th>Updated</th>';
    echo '</tr>';

    foreach (array_merge($dirs, $files) as $item) {
        $full = $listingDir . DIRECTORY_SEPARATOR . $item;
        $icon = fl_get_icon($item, $full);
        $rel2 = $prefixRel === '' ? $item : $prefixRel . '/' . $item;
        $childRel = $rel2;

        if (is_dir($full)) {
            echo '<tr><td class="icon">' . $icon . '</td>';
            echo '<td><a href="?' . $pathQ . 'path=' . rawurlencode($childRel) . '">' . fl_h($item) . '/</a></td>';
            echo '<td></td><td></td><td></td><td></td><td>-</td>';
            echo '<td>' . date('Y-m-d H:i', filemtime($full) ?: 0) . '</td></tr>';
            continue;
        }

        $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;
        $clientRel = fl_relative_for_client($full, $base);

        echo '<tr><td class="icon">' . $icon . '</td>';
        echo '<td><a href="?' . $pathQ . 'path=' . rawurlencode($clientRel) . '">' . fl_h($item) . '</a></td>';
        fl_render_meta_cell($clientRel, $ext2, 'APP_VERSION', $ver_store, '(Ver)');
        fl_render_meta_cell($clientRel, $ext2, 'APP_CATEGORY', $meta2['APP_CATEGORY'], '(Category)');
        fl_render_meta_cell($clientRel, $ext2, 'APP_DESCRIPTION', $meta2['APP_DESCRIPTION'], '(Summary)');
        fl_render_meta_cell($clientRel, $ext2, 'APP_MEMO', $meta2['APP_MEMO'], '(Memo)');
        echo '<td>' . number_format(filesize($full)) . ' bytes</td>';
        echo '<td>' . date('Y-m-d H:i', $mtime2) . '</td></tr>';
    }

    echo '</table>';
}

// selector-lite POST (checkbox sync)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $relative = isset($_POST['path']) ? urldecode(trim((string) $_POST['path'], '/')) : '';
    $select = $_POST['select'] ?? null;

    if ($select !== null) {
        if (!isset($_SESSION['selected']) || !is_array($_SESSION['selected'])) {
            $_SESSION['selected'] = [];
        }
        if ((string) $select === '1') {
            $_SESSION['selected'][$relative] = true;
        } else {
            unset($_SESSION['selected'][$relative]);
        }
    }
    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;
    }

    $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';
$browseDir = null;
$browseRel = '';

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

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

        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;
    }

    if (is_dir($path)) {
        $browseDir = $path;
        $browseRel = fl_normalize_relative($relative, $base);
        if ($browseRel === '') {
            $browseRel = trim(str_replace('\\', '/', $relative), '/');
        }
    }
}

$selected = isset($_SESSION['selected']) && is_array($_SESSION['selected'])
    ? array_keys($_SESSION['selected'])
    : [];
$selected = fl_sort_by_tree_order($selected);

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="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache">
<title>File Lister v<?= APP_VERSION ?><?= $embed ? '' : ' — ' . fl_h(FL_MOUNT_NAME) ?></title>
<style>
  body { background:#111; color:#eee; font-family:sans-serif; margin:24px; line-height:1.6; }
  body.embed { margin:12px; }
  body.embed h1 { display:none; }
  body.embed .hint { font-size:12px; margin-bottom:8px; }
  h1 { font-size:18px; color:#4ea3ff; margin:0 0 12px; }
  .hint { color:#888; font-size:13px; margin-bottom:16px; }
  table { width:100%; border-collapse:collapse; margin-top:8px; margin-bottom:28px; }
  th, td { border:1px solid #333; padding:8px 10px; font-size:13px; }
  th { background:#222; }
  a { color:#4ea3ff; text-decoration:none; }
  a:hover { text-decoration:underline; }
  .icon { font-size:18px; text-align:center; }
  .meta-cell { min-width:72px; 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:8px 0 12px; display:flex; flex-wrap:wrap; gap:6px; font-size:11px; }
  .sort-link { color:#888; padding:3px 8px; border:1px solid #333; border-radius:4px; }
  .sort-link.active { color:#4ea3ff; border-color:#4ea3ff; }
  h2.path-title { font-size:15px; color:#4ea3ff; margin:24px 0 8px; font-weight:600; }
  .empty { color:#666; padding:24px; border:1px dashed #333; border-radius:6px; text-align:center; }
  hr { border:none; border-top:1px solid #333; margin:24px 0; }
</style>
</head>
<body class="<?= $embed ? 'embed' : '' ?>">

<?php if (!$embed): ?>
<h1>File Lister v<?= APP_VERSION ?></h1>
<?php endif; ?>

<p class="hint">Check folders or files in Selector (left). Metadata appears here.</p>

<?php
$sortRel = $browseDir !== null ? $browseRel : $relative;
fl_render_sort_bar($sort, $order, $sortRel, $embed);

if ($browseDir !== null) {
    $pathQ = $embed ? 'embed=1&amp;' : '';
    $parentRel = dirname(str_replace('\\', '/', $browseRel));
    if ($parentRel === '.' || $parentRel === false) {
        $parentRel = '';
    }
    echo '<p class="hint" style="margin-bottom:10px">';
    if ($parentRel !== '') {
        echo '<a href="?' . $pathQ . 'path=' . rawurlencode($parentRel) . '">⤴ ' . fl_h($parentRel) . '</a>';
    } else {
        echo '<a href="?' . ($embed ? 'embed=1' : '') . '">⤴ Catalog home</a>';
    }
    echo ' · <strong>' . fl_h($browseRel) . '/</strong></p>';
    fl_render_listing_table($browseDir, $browseRel, $base, $sort, $order, $embed);
} elseif ($selected === []) {
    echo '<p class="empty">No selection yet. Use the checkboxes in Selector.</p>';
} else {
    foreach ($selected as $p) {
        $abs = fl_resolve_path($p, $base);
        if (!$abs) {
            continue;
        }
        echo '<h2 class="path-title">' . fl_h($p) . '</h2>';
        fl_render_listing_table($abs, $p, $base, $sort, $order, $embed);
    }
}
?>

<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 failed:\n' + txt); if (onNg) onNg(); }
    })
    .catch(function() { alert('Network error'); if (onNg) onNg(); });
}
</script>
</body>
</html>
