<?php
require_once dirname(__DIR__) . '/config/config.php';
require_once dirname(__DIR__) . '/includes/db.php';
require_once dirname(__DIR__) . '/includes/functions.php';
require_once dirname(__DIR__) . '/includes/auth.php';
require_once dirname(__DIR__) . '/includes/layout.php';

requirePermission('music_scan');

$allowedExtensions = getAllowedExtensionsSetting();
$scanPathSetting = getScanPathSetting();
$scanRoot = resolveFilesystemPath($scanPathSetting, 'dir') ?? toWinPath($scanPathSetting);
$scanRootExists = is_string($scanRoot) && $scanRoot !== '' && is_dir($scanRoot);
$scanRootWritable = $scanRootExists && is_writable($scanRoot);
$messages = [];
$errors = [];
$uploadedFiles = [];
$createdFolderLabel = '';
$targetFolderValue = '';
$newFolderValue = '';
$customFileNameValue = '';

function musicUploadBytesToLabel($bytes) {
    $bytes = (int)$bytes;
    if ($bytes <= 0) return '0 B';
    $units = ['B', 'KB', 'MB', 'GB'];
    $i = 0;
    while ($bytes >= 1024 && $i < count($units) - 1) {
        $bytes /= 1024;
        $i++;
    }
    return number_format($bytes, $i === 0 ? 0 : 1) . ' ' . $units[$i];
}

function musicUploadIniBytes($value) {
    $value = trim((string)$value);
    if ($value === '') return 0;
    $unit = strtolower(substr($value, -1));
    $number = (float)$value;
    switch ($unit) {
        case 'g': $number *= 1024;
        case 'm': $number *= 1024;
        case 'k': $number *= 1024;
    }
    return (int)$number;
}

function musicUploadErrorLabel($code) {
    $labels = [
        UPLOAD_ERR_INI_SIZE => '서버 업로드 제한보다 파일이 큽니다.',
        UPLOAD_ERR_FORM_SIZE => '허용된 파일 크기보다 큽니다.',
        UPLOAD_ERR_PARTIAL => '파일이 일부만 업로드되었습니다.',
        UPLOAD_ERR_NO_FILE => '업로드된 파일이 없습니다.',
        UPLOAD_ERR_NO_TMP_DIR => '임시 업로드 폴더를 사용할 수 없습니다.',
        UPLOAD_ERR_CANT_WRITE => '서버에 파일을 쓸 수 없습니다.',
        UPLOAD_ERR_EXTENSION => '서버 확장 기능이 업로드를 중단했습니다.',
    ];
    return $labels[$code] ?? '파일 업로드에 실패했습니다.';
}

function musicUploadCleanSegment($value) {
    $value = trim(ensureUtf8String($value));
    if ($value === '' || $value === '.' || $value === '..') {
        throw new InvalidArgumentException('사용할 수 없는 폴더명입니다.');
    }
    if (strpos($value, '..') !== false) {
        throw new InvalidArgumentException('상위 폴더 이동 문자는 사용할 수 없습니다.');
    }
    $value = preg_replace('/[\\\\\/:*?"<>|\x00-\x1F]+/u', ' ', $value);
    $value = preg_replace('/\s+/u', ' ', $value);
    $value = trim($value, " \t\n\r\0\x0B.");
    if ($value === '') {
        throw new InvalidArgumentException('사용할 수 없는 폴더명입니다.');
    }
    if (function_exists('mb_substr')) {
        $value = mb_substr($value, 0, 80, 'UTF-8');
    } else {
        $value = substr($value, 0, 80);
    }
    return $value;
}

function musicUploadRelativeSegments($relativePath) {
    $relativePath = str_replace('\\', '/', ensureUtf8String($relativePath));
    $relativePath = trim($relativePath, " \t\n\r\0\x0B/");
    if ($relativePath === '') return [];
    if (preg_match('/^[a-z]:/i', $relativePath)) {
        throw new InvalidArgumentException('드라이브 경로는 입력할 수 없습니다.');
    }
    $segments = [];
    foreach (explode('/', $relativePath) as $segment) {
        $segment = trim($segment);
        if ($segment === '') continue;
        $segments[] = musicUploadCleanSegment($segment);
    }
    return $segments;
}

function musicUploadCleanFileBase($value, $fallback) {
    $value = trim(ensureUtf8String($value));
    if ($value === '') $value = $fallback;
    $value = str_replace('\\', '/', $value);
    $value = basename($value);
    $value = preg_replace('/\.[a-z0-9]{1,8}$/iu', '', $value);
    $value = preg_replace('/[\\\\\/:*?"<>|\x00-\x1F]+/u', ' ', $value);
    $value = preg_replace('/\s+/u', ' ', $value);
    $value = trim($value, " \t\n\r\0\x0B.");
    if ($value === '') $value = 'music-file';
    if (function_exists('mb_substr')) {
        $value = mb_substr($value, 0, 150, 'UTF-8');
    } else {
        $value = substr($value, 0, 150);
    }
    return $value;
}

function musicUploadUniquePath($dir, $baseName, $extension) {
    $extension = strtolower(trim($extension, '.'));
    $candidate = $dir . DIRECTORY_SEPARATOR . $baseName . '.' . $extension;
    $index = 1;
    while (file_exists($candidate)) {
        $candidate = $dir . DIRECTORY_SEPARATOR . $baseName . ' (' . $index . ').' . $extension;
        $index++;
    }
    return $candidate;
}

function musicUploadRelativeLabel(array $segments) {
    return empty($segments) ? '스캔 루트' : implode(' / ', $segments);
}

function musicUploadFilesArray($files) {
    if (empty($files['name']) || !is_array($files['name'])) return [];
    $result = [];
    foreach ($files['name'] as $index => $name) {
        $result[] = [
            'name' => $name,
            'type' => $files['type'][$index] ?? '',
            'tmp_name' => $files['tmp_name'][$index] ?? '',
            'error' => $files['error'][$index] ?? UPLOAD_ERR_NO_FILE,
            'size' => $files['size'][$index] ?? 0,
        ];
    }
    return $result;
}

$uploadMaxLabel = musicUploadBytesToLabel(musicUploadIniBytes(ini_get('upload_max_filesize')));
$postMaxLabel = musicUploadBytesToLabel(musicUploadIniBytes(ini_get('post_max_size')));

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    requireCsrfToken();
    $targetFolderValue = trim((string)($_POST['target_folder'] ?? ''));
    $newFolderValue = trim((string)($_POST['new_folder'] ?? ''));
    $customFileNameValue = trim((string)($_POST['custom_file_name'] ?? ''));

    try {
        if (!$scanRootExists) {
            throw new RuntimeException('스캔 대상 폴더를 찾을 수 없습니다. 먼저 스캔 설정을 확인하세요.');
        }
        if (!$scanRootWritable) {
            throw new RuntimeException('스캔 대상 폴더에 파일을 저장할 권한이 없습니다.');
        }

        $segments = musicUploadRelativeSegments($targetFolderValue);
        if ($newFolderValue !== '') {
            $segments = array_merge($segments, musicUploadRelativeSegments($newFolderValue));
        }

        $targetDir = rtrim($scanRoot, "\\/") . (empty($segments) ? '' : DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $segments));
        if (!is_dir($targetDir) && !mkdir($targetDir, 0775, true)) {
            throw new RuntimeException('대상 폴더를 만들 수 없습니다.');
        }
        if (!is_dir($targetDir) || !is_writable($targetDir)) {
            throw new RuntimeException('대상 폴더에 파일을 저장할 수 없습니다.');
        }
        if (!isPathInside($targetDir, $scanRoot)) {
            throw new RuntimeException('스캔 대상 폴더 밖에는 저장할 수 없습니다.');
        }

        $createdFolderLabel = musicUploadRelativeLabel($segments);
        if ($newFolderValue !== '') {
            $messages[] = '폴더를 준비했습니다: ' . $createdFolderLabel;
        }

        $files = musicUploadFilesArray($_FILES['music_files'] ?? []);
        $files = array_values(array_filter($files, static function ($file) {
            return (int)($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE || trim((string)($file['name'] ?? '')) !== '';
        }));
        $maxFiles = 20;
        if (count($files) > $maxFiles) {
            throw new RuntimeException('한 번에 최대 ' . $maxFiles . '개 파일까지 업로드할 수 있습니다.');
        }

        foreach ($files as $fileIndex => $file) {
            $originalName = basename(str_replace('\\', '/', ensureUtf8String($file['name'] ?? '')));
            if ((int)$file['error'] !== UPLOAD_ERR_OK) {
                $errors[] = ($originalName ?: '파일') . ': ' . musicUploadErrorLabel((int)$file['error']);
                continue;
            }
            if (!is_uploaded_file($file['tmp_name'])) {
                $errors[] = ($originalName ?: '파일') . ': 업로드 임시 파일을 확인할 수 없습니다.';
                continue;
            }

            $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
            if ($extension === '' || !in_array($extension, $allowedExtensions, true)) {
                $errors[] = ($originalName ?: '파일') . ': 허용되지 않는 확장자입니다.';
                continue;
            }

            $fallbackBase = pathinfo($originalName, PATHINFO_FILENAME);
            $baseName = (count($files) === 1 && $customFileNameValue !== '')
                ? musicUploadCleanFileBase($customFileNameValue, $fallbackBase)
                : musicUploadCleanFileBase($fallbackBase, 'music-file');
            $destination = musicUploadUniquePath($targetDir, $baseName, $extension);
            if (!isPathInside(dirname($destination), $scanRoot)) {
                $errors[] = ($originalName ?: '파일') . ': 저장 위치가 올바르지 않습니다.';
                continue;
            }
            if (!move_uploaded_file($file['tmp_name'], $destination)) {
                $errors[] = ($originalName ?: '파일') . ': 파일 저장에 실패했습니다.';
                continue;
            }

            $savedName = basename($destination);
            $uploadedFiles[] = [
                'name' => $savedName,
                'folder' => $createdFolderLabel,
                'size' => musicUploadBytesToLabel((int)$file['size']),
            ];
        }

        if (!empty($uploadedFiles)) {
            $messages[] = number_format(count($uploadedFiles)) . '개 파일을 업로드했습니다. 스캔을 실행하면 음악 목록에 반영됩니다.';
            writeAppLog('info', 'music_upload', 'Music files uploaded to scan folder', [
                'count' => count($uploadedFiles),
                'folder' => $createdFolderLabel,
                'files' => array_map(static function ($file) { return $file['name']; }, $uploadedFiles),
            ]);
            writeAuditLog('music_upload', '음악 파일 업로드: ' . count($uploadedFiles) . '개 / ' . $createdFolderLabel, 'success', 'info', 'music_upload');
        } elseif ($newFolderValue === '') {
            $errors[] = '업로드할 음악 파일을 선택하세요.';
        }
    } catch (Throwable $e) {
        $errors[] = $e->getMessage();
        writeAppLog('warning', 'music_upload_error', 'Music upload failed', ['error' => $e->getMessage()]);
        writeAuditLog('music_upload_failed', '음악 파일 업로드 실패', 'failed', 'warning', 'music_upload');
    }
}

renderHeader('음악 파일 업로드', 'scan');
?>
<div class="admin-dashboard music-upload-page">
    <div class="page-title-section music-upload-title">
        <div>
            <span class="section-kicker">관리자 업로드</span>
            <h1 class="page-main-title">음악 파일 업로드</h1>
            <p class="page-subtitle">관리자가 음악 파일을 스캔 대상 폴더 안에 저장하고, 이후 기존 스캔 로직으로 DB에 반영합니다.</p>
        </div>
        <div class="page-title-actions">
            <a class="btn btn-secondary" href="scan.php"><i class="fa-solid fa-rotate"></i> 스캔 관리</a>
            <a class="btn btn-secondary" href="../settings.php"><i class="fa-solid fa-sliders"></i> 스캔 설정</a>
        </div>
    </div>

    <?php foreach ($messages as $message): ?>
        <div class="alert-box alert-success"><div class="alert-icon"><i class="fa-solid fa-circle-check"></i></div><div class="alert-content"><p><?php echo h($message); ?></p></div></div>
    <?php endforeach; ?>
    <?php foreach ($errors as $error): ?>
        <div class="alert-box alert-danger"><div class="alert-icon"><i class="fa-solid fa-triangle-exclamation"></i></div><div class="alert-content"><p><?php echo h($error); ?></p></div></div>
    <?php endforeach; ?>

    <section class="music-upload-shell">
        <form class="music-upload-form" method="post" enctype="multipart/form-data">
            <?php echo csrfInput(); ?>
            <div class="music-upload-panel">
                <div class="music-upload-panel-head">
                    <i class="fa-solid fa-cloud-arrow-up"></i>
                    <div>
                        <h2>업로드 대상 설정</h2>
                        <p>스캔 루트 하위 폴더만 사용할 수 있습니다. 실제 서버 경로는 화면에 표시하지 않습니다.</p>
                    </div>
                </div>

                <div class="music-upload-grid">
                    <div class="music-upload-field music-upload-field-wide">
                        <span>업로드 위치</span>
                        <input type="hidden" name="target_folder" id="music-upload-target-folder" value="<?php echo h($targetFolderValue); ?>">
                        <div class="music-folder-browser" data-endpoint="music_upload_folders.php">
                            <div class="music-folder-browser-top">
                                <div class="music-folder-current">
                                    <small>현재 위치</small>
                                    <strong id="music-folder-current-label">스캔 루트</strong>
                                </div>
                                <div class="music-folder-actions">
                                    <button type="button" class="btn btn-secondary btn-sm" id="music-folder-root"><i class="fa-solid fa-house"></i> 루트</button>
                                    <button type="button" class="btn btn-secondary btn-sm" id="music-folder-up" disabled><i class="fa-solid fa-arrow-up"></i> 상위</button>
                                </div>
                            </div>
                            <div class="music-folder-list" id="music-folder-list" role="list" aria-live="polite">
                                <button type="button" class="music-folder-empty" disabled>폴더 목록을 불러오는 중입니다.</button>
                            </div>
                        </div>
                        <small>스캔 대상 루트 안에서만 탐색할 수 있습니다. 실제 서버 경로는 표시하지 않습니다.</small>
                    </div>
                    <label class="music-upload-field">
                        <span>새 폴더 만들기</span>
                        <input type="text" name="new_folder" value="<?php echo h($newFolderValue); ?>" placeholder="예: Palette">
                        <small>입력하면 대상 폴더 아래에 새 폴더를 만듭니다.</small>
                    </label>
                    <label class="music-upload-field music-upload-field-wide">
                        <span>저장 파일명</span>
                        <input type="text" name="custom_file_name" value="<?php echo h($customFileNameValue); ?>" placeholder="단일 파일 업로드 시 예: 가수 - 곡명">
                        <small>단일 파일 업로드 때만 적용합니다. 확장자는 업로드 파일 확장자를 유지합니다.</small>
                    </label>
                </div>
            </div>

            <div class="music-upload-panel">
                <div class="music-upload-panel-head">
                    <i class="fa-solid fa-file-audio"></i>
                    <div>
                        <h2>음악 파일 선택</h2>
                        <p>허용 확장자: <?php echo h(implode(', ', array_map(static function ($ext) { return '.' . $ext; }, $allowedExtensions))); ?></p>
                    </div>
                </div>
                <label class="music-upload-drop" for="music-upload-files">
                    <input id="music-upload-files" type="file" name="music_files[]" multiple accept="<?php echo h(implode(',', array_map(static function ($ext) { return '.' . $ext; }, $allowedExtensions))); ?>">
                    <i class="fa-solid fa-music"></i>
                    <strong>음악 파일을 선택하세요</strong>
                    <span>최대 업로드 <?php echo h($uploadMaxLabel); ?>, 요청 전체 <?php echo h($postMaxLabel); ?></span>
                </label>
                <div class="music-upload-file-list" id="music-upload-file-list" aria-live="polite">
                    <span>선택된 파일이 없습니다.</span>
                </div>

                <div class="music-upload-actions">
                    <button type="submit" class="btn btn-primary" <?php echo (!$scanRootExists || !$scanRootWritable) ? 'disabled' : ''; ?>>
                        <i class="fa-solid fa-upload"></i> 업로드
                    </button>
                    <a class="btn btn-secondary" href="scan.php"><i class="fa-solid fa-rotate"></i> 업로드 후 스캔 실행</a>
                </div>
            </div>
        </form>

        <aside class="music-upload-side">
            <article class="music-upload-info-card <?php echo $scanRootExists && $scanRootWritable ? 'is-ok' : 'is-warning'; ?>">
                <span>스캔 대상 상태</span>
                <strong><?php echo $scanRootExists ? ($scanRootWritable ? '업로드 가능' : '쓰기 권한 확인 필요') : '폴더 없음'; ?></strong>
                <p><?php echo h($scanRootExists ? '설정된 스캔 대상 폴더를 확인했습니다.' : '스캔 설정에서 대상 폴더를 먼저 지정하세요.'); ?></p>
            </article>
            <article class="music-upload-info-card">
                <span>저장 규칙</span>
                <strong>파일명 유지 + 안전 문자 정리</strong>
                <p>예: <b>가수 - 곡명.mp3</b> 형태로 저장할 수 있습니다. 금지 문자는 자동 정리됩니다.</p>
            </article>
            <article class="music-upload-info-card">
                <span>반영 방법</span>
                <strong>업로드 후 스캔</strong>
                <p>파일 저장 뒤 스캔을 실행하면 getID3와 기존 fallback 분석으로 음악 목록에 등록됩니다.</p>
            </article>
        </aside>
    </section>

    <?php if (!empty($uploadedFiles)): ?>
        <section class="music-upload-result">
            <div class="section-heading-row">
                <h2 class="section-title">업로드 결과</h2>
                <a class="btn btn-primary btn-sm" href="scan.php"><i class="fa-solid fa-rotate"></i> 지금 스캔하기</a>
            </div>
            <div class="music-upload-result-list">
                <?php foreach ($uploadedFiles as $file): ?>
                    <div class="music-upload-result-row">
                        <i class="fa-solid fa-file-audio"></i>
                        <strong><?php echo h($file['name']); ?></strong>
                        <span><?php echo h($file['folder']); ?></span>
                        <small><?php echo h($file['size']); ?></small>
                    </div>
                <?php endforeach; ?>
            </div>
        </section>
    <?php endif; ?>
</div>

<script>
(function () {
    const input = document.getElementById('music-upload-files');
    const list = document.getElementById('music-upload-file-list');
    if (input && list && list.dataset.bound !== '1') {
        list.dataset.bound = '1';
        input.addEventListener('change', function () {
            const files = Array.from(input.files || []);
            list.textContent = '';
            if (!files.length) {
                const empty = document.createElement('span');
                empty.textContent = '선택된 파일이 없습니다.';
                list.appendChild(empty);
                return;
            }
            files.slice(0, 8).forEach(function (file) {
                const item = document.createElement('span');
                item.textContent = file.name;
                list.appendChild(item);
            });
            if (files.length > 8) {
                const more = document.createElement('strong');
                more.textContent = '외 ' + (files.length - 8).toLocaleString('ko-KR') + '개';
                list.appendChild(more);
            }
        });
    }

    const browser = document.querySelector('.music-folder-browser');
    const targetInput = document.getElementById('music-upload-target-folder');
    const currentLabel = document.getElementById('music-folder-current-label');
    const folderList = document.getElementById('music-folder-list');
    const rootButton = document.getElementById('music-folder-root');
    const upButton = document.getElementById('music-folder-up');
    if (!browser || !targetInput || !currentLabel || !folderList || browser.dataset.bound === '1') return;
    browser.dataset.bound = '1';

    const endpoint = browser.dataset.endpoint || 'music_upload_folders.php';
    let currentPath = (targetInput.value || '').replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
    let parentPath = '';

    function setLoading(message) {
        folderList.textContent = '';
        const item = document.createElement('button');
        item.type = 'button';
        item.className = 'music-folder-empty';
        item.disabled = true;
        item.textContent = message;
        folderList.appendChild(item);
    }

    function renderFolders(data) {
        currentPath = data.path || '';
        parentPath = data.parent || '';
        targetInput.value = currentPath;
        currentLabel.textContent = data.label || '스캔 루트';
        if (upButton) upButton.disabled = !data.can_go_up;

        folderList.textContent = '';
        const folders = Array.isArray(data.folders) ? data.folders : [];
        if (!folders.length) {
            const empty = document.createElement('button');
            empty.type = 'button';
            empty.className = 'music-folder-empty';
            empty.disabled = true;
            empty.textContent = '하위 폴더가 없습니다. 새 폴더명을 입력해 만들 수 있습니다.';
            folderList.appendChild(empty);
            return;
        }
        folders.forEach(function (folder) {
            const button = document.createElement('button');
            button.type = 'button';
            button.className = 'music-folder-item';
            button.dataset.path = folder.path || '';
            button.innerHTML = '<i class="fa-solid fa-folder"></i><span></span><small>열기</small>';
            button.querySelector('span').textContent = folder.name || '폴더';
            button.addEventListener('click', function () {
                loadFolder(button.dataset.path || '');
            });
            folderList.appendChild(button);
        });
    }

    function loadFolder(path) {
        setLoading('폴더 목록을 불러오는 중입니다.');
        const url = endpoint + '?path=' + encodeURIComponent(path || '');
        fetch(url, {
            headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
            cache: 'no-store',
            credentials: 'same-origin'
        })
            .then(function (response) { return response.json().then(function (data) { return { ok: response.ok, data: data }; }); })
            .then(function (result) {
                if (!result.ok || !result.data.success) throw new Error(result.data.message || '폴더 목록을 불러오지 못했습니다.');
                renderFolders(result.data);
            })
            .catch(function (error) {
                setLoading(error.message || '폴더 목록을 불러오지 못했습니다.');
            });
    }

    if (rootButton) {
        rootButton.addEventListener('click', function () { loadFolder(''); });
    }
    if (upButton) {
        upButton.addEventListener('click', function () { loadFolder(parentPath || ''); });
    }
    loadFolder(currentPath);
})();
</script>

<?php renderFooter(); ?>
