<?php

function playbackFailureTypeLabels() {
    return [
        'invalid_id' => '잘못된 곡 ID',
        'not_found' => '곡 또는 파일 없음',
        'forbidden_path' => '허용되지 않은 경로',
        'empty_file' => '비어 있거나 읽을 수 없는 파일',
        'range_error' => 'Range 요청 오류',
        'open_failed' => '파일 열기 실패',
        'unsupported' => '지원하지 않는 형식',
        'network' => '네트워크 오류',
        'client_error' => '브라우저 재생 오류',
        'stream_error' => '스트리밍 오류',
        'unknown' => '알 수 없는 오류',
    ];
}

function playbackFailureStatusLabels() {
    return [
        'new' => '미확인',
        'reviewing' => '확인 중',
        'resolved' => '해결됨',
        'ignored' => '무시',
    ];
}

function playbackFailureTypeLabel($type) {
    $labels = playbackFailureTypeLabels();
    return $labels[(string)$type] ?? $labels['unknown'];
}

function playbackFailureStatusLabel($status) {
    $labels = playbackFailureStatusLabels();
    return $labels[(string)$status] ?? $labels['new'];
}

function normalizePlaybackFailureType($type) {
    $type = (string)$type;
    $labels = playbackFailureTypeLabels();
    return array_key_exists($type, $labels) ? $type : 'unknown';
}

function ensurePlaybackFailureTables() {
    static $checked = false;
    if ($checked) return;
    $checked = true;

    $pdo = getDBConnection();
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS app_playback_failures (
            id INT AUTO_INCREMENT PRIMARY KEY,
            song_id INT NOT NULL DEFAULT 0,
            error_type VARCHAR(40) NOT NULL DEFAULT 'unknown',
            status VARCHAR(30) NOT NULL DEFAULT 'new',
            failure_count INT NOT NULL DEFAULT 1,
            first_seen_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            last_seen_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            last_http_status INT DEFAULT NULL,
            last_client_code VARCHAR(40) DEFAULT NULL,
            last_source VARCHAR(30) DEFAULT NULL,
            last_user_id INT DEFAULT NULL,
            last_context VARCHAR(255) DEFAULT NULL,
            admin_note TEXT DEFAULT NULL,
            handled_by_admin INT DEFAULT NULL,
            handled_at DATETIME DEFAULT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            UNIQUE KEY uniq_playback_failure_song_type (song_id, error_type),
            KEY idx_playback_failure_status (status),
            KEY idx_playback_failure_type (error_type),
            KEY idx_playback_failure_seen (last_seen_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    ");

    $pdo->exec("
        CREATE TABLE IF NOT EXISTS app_playback_failure_events (
            id INT AUTO_INCREMENT PRIMARY KEY,
            failure_id INT NOT NULL,
            song_id INT NOT NULL DEFAULT 0,
            error_type VARCHAR(40) NOT NULL DEFAULT 'unknown',
            http_status INT DEFAULT NULL,
            client_code VARCHAR(40) DEFAULT NULL,
            source VARCHAR(30) DEFAULT NULL,
            user_id INT DEFAULT NULL,
            context VARCHAR(255) DEFAULT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            KEY idx_playback_event_failure (failure_id),
            KEY idx_playback_event_song (song_id),
            KEY idx_playback_event_created (created_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    ");
}

function playbackFailureCleanContext($value) {
    $value = trim(maskInternalPathText(ensureUtf8String($value)));
    if ($value === '') return null;
    if (function_exists('mb_substr')) return mb_substr($value, 0, 255, 'UTF-8');
    return substr($value, 0, 255);
}

function recordPlaybackFailure($songId, $errorType, $source = 'stream', $options = []) {
    try {
        ensurePlaybackFailureTables();
        $pdo = getDBConnection();
        $songId = max(0, (int)$songId);
        $errorType = normalizePlaybackFailureType($errorType);
        $source = preg_replace('/[^a-z0-9_-]/i', '', (string)$source) ?: 'stream';
        $httpStatus = isset($options['http_status']) ? (int)$options['http_status'] : null;
        $clientCode = isset($options['client_code']) ? playbackFailureCleanContext($options['client_code']) : null;
        $context = isset($options['context']) ? playbackFailureCleanContext($options['context']) : null;
        $userId = isLoggedIn() ? (int)($_SESSION['admin_user_id'] ?? 0) : null;

        $select = $pdo->prepare("SELECT id, last_seen_at FROM app_playback_failures WHERE song_id = :song_id AND error_type = :error_type LIMIT 1");
        $select->execute([
            ':song_id' => $songId,
            ':error_type' => $errorType,
        ]);
        $existing = $select->fetch();

        if ($existing) {
            $failureId = (int)$existing['id'];
            $update = $pdo->prepare("
                UPDATE app_playback_failures
                SET failure_count = failure_count + 1,
                    last_seen_at = NOW(),
                    last_http_status = :last_http_status,
                    last_client_code = :last_client_code,
                    last_source = :last_source,
                    last_user_id = :last_user_id,
                    last_context = :last_context,
                    status = CASE WHEN status = 'resolved' THEN 'new' ELSE status END,
                    updated_at = NOW()
                WHERE id = :id
            ");
            $update->execute([
                ':last_http_status' => $httpStatus,
                ':last_client_code' => $clientCode,
                ':last_source' => $source,
                ':last_user_id' => $userId ?: null,
                ':last_context' => $context,
                ':id' => $failureId,
            ]);
        } else {
            $insert = $pdo->prepare("
                INSERT INTO app_playback_failures
                    (song_id, error_type, status, failure_count, first_seen_at, last_seen_at, last_http_status, last_client_code, last_source, last_user_id, last_context)
                VALUES
                    (:song_id, :error_type, 'new', 1, NOW(), NOW(), :last_http_status, :last_client_code, :last_source, :last_user_id, :last_context)
            ");
            $insert->execute([
                ':song_id' => $songId,
                ':error_type' => $errorType,
                ':last_http_status' => $httpStatus,
                ':last_client_code' => $clientCode,
                ':last_source' => $source,
                ':last_user_id' => $userId ?: null,
                ':last_context' => $context,
            ]);
            $failureId = (int)$pdo->lastInsertId();
        }

        $lastSeen = isset($existing['last_seen_at']) ? strtotime((string)$existing['last_seen_at']) : 0;
        $shouldInsertEvent = !$existing || $lastSeen <= 0 || (time() - $lastSeen) >= 300;
        if ($shouldInsertEvent) {
            $event = $pdo->prepare("
                INSERT INTO app_playback_failure_events
                    (failure_id, song_id, error_type, http_status, client_code, source, user_id, context)
                VALUES
                    (:failure_id, :song_id, :error_type, :http_status, :client_code, :source, :user_id, :context)
            ");
            $event->execute([
                ':failure_id' => $failureId,
                ':song_id' => $songId,
                ':error_type' => $errorType,
                ':http_status' => $httpStatus,
                ':client_code' => $clientCode,
                ':source' => $source,
                ':user_id' => $userId ?: null,
                ':context' => $context,
            ]);
        }

        if (function_exists('writeAppLog') && $shouldInsertEvent) {
            writeAppLog('warning', 'playback_failure', 'Playback failure recorded', [
                'song_id' => $songId,
                'error_type' => $errorType,
                'source' => $source,
                'http_status' => $httpStatus,
            ]);
        }
        return $failureId;
    } catch (Throwable $e) {
        if (function_exists('writeAppLog')) {
            writeAppLog('error', 'playback_failure_error', 'Playback failure recording failed', [
                'error' => limitLogText($e->getMessage(), 300),
            ]);
        }
        return null;
    }
}
