import { Router } from "express";
import multer from "multer";
import path from "path";
import fs from "fs";
import crypto from "crypto";
import { getDatabase } from "../../database.js";
import { getDatabaseAsync } from "../../database-worker-proxy.js";
import { getLibraryCacheService } from "../../libraryCacheService.js";
import { serverLogger } from "../../../utils/serverLogger.js";
import { validateFilePath } from "../../../utils/security.js";
import { resolveTrackIds, ResolveValidationError } from "../../trackResolve.js";
import { getRealtimeEnrichment } from "../../realtimeEnrichment.js";
import { getArtworkService } from "../../artworkService.js";
import { getRealtimeArtworkService } from "../../realtimeArtworkService.js";
import { smartMetadataParser } from "../../smartMetadataParser.js";
import { broadcastTracksUpdated } from "../../websocketEvents.js";
import { driveLibraryManager } from "../../cloud-storage/driveLibraryManager.js";
import { processInChunks, sortedMerge, streamJsonResponse } from "../../utils/asyncIterUtils.js";
import { getArtworkUrl, toSlimLocalTrack } from "./trackProjection.js";
// getArtworkUrl + toSlimLocalTrack + the Slim* shapes live in ./trackProjection
// (a leaf module) so they can be unit-tested without the route's heavy imports.
// Concurrency guard: prevent multiple large track requests from running simultaneously
let heavyRequestInProgress = false;
export function createTracksRoutes(appState) {
    const router = Router();
    const db = getDatabase();
    // Configure multer for artwork uploads in bulk edits
    const getArtworkDir = ()=>{
        const dbPath = db.dbPath || path.join(process.env.HOME || '', 'Library/Application Support/SOUND/sound.db');
        return path.join(path.dirname(dbPath), 'artwork');
    };
    const storage = multer.diskStorage({
        destination: (req, file, cb)=>{
            const artworkDir = getArtworkDir();
            if (!fs.existsSync(artworkDir)) {
                fs.mkdirSync(artworkDir, {
                    recursive: true
                });
            }
            cb(null, artworkDir);
        },
        filename: (req, file, cb)=>{
            const hash = crypto.createHash('md5').update(file.originalname + Date.now()).digest('hex');
            cb(null, `${hash}.jpg`);
        }
    });
    const upload = multer({
        storage,
        limits: {
            fileSize: 10 * 1024 * 1024
        },
        fileFilter: (req, file, cb)=>{
            const allowedMimes = [
                'image/jpeg',
                'image/jpg',
                'image/png',
                'image/gif',
                'image/webp'
            ];
            if (allowedMimes.includes(file.mimetype)) {
                cb(null, true);
            } else {
                cb(new Error('Invalid file type. Only images allowed.'));
            }
        }
    });
    // Get all tracks with pagination and optional real-time enrichment
    // Includes both local tracks and Google Drive tracks
    router.get("/tracks", async (req, res)=>{
        try {
            // Allow up to 100000 tracks per request for collection shuffle
            // Default to 100 for normal paginated requests
            const limit = Math.min(parseInt(req.query.limit) || 100, 100000);
            const offset = parseInt(req.query.offset) || 0;
            const orderBy = req.query.orderBy || "artist, album, title";
            const enrich = req.query.enrich === "true" || req.headers["x-enrich-metadata"] === "true";
            const includeDrive = req.query.includeDrive !== "false"; // Include Drive tracks by default
            // Slim mode: return only essential Collection-view fields (~6x smaller response)
            const slim = req.query.slim === "true";
            // Concurrency guard: reject concurrent large requests to prevent double CPU/memory usage
            if (limit > 5000 && heavyRequestInProgress) {
                res.status(503).json({
                    error: "Server busy processing another large request. Please retry.",
                    retryAfter: 5
                });
                return;
            }
            const isHeavy = limit > 5000;
            if (isHeavy) heavyRequestInProgress = true;
            try {
                // Extract album/artist filters from query parameters
                const filters = {};
                if (req.query.album) {
                    filters.album = decodeURIComponent(req.query.album);
                }
                if (req.query.artist) {
                    filters.artist = decodeURIComponent(req.query.artist);
                }
                // Run the heavy library query in the db-worker THREAD, off the main event
                // loop. On the I/O-contended NAS, a synchronous better-sqlite3 .all() over
                // the full 137K-track table with ORDER BY/OFFSET blocks the loop for
                // 10-20s, which trips the health watchdog into a full server restart
                // mid-playback (the user's "random stops"). The worker exposes getAllTracks
                // with the identical {tracks,total} shape over the same DB file;
                // libraryCacheService already reads through this same worker path.
                //
                // FAST PATH: the heavy SLIM full-library fetch (collection shuffle —
                // limit > 5000, no filters, default order, slim=true) is served from the
                // in-memory library cache instead of that 10-20s query. The DB-worker
                // timeout on this exact query was the "shuffle repeating" root cause. The
                // cache holds the slim CACHE_TRACK_COLUMNS, so it is correct ONLY for slim
                // mode; full mode / filtered / custom-ordered stay on the DB worker.
                const canUseCache = isHeavy && slim && !filters.album && !filters.artist && orderBy === "artist, album, title";
                const result = canUseCache ? await getLibraryCacheService().getAllTracksCached(limit, offset) : await getDatabaseAsync().getAllTracks(limit, offset, orderBy, filters);
                // Include Google Drive tracks if available and requested
                // IMPORTANT: Only include Drive tracks on the FIRST page (offset=0) to prevent
                // duplicates when clients paginate through the collection
                let driveTracks = [];
                if (includeDrive && offset === 0) {
                    try {
                        const driveTracksRaw = await driveLibraryManager.getAllDriveTracks();
                        // Transform Drive tracks to match local track format
                        driveTracks = driveTracksRaw.map((track)=>({
                                // Use drive: prefix for path to identify as Drive track
                                path: `drive:${track.driveFileId}`,
                                title: track.title,
                                artist: track.artist,
                                album: track.album || "Unknown Album",
                                album_artist: track.albumArtist,
                                track_no: track.trackNumber,
                                disk_no: track.discNumber,
                                year: track.year,
                                genre: track.genre ? track.genre.join(", ") : null,
                                duration: track.duration,
                                bitrate: track.bitrate,
                                sample_rate: track.sampleRate,
                                bits_per_sample: track.bitsPerSample,
                                channels: track.channels,
                                codec: track.codec,
                                file_size: track.fileSize,
                                mime_type: track.mimeType,
                                // Use drive file ID for artwork retrieval
                                artwork_hash: track.hasEmbeddedArtwork ? `drive:${track.driveFileId}` : null,
                                metadata_confidence: track.metadataConfidence,
                                source: "google-drive",
                                drive_file_id: track.driveFileId,
                                drive_path: track.drivePath
                            }));
                        // Apply filters to Drive tracks if specified
                        if (filters.album) {
                            driveTracks = driveTracks.filter((t)=>t.album?.toLowerCase().trim() === filters.album?.toLowerCase().trim());
                        }
                        if (filters.artist) {
                            driveTracks = driveTracks.filter((t)=>t.artist?.toLowerCase().trim() === filters.artist?.toLowerCase().trim());
                        }
                    } catch (driveError) {
                        serverLogger.warn("Failed to load Drive tracks", {
                            error: driveError
                        });
                    // Continue with just local tracks
                    }
                }
                // If enrichment is requested and it's a small batch, enrich in real-time
                if (enrich && result.tracks.length <= 50) {
                    const enrichmentService = getRealtimeEnrichment();
                    // Start enrichment in background for tracks that need it
                    const enrichmentPromises = result.tracks.map(async (track)=>{
                        try {
                            const enrichResult = await enrichmentService.enrichTrack(track, {
                                forceRefresh: false,
                                includeArtwork: true,
                                timeout: 5000
                            });
                            if (enrichResult.enriched) {
                                serverLogger.debug("Track enriched on request", {
                                    path: track.path,
                                    fields: enrichResult.fields
                                });
                            }
                            return enrichResult.track;
                        } catch (error) {
                            // Return original track if enrichment fails
                            return track;
                        }
                    });
                    // Wait for all enrichments to complete (with timeout)
                    const enrichedTracks = await Promise.race([
                        Promise.all(enrichmentPromises),
                        new Promise((resolve)=>setTimeout(()=>resolve(result.tracks), 10000))
                    ]);
                    result.tracks = enrichedTracks;
                }
                // Transform a local track for the API response. In slim mode, project
                // only the Collection-view fields (~6x smaller); otherwise return the
                // full track. Defined once and reused by both the small synchronous path
                // and the large chunked/streaming path below.
                const transformTrack = (track)=>{
                    if (slim) return toSlimLocalTrack(track);
                    const artworkUrl = getArtworkUrl(track.artwork_hash);
                    return {
                        ...track,
                        trackNumber: track.track_no,
                        artwork_url: artworkUrl,
                        artwork: artworkUrl,
                        url: `/api/stream/${encodeURIComponent(track.path)}`,
                        stream_url: `/api/stream/${encodeURIComponent(track.path)}`,
                        source: "local"
                    };
                };
                // For small results, use the fast synchronous path (no overhead)
                if (result.tracks.length <= 1000 && driveTracks.length === 0) {
                    const transformedLocalTracks = result.tracks.map(transformTrack);
                    res.json({
                        tracks: transformedLocalTracks,
                        total: result.total,
                        driveTracksCount: 0,
                        slim
                    });
                    return;
                }
                // Large result path: use chunked transforms + streaming JSON to avoid blocking
                // Transform local tracks in chunks, yielding to event loop between batches.
                // Uses the shared transformTrack helper so slim mode is honored here too.
                const transformedLocalTracks = await processInChunks(result.tracks, transformTrack, 1000);
                // Transform Drive tracks (typically small, but consistent treatment)
                const transformedDriveTracks = driveTracks.map((track)=>{
                    const artworkUrl = track.artwork_hash ? `/api/google-drive/artwork/${track.drive_file_id}` : null;
                    if (slim) {
                        return {
                            path: track.path,
                            title: track.title,
                            artist: track.artist,
                            album: track.album,
                            duration: track.duration,
                            year: track.year,
                            track_no: track.track_no,
                            trackNumber: track.track_no,
                            disk_no: track.disk_no,
                            artwork_hash: track.artwork_hash,
                            artwork_url: artworkUrl,
                            artwork: artworkUrl,
                            source: "google-drive",
                            drive_file_id: track.drive_file_id,
                            url: `/api/google-drive/stream/${track.drive_file_id}`,
                            stream_url: `/api/google-drive/stream/${track.drive_file_id}`
                        };
                    }
                    return {
                        ...track,
                        trackNumber: track.track_no,
                        artwork_url: artworkUrl,
                        artwork: artworkUrl,
                        file_url: `/api/google-drive/stream/${track.drive_file_id}`,
                        url: `/api/google-drive/stream/${track.drive_file_id}`,
                        stream_url: `/api/google-drive/stream/${track.drive_file_id}`
                    };
                });
                // Merge pre-sorted arrays instead of re-sorting 50k items with localeCompare.
                // Both local (SQL ORDER BY) and Drive tracks are already sorted by artist, album.
                const trackCompare = (a, b)=>{
                    const artistCmp = (a.artist || "").localeCompare(b.artist || "");
                    if (artistCmp !== 0) return artistCmp;
                    const albumCmp = (a.album || "").localeCompare(b.album || "");
                    if (albumCmp !== 0) return albumCmp;
                    return (a.title || "").localeCompare(b.title || "");
                };
                const allTracks = sortedMerge(transformedLocalTracks, transformedDriveTracks, trackCompare);
                // Stream JSON response in chunks to avoid blocking on JSON.stringify of ~50MB
                await streamJsonResponse(res, {
                    total: result.total + driveTracks.length,
                    driveTracksCount: driveTracks.length,
                    slim
                }, "tracks", allTracks, 500);
            } finally{
                if (isHeavy) heavyRequestInProgress = false;
            }
        } catch (error) {
            // Surface the real cause — a bare `{ error }` serializes an Error to `{}`
            // (message/stack aren't enumerable), which is why the "Failed to get tracks"
            // floods told us nothing. Capture message + a short stack + whether it was
            // the heavy full-library path that timed out the worker.
            serverLogger.error("Failed to get tracks", {
                error: error instanceof Error ? error.message : String(error),
                stack: error instanceof Error ? error.stack?.split("\n").slice(0, 5).join(" | ") : undefined
            });
            res.status(500).json({
                error: "Failed to retrieve tracks"
            });
        }
    });
    // Search tracks (includes local and Drive tracks)
    router.get("/tracks/search", async (req, res)=>{
        try {
            const db = await getDatabase();
            const query = req.query.q || "";
            const limit = Math.min(parseInt(req.query.limit) || 50, 200);
            const offset = parseInt(req.query.offset) || 0;
            const includeDrive = req.query.includeDrive !== "false";
            if (!query.trim()) {
                return res.status(400).json({
                    error: "Search query required"
                });
            }
            const result = db.searchTracks(query, limit, offset);
            // Transform local tracks to include artwork URLs and stream URLs
            const transformedLocalTracks = result.tracks.map((track)=>({
                    ...track,
                    trackNumber: track.track_no,
                    artwork_url: getArtworkUrl(track.artwork_hash),
                    artwork: getArtworkUrl(track.artwork_hash),
                    url: `/api/stream/${encodeURIComponent(track.path)}`,
                    stream_url: `/api/stream/${encodeURIComponent(track.path)}`,
                    source: "local"
                }));
            // Search Drive tracks if requested
            let transformedDriveTracks = [];
            if (includeDrive) {
                try {
                    const driveResults = await driveLibraryManager.searchDriveTracks(query, limit);
                    transformedDriveTracks = driveResults.map((track)=>({
                            path: `drive:${track.driveFileId}`,
                            title: track.title,
                            artist: track.artist,
                            album: track.album || "Unknown Album",
                            album_artist: track.albumArtist,
                            track_no: track.trackNumber,
                            trackNumber: track.trackNumber,
                            disk_no: track.discNumber,
                            year: track.year,
                            genre: track.genre ? track.genre.join(", ") : null,
                            duration: track.duration,
                            bitrate: track.bitrate,
                            sample_rate: track.sampleRate,
                            bits_per_sample: track.bitsPerSample,
                            channels: track.channels,
                            codec: track.codec,
                            file_size: track.fileSize,
                            mime_type: track.mimeType,
                            artwork_hash: track.hasEmbeddedArtwork ? `drive:${track.driveFileId}` : null,
                            metadata_confidence: track.metadataConfidence,
                            artwork_url: track.hasEmbeddedArtwork ? `/api/google-drive/artwork/${track.driveFileId}` : null,
                            artwork: track.hasEmbeddedArtwork ? `/api/google-drive/artwork/${track.driveFileId}` : null,
                            // Include file_url for WAVE compatibility
                            file_url: `/api/google-drive/stream/${track.driveFileId}`,
                            url: `/api/google-drive/stream/${track.driveFileId}`,
                            stream_url: `/api/google-drive/stream/${track.driveFileId}`,
                            source: "google-drive",
                            drive_file_id: track.driveFileId,
                            drive_path: track.drivePath
                        }));
                } catch (driveError) {
                    serverLogger.warn("Failed to search Drive tracks", {
                        error: driveError
                    });
                }
            }
            // Combine results
            const allTracks = [
                ...transformedLocalTracks,
                ...transformedDriveTracks
            ];
            const transformedResult = {
                tracks: allTracks,
                total: result.total + transformedDriveTracks.length,
                driveTracksCount: transformedDriveTracks.length
            };
            res.json(transformedResult);
        } catch (error) {
            serverLogger.error("Search failed", {
                error
            });
            res.status(500).json({
                error: "Search failed"
            });
        }
    });
    // Fix malformed metadata for tracks
    router.post("/tracks/fix-metadata", async (req, res)=>{
        serverLogger.info("Metadata fix request received", {
            body: req.body,
            headers: req.headers,
            hasBody: !!req.body
        });
        // Ensure body exists
        if (!req.body) {
            return res.status(400).json({
                error: "Request body is missing"
            });
        }
        try {
            const { trackIds, paths } = req.body;
            const db = getDatabase();
            const trackPaths = paths || trackIds || [];
            if (!Array.isArray(trackPaths) || trackPaths.length === 0) {
                return res.status(400).json({
                    error: "No tracks specified",
                    body: req.body
                });
            }
            const results = [];
            let fixedCount = 0;
            for (const trackPath of trackPaths){
                const track = db.getTrackByPath(trackPath);
                if (!track) {
                    results.push({
                        path: trackPath,
                        status: "not_found"
                    });
                    continue;
                }
                // Parse and fix metadata
                const parsedMetadata = smartMetadataParser.parse({
                    title: track.title,
                    artist: track.artist,
                    album: track.album,
                    path: trackPath
                });
                if (parsedMetadata.fixed) {
                    // Update the track in database with corrected metadata
                    const updatedTrack = {
                        ...track,
                        title: parsedMetadata.title,
                        artist: parsedMetadata.artist,
                        album: parsedMetadata.album,
                        metadata_confidence: parsedMetadata.confidence
                    };
                    db.upsertTrack(updatedTrack);
                    fixedCount++;
                    results.push({
                        path: trackPath,
                        status: "fixed",
                        original: {
                            title: track.title,
                            artist: track.artist,
                            album: track.album
                        },
                        fixed: {
                            title: parsedMetadata.title,
                            artist: parsedMetadata.artist,
                            album: parsedMetadata.album,
                            year: parsedMetadata.year
                        },
                        confidence: parsedMetadata.confidence,
                        issues: parsedMetadata.issues
                    });
                } else {
                    results.push({
                        path: trackPath,
                        status: "no_issues",
                        metadata: {
                            title: track.title,
                            artist: track.artist,
                            album: track.album
                        }
                    });
                }
            }
            res.json({
                success: true,
                processed: trackPaths.length,
                fixed: fixedCount,
                results
            });
        } catch (error) {
            serverLogger.error("Metadata fix failed", {
                error: error instanceof Error ? error.message : error,
                stack: error instanceof Error ? error.stack : undefined
            });
            res.status(500).json({
                error: "Failed to fix metadata",
                details: error instanceof Error ? error.message : "Unknown error"
            });
        }
    });
    // Force refresh metadata for currently playing track
    router.post("/tracks/refresh-playing", async (req, res)=>{
        serverLogger.info("Refresh playing track request", {
            body: req.body
        });
        try {
            const { trackPath, path } = req.body;
            const actualPath = trackPath || path;
            if (!actualPath) {
                return res.status(400).json({
                    error: "Track path required"
                });
            }
            const db = getDatabase(); // Not async!
            const track = db.getTrackByPath(actualPath);
            if (!track) {
                return res.status(404).json({
                    error: "Track not found"
                });
            }
            // SIMPLE FIX: Check if title needs parsing and fix it
            let needsBroadcast = false;
            let updatedTrack = {
                ...track
            }; // Full copy with all fields
            // Check for different filename patterns
            const fullPattern = /^(.+?)\s*-\s*(.+?)\s*-\s*(\d+)\s*-\s*(.+)$/; // Artist - Album - Track# - Title
            const simplePattern = /^(\d+)\s*-\s*(.+)$/; // Track# - Title
            serverLogger.info("Checking title for patterns", {
                title: track.title,
                hasTitle: !!track.title,
                matchesFull: track.title ? !!track.title.match(fullPattern) : false,
                matchesSimple: track.title ? !!track.title.match(simplePattern) : false
            });
            if (track.title) {
                // Try full pattern first: Artist - Album - TrackNumber - Title
                const fullMatch = track.title.match(fullPattern);
                if (fullMatch) {
                    updatedTrack.title = fullMatch[4].trim();
                    updatedTrack.artist = fullMatch[1].trim();
                    updatedTrack.album = updatedTrack.album || fullMatch[2].trim();
                    updatedTrack.track_no = parseInt(fullMatch[3]);
                    serverLogger.info("Cleaned full pattern metadata", {
                        path: actualPath,
                        oldTitle: track.title,
                        newTitle: updatedTrack.title,
                        artist: updatedTrack.artist,
                        album: updatedTrack.album
                    });
                    needsBroadcast = true;
                } else {
                    // Try simple pattern: TrackNumber - Title
                    const simpleMatch = track.title.match(simplePattern);
                    if (simpleMatch) {
                        updatedTrack.track_no = parseInt(simpleMatch[1]);
                        updatedTrack.title = simpleMatch[2].trim();
                        serverLogger.info("Cleaned simple pattern metadata", {
                            path: actualPath,
                            oldTitle: track.title,
                            newTitle: updatedTrack.title,
                            trackNo: updatedTrack.track_no
                        });
                        needsBroadcast = true;
                    }
                }
                // If we found a pattern match, save to database
                if (needsBroadcast) {
                    try {
                        db.upsertTrack(updatedTrack);
                        serverLogger.info("Successfully saved cleaned metadata");
                    } catch (error) {
                        serverLogger.error("Failed to save cleaned metadata", {
                            error
                        });
                    }
                }
            }
            // Also try to get artwork if missing
            const enrichmentService = getRealtimeEnrichment();
            const enrichResult = await enrichmentService.enrichTrack(updatedTrack, {
                forceRefresh: !updatedTrack.artwork_hash,
                includeArtwork: true,
                timeout: 5000
            });
            // If artwork was found, update and broadcast
            if (!updatedTrack.artwork_hash && enrichResult.track.artwork_hash) {
                updatedTrack = enrichResult.track;
                db.upsertTrack(updatedTrack);
                needsBroadcast = true;
            }
            // Add artwork URL
            const finalTrack = {
                ...updatedTrack,
                trackNumber: updatedTrack.track_no,
                artwork_url: getArtworkUrl(updatedTrack.artwork_hash),
                artwork: getArtworkUrl(updatedTrack.artwork_hash)
            };
            // ALWAYS broadcast if we made changes OR if forceRefresh was requested
            if (needsBroadcast || req.body.forceRefresh) {
                const { playerWebSocket } = await import("../../playerWebSocket.js");
                if (playerWebSocket && playerWebSocket.broadcast) {
                    playerWebSocket.broadcast({
                        type: "metadata.updated",
                        data: {
                            path: actualPath,
                            track: finalTrack,
                            timestamp: Date.now()
                        }
                    }, "metadata");
                    serverLogger.info("Broadcasting metadata update", {
                        path: actualPath,
                        title: finalTrack.title,
                        artist: finalTrack.artist,
                        hasArtwork: !!finalTrack.artwork_hash
                    });
                }
            }
            if (enrichResult.enriched) {
                serverLogger.info("Playing track enriched successfully", {
                    path: actualPath,
                    fields: enrichResult.fields,
                    title: enrichResult.track.title,
                    artist: enrichResult.track.artist
                });
                // Also trigger background enrichment for thorough processing
                const { processTrackImmediately } = await import("../../metadataEnrichment.js").catch(()=>({
                        processTrackImmediately: null
                    }));
                if (processTrackImmediately) {
                    processTrackImmediately(actualPath).catch((error)=>{
                        serverLogger.error("Background enrichment failed", {
                            error,
                            path: actualPath
                        });
                    });
                }
                return res.json({
                    success: true,
                    enriched: true,
                    track: finalTrack
                });
            }
            // Return the updated track data
            res.json({
                success: true,
                enriched: needsBroadcast,
                track: finalTrack
            });
        } catch (error) {
            serverLogger.error("Failed to refresh playing track", {
                error
            });
            res.status(500).json({
                error: "Failed to refresh track metadata"
            });
        }
    });
    // Fetch artwork for tracks without it - WAVE integration
    // IMPORTANT: This must be BEFORE /tracks/:id to avoid route conflicts
    router.post("/tracks/fetch-artwork", async (req, res)=>{
        serverLogger.info("Artwork fetch request received", {
            body: req.body
        });
        try {
            const { trackIds, paths } = req.body;
            const db = getDatabase();
            const artworkService = getArtworkService();
            // Get tracks that need artwork
            const trackPaths = paths || trackIds || [];
            if (!Array.isArray(trackPaths) || trackPaths.length === 0) {
                return res.status(400).json({
                    error: "No tracks specified"
                });
            }
            const results = [];
            let fetchedCount = 0;
            for (const trackPath of trackPaths){
                const track = db.getTrackByPath(trackPath);
                if (!track) continue;
                // Skip if already has artwork
                if (track.artwork_hash) {
                    results.push({
                        path: trackPath,
                        status: "already_has_artwork",
                        artwork_url: `/api/sound/artwork/${track.artwork_hash}`
                    });
                    continue;
                }
                try {
                    // First, parse and fix metadata if needed
                    const parsedMetadata = smartMetadataParser.parse({
                        title: track.title,
                        artist: track.artist,
                        album: track.album,
                        path: trackPath
                    });
                    // Update track with fixed metadata if it was corrected
                    if (parsedMetadata.fixed) {
                        serverLogger.info("Fixed malformed metadata", {
                            original: {
                                title: track.title,
                                artist: track.artist,
                                album: track.album
                            },
                            fixed: {
                                title: parsedMetadata.title,
                                artist: parsedMetadata.artist,
                                album: parsedMetadata.album
                            },
                            issues: parsedMetadata.issues
                        });
                        // Update the track in database with corrected metadata
                        const updatedTrack = {
                            ...track,
                            title: parsedMetadata.title,
                            artist: parsedMetadata.artist,
                            album: parsedMetadata.album,
                            metadata_confidence: parsedMetadata.confidence
                        };
                        db.upsertTrack(updatedTrack);
                        // Update the local reference for subsequent operations
                        Object.assign(track, updatedTrack);
                    }
                    // First try to extract embedded artwork, with fallback to folder artwork (cover.jpg, folder.jpg)
                    const artworkResult = await artworkService.extractArtworkWithFallback(trackPath);
                    let artworkHash = artworkResult.hash;
                    // If no embedded artwork, try to fetch from MusicBrainz
                    if (!artworkHash && track.musicbrainz_albumid) {
                        serverLogger.info("Fetching artwork from Cover Art Archive", {
                            artist: track.artist,
                            album: track.album,
                            releaseId: track.musicbrainz_albumid
                        });
                        artworkHash = await artworkService.fetchAlbumArtwork(track.musicbrainz_albumid);
                    }
                    // If still no artwork and we have corrected metadata, try to search for it
                    if (!artworkHash && parsedMetadata.fixed && parsedMetadata.artist && parsedMetadata.album) {
                        serverLogger.info("Searching for artwork with corrected metadata", {
                            artist: parsedMetadata.artist,
                            album: parsedMetadata.album
                        });
                        // Try to get enrichment data with corrected metadata
                        const enrichmentService = getRealtimeEnrichment();
                        const enrichedTrack = await enrichmentService.enrichTrack(track, {
                            forceRefresh: false,
                            includeArtwork: true,
                            timeout: 10000
                        });
                        if (enrichedTrack.track.artwork_hash) {
                            artworkHash = enrichedTrack.track.artwork_hash;
                        } else if (enrichedTrack.track.musicbrainz_albumid) {
                            // Try fetching with the newly found MusicBrainz ID
                            artworkHash = await artworkService.fetchAlbumArtwork(enrichedTrack.track.musicbrainz_albumid);
                            if (artworkHash) {
                                track.musicbrainz_albumid = enrichedTrack.track.musicbrainz_albumid;
                            }
                        }
                    }
                    if (artworkHash) {
                        // Update track with artwork hash
                        db.upsertTrack({
                            ...track,
                            artwork_hash: artworkHash
                        });
                        fetchedCount++;
                        results.push({
                            path: trackPath,
                            status: "fetched",
                            artwork_url: `/api/sound/artwork/${artworkHash}`,
                            source: track.musicbrainz_albumid ? "musicbrainz" : "embedded"
                        });
                    } else {
                        results.push({
                            path: trackPath,
                            status: "not_found",
                            artwork_url: null
                        });
                    }
                } catch (error) {
                    serverLogger.error("Failed to fetch artwork for track", {
                        path: trackPath,
                        error: error instanceof Error ? error.message : error
                    });
                    results.push({
                        path: trackPath,
                        status: "error",
                        error: error instanceof Error ? error.message : "Unknown error"
                    });
                }
            }
            res.json({
                success: true,
                processed: trackPaths.length,
                fetched: fetchedCount,
                results
            });
        } catch (error) {
            serverLogger.error("Artwork fetch failed", {
                error
            });
            res.status(500).json({
                error: "Failed to fetch artwork"
            });
        }
    });
    // Get track by ID (path) with automatic enrichment
    router.get("/tracks/:id", async (req, res)=>{
        try {
            const db = await getDatabase();
            const trackPath = decodeURIComponent(req.params.id);
            const enrich = req.query.enrich !== "false"; // Default to true for individual tracks
            const isPlaying = req.headers["x-is-playing"] === "true" || req.query.playing === "true";
            // Validate path
            if (!validateFilePath(trackPath, appState.getAllLibraries())) {
                return res.status(403).json({
                    error: "Access denied"
                });
            }
            let track = db.getTrackByPath(trackPath);
            if (!track) {
                return res.status(404).json({
                    error: "Track not found"
                });
            }
            // Automatically enrich individual track requests
            if (enrich) {
                const enrichmentService = getRealtimeEnrichment();
                try {
                    const enrichResult = await enrichmentService.enrichTrack(track, {
                        forceRefresh: isPlaying && (!track.musicbrainz_trackid || !track.artwork_hash),
                        includeArtwork: true,
                        timeout: isPlaying ? 10000 : 5000
                    });
                    if (enrichResult.enriched) {
                        serverLogger.info("Track enriched on individual request", {
                            path: track.path,
                            fields: enrichResult.fields,
                            artist: enrichResult.track.artist,
                            title: enrichResult.track.title
                        });
                        track = enrichResult.track;
                    }
                } catch (error) {
                    serverLogger.warn("Failed to enrich track on request", {
                        path: trackPath,
                        error: error instanceof Error ? error.message : error
                    });
                }
            }
            // Add artwork URL to track
            const trackWithArtwork = {
                ...track,
                trackNumber: track.track_no,
                artwork_url: getArtworkUrl(track.artwork_hash),
                artwork: getArtworkUrl(track.artwork_hash)
            };
            res.json(trackWithArtwork);
        } catch (error) {
            serverLogger.error("Failed to get track", {
                error
            });
            res.status(500).json({
                error: "Failed to get track"
            });
        }
    });
    // Update track metadata
    router.put("/tracks/:id", async (req, res)=>{
        try {
            const db = await getDatabase();
            const trackPath = decodeURIComponent(req.params.id);
            // Validate path
            if (!validateFilePath(trackPath, appState.getAllLibraries())) {
                return res.status(403).json({
                    error: "Access denied"
                });
            }
            const track = db.getTrackByPath(trackPath);
            if (!track) {
                return res.status(404).json({
                    error: "Track not found"
                });
            }
            // Update only allowed fields
            const allowedFields = [
                "title",
                "artist",
                "album",
                "year",
                "genre"
            ];
            const updates = {};
            for (const field of allowedFields){
                if (req.body[field] !== undefined) {
                    updates[field] = req.body[field];
                }
            }
            if (Object.keys(updates).length === 0) {
                return res.status(400).json({
                    error: "No valid fields to update"
                });
            }
            db.upsertTrack({
                ...track,
                ...updates
            });
            res.json({
                success: true,
                updated: updates
            });
        } catch (error) {
            serverLogger.error("Failed to update track", {
                error
            });
            res.status(500).json({
                error: "Failed to update track"
            });
        }
    });
    // Get tracks by artist
    router.get("/artists/:artist/tracks", async (req, res)=>{
        try {
            const db = await getDatabase();
            const artist = decodeURIComponent(req.params.artist);
            const limit = Math.min(parseInt(req.query.limit) || 100, 500);
            const offset = parseInt(req.query.offset) || 0;
            const tracks = db.getTracksByArtist(artist, limit, offset);
            res.json({
                tracks,
                artist
            });
        } catch (error) {
            serverLogger.error("Failed to get artist tracks", {
                error
            });
            res.status(500).json({
                error: "Failed to get artist tracks"
            });
        }
    });
    // Get tracks by album
    router.get("/albums/:artist/:album/tracks", async (req, res)=>{
        try {
            const db = await getDatabase();
            const artist = decodeURIComponent(req.params.artist);
            const album = decodeURIComponent(req.params.album);
            const tracks = db.getTracksByAlbum(artist, album);
            res.json({
                tracks,
                artist,
                album
            });
        } catch (error) {
            serverLogger.error("Failed to get album tracks", {
                error
            });
            res.status(500).json({
                error: "Failed to get album tracks"
            });
        }
    });
    // Record play AND trigger enrichment for currently playing track
    router.post("/tracks/:id/play", async (req, res)=>{
        try {
            const db = await getDatabase();
            const trackPath = decodeURIComponent(req.params.id);
            // Validate path
            if (!validateFilePath(trackPath, appState.getAllLibraries())) {
                return res.status(403).json({
                    error: "Access denied"
                });
            }
            db.recordPlay(trackPath);
            // Get the track to check if it needs artwork
            const track = db.getTrackByPath(trackPath);
            // Trigger real-time artwork scanning for playing tracks without artwork
            if (track && !track.artwork_hash) {
                const realtimeArtworkService = getRealtimeArtworkService();
                // Scan for artwork in background (don't await)
                realtimeArtworkService.getTrackArtwork(trackPath).then((artworkUrl)=>{
                    if (artworkUrl) {
                        serverLogger.info("🎨 Real-time artwork found for playing track", {
                            trackPath,
                            artworkUrl
                        });
                        // Broadcast artwork update if WebSocket is available
                        import("../../playerWebSocket.js").then(({ playerWebSocket })=>{
                            if (playerWebSocket && playerWebSocket.broadcast) {
                                // Refresh the track data with new artwork
                                const updatedTrack = db.getTrackByPath(trackPath);
                                if (updatedTrack && updatedTrack.artwork_hash) {
                                    playerWebSocket.broadcast({
                                        type: "metadata.updated",
                                        data: {
                                            path: trackPath,
                                            track: {
                                                ...updatedTrack,
                                                artwork_url: `/artwork/${updatedTrack.artwork_hash}`,
                                                artwork: `/artwork/${updatedTrack.artwork_hash}`
                                            },
                                            timestamp: Date.now()
                                        }
                                    }, "metadata");
                                }
                            }
                        }).catch(()=>{
                        // Ignore WebSocket import errors
                        });
                    }
                }).catch((error)=>{
                    serverLogger.error("Failed to scan artwork for playing track", {
                        error,
                        trackPath
                    });
                });
            }
            // Check if auto-enrichment is enabled
            const autoEnrichSetting = db.db.prepare("SELECT value FROM settings WHERE key = 'autoEnrichWaveTracks'").get();
            if (autoEnrichSetting?.value === "true") {
                serverLogger.info("🎵 Track play recorded - triggering priority enrichment", {
                    trackPath
                });
                // Import and trigger immediate enrichment
                const { processTrackImmediately } = await import("../../metadataEnrichment.js").catch(()=>({
                        processTrackImmediately: null
                    }));
                if (processTrackImmediately) {
                    // Process in background, don't await
                    processTrackImmediately(trackPath).catch((error)=>{
                        serverLogger.error("Failed to enrich playing track", {
                            error,
                            trackPath
                        });
                    });
                }
            }
            res.json({
                success: true
            });
        } catch (error) {
            serverLogger.error("Failed to record play", {
                error
            });
            res.status(500).json({
                error: "Failed to record play"
            });
        }
    });
    // Batch-resolve opaque SOUND track_ids → metadata + path, for mixed playlists.
    //
    // Privacy model (see MIXED_PLAYLISTS_DESIGN.md): the Streamline cloud stores
    // ONLY a track's opaque random track_id UUID in a playlist — no path, no
    // metadata, no server info. When a client opens such a playlist while online,
    // it calls this endpoint on the user's OWN connected SOUND server to turn
    // those opaque ids back into renderable/playable tracks. The metadata that
    // comes back flows server→client only; it never returns to the cloud DB.
    //
    // Body: { track_ids: string[] }  (UUIDs; cap MAX_RESOLVE_BATCH)
    // Returns: {
    //   resolved:   Array<{ ...track, trackNumber, artwork_url, artwork }>,
    //   unresolved: string[]   // ids with no current match → client greys them out
    // }
    // No enrichment here (kept fast for whole-playlist batches); clients can hit
    // GET /tracks/:id for on-demand enrichment of the now-playing track.
    router.post("/tracks/resolve", async (req, res)=>{
        try {
            const db = await getDatabase();
            const allowedLibraries = appState.getAllLibraries();
            const result = resolveTrackIds((req.body ?? {}).track_ids, {
                getTrackById: (id)=>db.getTrackById(id),
                isPathAllowed: (p)=>validateFilePath(p, allowedLibraries),
                artworkUrl: getArtworkUrl
            });
            res.json(result);
        } catch (error) {
            if (error instanceof ResolveValidationError) {
                return res.status(error.status).json({
                    error: error.message
                });
            }
            serverLogger.error("Failed to resolve track_ids", {
                error
            });
            res.status(500).json({
                error: "Failed to resolve track_ids"
            });
        }
    });
    // Set (or clear) the listener's star rating for a track.
    // Body: { rating: 1..5 } to set, { rating: 0 } or { rating: null } to clear.
    // Writes only `user_rating` — never the file-embedded `rating` (POPM) value.
    router.post("/tracks/:id/rate", async (req, res)=>{
        try {
            const db = await getDatabase();
            const trackPath = decodeURIComponent(req.params.id);
            // Same path-traversal guard as the /play sibling.
            if (!validateFilePath(trackPath, appState.getAllLibraries())) {
                return res.status(403).json({
                    error: "Access denied"
                });
            }
            const { rating } = req.body ?? {};
            let changes;
            try {
                changes = db.setUserRating(trackPath, rating ?? null);
            } catch (validationError) {
                // validateUserRating throws RangeError/TypeError on bad input.
                return res.status(400).json({
                    error: validationError instanceof Error ? validationError.message : "Invalid rating"
                });
            }
            if (changes === 0) {
                return res.status(404).json({
                    error: "Track not found"
                });
            }
            const track = db.getTrackByPath(trackPath);
            res.json({
                success: true,
                path: trackPath,
                user_rating: track?.user_rating ?? null
            });
        } catch (error) {
            serverLogger.error("Failed to set user rating", {
                error
            });
            res.status(500).json({
                error: "Failed to set user rating"
            });
        }
    });
    // Enrich tracks endpoint - specifically for WAVE integration
    router.post("/tracks/enrich", async (req, res)=>{
        try {
            const { paths, options = {} } = req.body;
            const enrichmentService = getRealtimeEnrichment();
            const db = await getDatabase();
            // Handle single path or array of paths
            const trackPaths = Array.isArray(paths) ? paths : [
                paths
            ];
            if (!trackPaths || trackPaths.length === 0) {
                return res.status(400).json({
                    error: "No tracks specified for enrichment"
                });
            }
            // Validate all paths
            const validPaths = trackPaths.filter((p)=>validateFilePath(p, appState.getAllLibraries()));
            if (validPaths.length === 0) {
                return res.status(403).json({
                    error: "No valid paths provided"
                });
            }
            // Get tracks from database
            const tracks = validPaths.map((path)=>db.getTrackByPath(path)).filter((t)=>t !== null);
            if (tracks.length === 0) {
                return res.status(404).json({
                    error: "No tracks found"
                });
            }
            // Start enrichment
            serverLogger.info("WAVE requested metadata enrichment", {
                count: tracks.length,
                options
            });
            // Enrich tracks
            const enrichedTracks = await enrichmentService.enrichTracks(tracks, {
                forceRefresh: options.forceRefresh || false,
                includeArtwork: options.includeArtwork !== false,
                timeout: options.timeout || 5000
            });
            // Count how many were enriched
            const enrichedCount = enrichedTracks.filter((enriched, index)=>{
                const original = tracks[index];
                return enriched.artist !== original.artist || enriched.title !== original.title || enriched.album !== original.album || enriched.year !== original.year;
            }).length;
            res.json({
                success: true,
                processed: tracks.length,
                enriched: enrichedCount,
                tracks: enrichedTracks
            });
        } catch (error) {
            serverLogger.error("Track enrichment failed", {
                error
            });
            res.status(500).json({
                error: "Failed to enrich tracks"
            });
        }
    });
    // Removed duplicate route handler
    // Get enrichment status
    router.get("/tracks/enrichment/status", (req, res)=>{
        const enrichmentService = getRealtimeEnrichment();
        const stats = enrichmentService.getStatistics();
        res.json({
            ...stats,
            enabled: true,
            message: "Real-time enrichment is active"
        });
    });
    // Bulk update tracks metadata
    router.post("/tracks/bulk-update", upload.single('artwork'), async (req, res)=>{
        try {
            // Parse request - could be FormData (with artwork) or JSON (without)
            let trackPaths;
            let updates;
            if (req.file) {
                // FormData request with artwork
                trackPaths = JSON.parse(req.body.tracks || '[]');
                updates = {
                    artist: req.body.artist,
                    album: req.body.album,
                    year: req.body.year ? parseInt(req.body.year) : undefined,
                    total_tracks: req.body.totalTracks ? parseInt(req.body.totalTracks) : undefined
                };
            } else {
                // JSON request without artwork
                trackPaths = req.body.tracks;
                updates = req.body.updates;
            }
            const db = await getDatabase();
            if (!Array.isArray(trackPaths) || trackPaths.length === 0) {
                return res.status(400).json({
                    error: "No tracks specified"
                });
            }
            if (!updates || Object.keys(updates).filter((k)=>updates[k]).length === 0 && !req.file) {
                return res.status(400).json({
                    error: "No updates specified"
                });
            }
            serverLogger.info("Bulk update request", {
                trackCount: trackPaths.length,
                updates: Object.keys(updates).filter((k)=>updates[k]),
                hasArtwork: !!req.file
            });
            // If artwork uploaded, generate hash and register with artwork service
            let artworkHash = null;
            if (req.file) {
                artworkHash = path.basename(req.file.filename, path.extname(req.file.filename));
                const artworkService = getArtworkService();
                if (artworkService && typeof artworkService.registerArtwork === 'function') {
                    artworkService.registerArtwork(artworkHash, req.file.path);
                }
                serverLogger.info("Artwork uploaded for bulk update", {
                    hash: artworkHash,
                    path: req.file.path,
                    trackCount: trackPaths.length
                });
            }
            // For bulk updates, we validate by checking if the track exists in the database
            // This is more reliable than filesystem validation since paths are already trusted
            let updatedCount = 0;
            const results = [];
            for (const trackPath of trackPaths){
                try {
                    const track = db.getTrackByPath(trackPath);
                    if (!track) {
                        results.push({
                            path: trackPath,
                            status: "not_found"
                        });
                        continue;
                    }
                    // Build update object with only allowed fields
                    const trackUpdates = {};
                    if (updates.artist) trackUpdates.artist = updates.artist;
                    if (updates.album) trackUpdates.album = updates.album;
                    if (updates.albumartist) trackUpdates.albumartist = updates.albumartist;
                    if (updates.year) trackUpdates.year = typeof updates.year === 'number' ? updates.year : parseInt(String(updates.year));
                    if (updates.total_tracks) trackUpdates.total_tracks = typeof updates.total_tracks === 'number' ? updates.total_tracks : parseInt(String(updates.total_tracks));
                    if (artworkHash) trackUpdates.artwork_hash = artworkHash;
                    // Update track in database
                    db.upsertTrack({
                        ...track,
                        ...trackUpdates
                    });
                    updatedCount++;
                    results.push({
                        path: trackPath,
                        status: "updated",
                        updates: trackUpdates
                    });
                } catch (error) {
                    serverLogger.error("Failed to update track", {
                        error,
                        path: trackPath
                    });
                    results.push({
                        path: trackPath,
                        status: "error",
                        error: error instanceof Error ? error.message : "Unknown error"
                    });
                }
            }
            serverLogger.info("Bulk update completed", {
                total: trackPaths.length,
                updated: updatedCount,
                artworkApplied: !!artworkHash
            });
            // Broadcast WebSocket event for real-time UI updates
            if (updatedCount > 0) {
                // Fetch updated tracks in a single batch query (avoid N+1)
                const updatedTracks = db.getTracksByPaths(trackPaths);
                if (updatedTracks.length > 0) {
                    // For large bulk edits, broadcast in batches to avoid overwhelming clients
                    const BATCH_SIZE = 100;
                    if (updatedTracks.length > BATCH_SIZE) {
                        serverLogger.debug(`Broadcasting ${updatedTracks.length} tracks in batches of ${BATCH_SIZE}`);
                        for(let i = 0; i < updatedTracks.length; i += BATCH_SIZE){
                            const batch = updatedTracks.slice(i, i + BATCH_SIZE);
                            broadcastTracksUpdated(appState, batch);
                        }
                        serverLogger.info("WebSocket event broadcast (batched)", {
                            event: "tracks-updated",
                            totalTracks: updatedTracks.length,
                            batches: Math.ceil(updatedTracks.length / BATCH_SIZE)
                        });
                    } else {
                        broadcastTracksUpdated(appState, updatedTracks);
                        serverLogger.info("WebSocket event broadcast", {
                            event: "tracks-updated",
                            trackCount: updatedTracks.length
                        });
                    }
                }
            }
            res.json({
                success: true,
                processed: trackPaths.length,
                updated: updatedCount,
                artworkHash,
                results
            });
        } catch (error) {
            serverLogger.error("Bulk update failed", {
                error
            });
            // Clean up uploaded file if there was an error
            if (req.file && fs.existsSync(req.file.path)) {
                fs.unlinkSync(req.file.path);
            }
            res.status(500).json({
                error: "Failed to bulk update tracks"
            });
        }
    });
    // Debug endpoint to view raw metadata and rescan a track
    // GET /tracks/debug/:id - View current DB metadata, raw file metadata, and what enhancement would produce
    // POST /tracks/debug/:id/rescan - Force rescan of the track
    router.get("/tracks/debug/:id(*)", async (req, res)=>{
        try {
            const db = await getDatabase();
            const trackPath = decodeURIComponent(req.params.id);
            // Get current database record
            const dbTrack = db.getTrackByPath(trackPath);
            if (!dbTrack) {
                return res.status(404).json({
                    error: "Track not found in database"
                });
            }
            // Try to read raw metadata from file
            let rawMetadata = null;
            let enhancedMetadata = null;
            try {
                const mm = await import("music-metadata");
                const metadata = await mm.parseFile(trackPath, {
                    skipCovers: true,
                    includeChapters: false
                });
                rawMetadata = {
                    title: metadata.common.title,
                    artist: metadata.common.artist,
                    album: metadata.common.album,
                    albumartist: metadata.common.albumartist,
                    year: metadata.common.year,
                    track: metadata.common.track,
                    disk: metadata.common.disk,
                    genre: metadata.common.genre,
                    duration: metadata.format.duration,
                    codec: metadata.format.codec,
                    bitrate: metadata.format.bitrate,
                    sampleRate: metadata.format.sampleRate
                };
                // Import and run enhanceMetadata to see what corrections would be applied
                const { enhanceMetadata, detectAlbumAsArtist } = await import("../../metadataEnhancer.js");
                // Check if artist would be detected as album
                const albumCheck = detectAlbumAsArtist(rawMetadata.artist || "");
                enhancedMetadata = enhanceMetadata({
                    artist: rawMetadata.artist,
                    album: rawMetadata.album,
                    title: rawMetadata.title,
                    albumArtist: rawMetadata.albumartist,
                    year: rawMetadata.year,
                    trackNumber: rawMetadata.track?.no,
                    filePath: trackPath
                });
                enhancedMetadata.albumCheck = albumCheck;
            } catch (readError) {
                rawMetadata = {
                    error: "Failed to read file metadata",
                    details: readError.message
                };
            }
            res.json({
                path: trackPath,
                database: {
                    title: dbTrack.title,
                    artist: dbTrack.artist,
                    album: dbTrack.album,
                    albumartist: dbTrack.albumartist,
                    year: dbTrack.year,
                    track_no: dbTrack.track_no,
                    disk_no: dbTrack.disk_no,
                    genre: dbTrack.genre,
                    metadata_confidence: dbTrack.metadata_confidence,
                    artwork_hash: dbTrack.artwork_hash
                },
                rawFile: rawMetadata,
                enhanced: enhancedMetadata
            });
        } catch (error) {
            serverLogger.error("Debug metadata failed", {
                error
            });
            res.status(500).json({
                error: "Failed to debug track metadata"
            });
        }
    });
    // Force rescan a specific track with full metadata extraction
    router.post("/tracks/debug/:id(*)/rescan", async (req, res)=>{
        try {
            const db = await getDatabase();
            const trackPath = decodeURIComponent(req.params.id);
            // Validate path exists in DB
            const existingTrack = db.getTrackByPath(trackPath);
            if (!existingTrack) {
                return res.status(404).json({
                    error: "Track not found in database"
                });
            }
            // Import scanner and rescan with full mode (not quick mode)
            const { scanFile } = await import("../../scanner.js");
            serverLogger.info("Force rescanning track", {
                trackPath
            });
            // Get all artists for intelligent matching
            const allArtists = db.getAllArtists();
            const artistTrackCounts = new Map();
            for (const a of allArtists){
                artistTrackCounts.set(a.artist, a.track_count);
            }
            // Rescan with quickMode=false for full extraction
            await scanFile(trackPath, undefined, false, artistTrackCounts);
            // Get the updated database record
            const updatedTrack = db.getTrackByPath(trackPath);
            serverLogger.info("Track rescanned", {
                trackPath,
                oldArtist: existingTrack.artist,
                newArtist: updatedTrack?.artist,
                oldAlbum: existingTrack.album,
                newAlbum: updatedTrack?.album
            });
            res.json({
                success: true,
                before: {
                    title: existingTrack.title,
                    artist: existingTrack.artist,
                    album: existingTrack.album,
                    albumartist: existingTrack.albumartist,
                    metadata_confidence: existingTrack.metadata_confidence
                },
                after: {
                    title: updatedTrack?.title,
                    artist: updatedTrack?.artist,
                    album: updatedTrack?.album,
                    albumartist: updatedTrack?.albumartist,
                    metadata_confidence: updatedTrack?.metadata_confidence
                }
            });
        } catch (error) {
            serverLogger.error("Rescan track failed", {
                error
            });
            res.status(500).json({
                error: "Failed to rescan track"
            });
        }
    });
    return router;
}

//# sourceMappingURL=tracksRoutes.js.map