function isFuzzyMatch(str1, str2) {
    if (!str1 || !str2) return false;
    const s1 = str1.toLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
    const s2 = str2.toLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
    return s1.includes(s2) || s2.includes(s1);
}

function findBestMatch(items, targetArtist, targetAlbum, importOptions) {
    if (!items || items.length === 0) return null;
    if (!importOptions?.strictArtistMatch && !importOptions?.strictAlbumMatch) return items[0];

    return (
        items.find((item) => {
            let artistOk = true;
            let albumOk = true;

            if (importOptions.strictArtistMatch && targetArtist) {
                const itemArtist = item.artist?.name || item.artists?.[0]?.name;
                if (!isFuzzyMatch(itemArtist, targetArtist)) artistOk = false;
            }

            if (importOptions.strictAlbumMatch && targetAlbum) {
                const itemAlbum = item.album?.title;
                if (itemAlbum && !isFuzzyMatch(itemAlbum, targetAlbum)) albumOk = false;
            }

            return artistOk && albumOk;
        }) || null
    );
}

/**
 * Helper function to get track artists string
 */
function getTrackArtists(track) {
    if (track.artists && track.artists.length > 0) {
        return track.artists.map((artist) => artist.name).join(', ');
    }
    return track.artist?.name || 'Unknown Artist';
}

/**
 * Generates CSV playlist export
 * @param {Object} _playlist - Playlist metadata
 * @param {Array} tracks - Array of track objects
 * @returns {string} CSV content
 */
export function generateCSV(_playlist, tracks) {
    const headers = ['Track Name', 'Artist Name(s)', 'Album', 'Duration'];
    let content = headers.map((h) => `"${h}"`).join(',') + '\n';

    tracks.forEach((track) => {
        const title = (track.title || '').replace(/"/g, '""');
        const artist = getTrackArtists(track).replace(/"/g, '""');
        const album = (track.album?.title || '').replace(/"/g, '""');
        const duration = formatDuration(track.duration || 0);

        content += `"${title}","${artist}","${album}","${duration}"\n`;
    });

    return content;
}

/**
 * Generates XSPF (XML Shareable Playlist Format) export
 * @param {Object} playlist - Playlist metadata
 * @param {Array} tracks - Array of track objects
 * @returns {string} XSPF XML content
 */
export function generateXSPF(playlist, tracks) {
    const date = new Date().toISOString();

    let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
    xml += '<playlist xmlns="http://xspf.org/ns/0/" version="1">\n';
    xml += `  <title>${escapeXml(playlist.title || 'Unknown Playlist')}</title>\n`;
    xml += `  <creator>${escapeXml(playlist.artist || 'Various Artists')}</creator>\n`;
    xml += `  <date>${date}</date>\n`;
    xml += '  <trackList>\n';

    tracks.forEach((track) => {
        xml += '    <track>\n';
        xml += `      <title>${escapeXml(track.title || 'Unknown Title')}</title>\n`;
        xml += `      <creator>${escapeXml(getTrackArtists(track))}</creator>\n`;
        if (track.album?.title) {
            xml += `      <album>${escapeXml(track.album.title)}</album>\n`;
        }
        if (track.duration) {
            xml += `      <duration>${Math.round(track.duration * 1000)}</duration>\n`;
        }
        xml += '    </track>\n';
    });

    xml += '  </trackList>\n';
    xml += '</playlist>\n';

    return xml;
}

/**
 * Generates generic XML playlist export
 * @param {Object} playlist - Playlist metadata
 * @param {Array} tracks - Array of track objects
 * @returns {string} XML content
 */
export function generateXML(playlist, tracks) {
    const date = new Date().toISOString();

    let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
    xml += '<playlist>\n';
    xml += `  <name>${escapeXml(playlist.title || 'Unknown Playlist')}</name>\n`;
    xml += `  <creator>${escapeXml(playlist.artist || 'Various Artists')}</creator>\n`;
    xml += `  <created>${date}</created>\n`;
    xml += `  <trackCount>${tracks.length}</trackCount>\n`;
    xml += '  <tracks>\n';

    tracks.forEach((track, index) => {
        xml += '    <track>\n';
        xml += `      <position>${index + 1}</position>\n`;
        xml += `      <title>${escapeXml(track.title || '')}</title>\n`;
        xml += `      <artist>${escapeXml(getTrackArtists(track) || '')}</artist>\n`;
        xml += `      <album>${escapeXml(track.album?.title || '')}</album>\n`;
        xml += `      <duration>${Math.round(track.duration || 0)}</duration>\n`;
        xml += '    </track>\n';
    });

    xml += '  </tracks>\n';
    xml += '</playlist>\n';

    return xml;
}

/**
 * Parses CSV playlist format
 * @param {string} csvText - CSV content
 * @param {Function} api - API instance for searching tracks
 * @param {Function} onProgress - Progress callback
 * @returns {Promise<{tracks: Array, missingTracks: Array}>}
 */
const HEADER_MAPPINGS = {
    track: ['track name', 'title', 'song', 'name', 'track', 'track title'],
    artist: ['artist name(s)', 'artist name', 'artist', 'artists', 'creator', 'artist names'],
    album: ['album', 'album name'],
    type: ['type', 'category', 'kind'],
    isrc: ['isrc', 'isrc code'],
    spotifyId: ['spotify - id', 'spotify id', 'spotify_id', 'spotifyid'],
    playlistName: ['playlist name', 'playlist', 'playlist title'],
    duration: ['duration', 'length', 'time'],
};

function normalizeHeader(header) {
    if (!header) return '';
    return header
        .replace(/^\uFEFF/, '')
        .toLowerCase()
        .trim()
        .replace(/[_\s]+/g, ' ');
}

function mapHeaders(rawHeaders) {
    const mapped = {};
    rawHeaders.forEach((header, index) => {
        const normalized = normalizeHeader(header);
        for (const [key, aliases] of Object.entries(HEADER_MAPPINGS)) {
            if (aliases.includes(normalized)) {
                mapped[key] = index;
                break;
            }
        }
    });
    return mapped;
}

function detectCSVFormat(mappedHeaders) {
    const hasType = mappedHeaders.type !== undefined;
    const hasTrack = mappedHeaders.track !== undefined;
    const hasArtist = mappedHeaders.artist !== undefined;
    const hasAlbum = mappedHeaders.album !== undefined;

    if (hasTrack && hasArtist) {
        return {
            format: 'library',
            hasMultipleTypes: hasType,
            supportsTracks: true,
            supportsAlbums: hasAlbum,
            supportsArtists: hasArtist && !hasTrack,
        };
    }

    if (hasArtist && !hasTrack) {
        return {
            format: 'artists',
            hasMultipleTypes: false,
            supportsTracks: false,
            supportsAlbums: false,
            supportsArtists: true,
        };
    }

    return {
        format: 'playlist',
        hasMultipleTypes: false,
        supportsTracks: true,
        supportsAlbums: false,
        supportsArtists: false,
    };
}

export async function parseDynamicCSV(csvText, api, onProgress, options = {}) {
    const lines = csvText.trim().split('\n');
    if (lines.length < 2) {
        return {
            format: 'unknown',
            tracks: [],
            albums: [],
            artists: [],
            missingItems: [],
            playlists: {},
        };
    }

    const parseLine = (text) => {
        const values = [];
        let current = '';
        let inQuote = false;

        for (let i = 0; i < text.length; i++) {
            const char = text[i];

            if (char === '"') {
                inQuote = !inQuote;
            } else if (char === ',' && !inQuote) {
                values.push(current);
                current = '';
            } else {
                current += char;
            }
        }
        values.push(current);

        return values.map((v) => v.trim().replace(/^"|"$/g, '').replace(/""/g, '"').trim());
    };

    const rawHeaders = parseLine(lines[0]);
    const mappedHeaders = mapHeaders(rawHeaders);
    const formatInfo = detectCSVFormat(mappedHeaders);
    const rows = lines.slice(1);

    const tracks = [];
    const albums = [];
    const artists = [];
    const missingItems = [];
    const playlists = {};
    const totalItems = rows.length;

    const getItemType = (values) => {
        if (mappedHeaders.type !== undefined) {
            const typeValue = values[mappedHeaders.type]?.toLowerCase().trim();
            if (typeValue === 'album' || typeValue === 'favorite album') return 'album';
            if (typeValue === 'artist' || typeValue === 'favorite artist') return 'artist';
            if (
                typeValue === 'favorite' ||
                typeValue === 'favorite track' ||
                typeValue === 'track' ||
                typeValue === 'playlist'
            )
                return 'track';
        }

        const hasTrackName = mappedHeaders.track !== undefined && values[mappedHeaders.track];
        const hasArtistName = mappedHeaders.artist !== undefined && values[mappedHeaders.artist];
        const hasAlbumName = mappedHeaders.album !== undefined && values[mappedHeaders.album];

        if (hasTrackName && hasArtistName) return 'track';

        if (hasTrackName && hasAlbumName && values[mappedHeaders.track] === values[mappedHeaders.album]) {
            return hasArtistName ? 'track' : 'album';
        }

        if (hasAlbumName && hasArtistName && !hasTrackName) return 'album';
        if (hasArtistName && !hasTrackName && !hasAlbumName) return 'artist';

        return 'track';
    };

    for (let i = 0; i < rows.length; i++) {
        const row = rows[i];
        if (!row.trim()) continue;

        const values = parseLine(row);
        const itemType = getItemType(values);

        const trackName = mappedHeaders.track !== undefined ? values[mappedHeaders.track] : '';
        const artistName = mappedHeaders.artist !== undefined ? values[mappedHeaders.artist] : '';
        const albumName = mappedHeaders.album !== undefined ? values[mappedHeaders.album] : '';
        const isrc = mappedHeaders.isrc !== undefined ? values[mappedHeaders.isrc] : '';
        const playlistName = mappedHeaders.playlistName !== undefined ? values[mappedHeaders.playlistName] : '';
        const typeValue = mappedHeaders.type !== undefined ? values[mappedHeaders.type]?.toLowerCase().trim() : '';
        const isFavorite = typeValue.includes('favorite');

        if (onProgress) {
            onProgress({
                current: i,
                total: totalItems,
                currentItem: trackName || artistName || albumName || 'Unknown item',
                type: itemType,
            });
        }

        await new Promise((resolve) => setTimeout(resolve, 300));

        try {
            if (itemType === 'track') {
                let foundTrack = null;

                if (isrc) {
                    const searchResult = await api.searchTracks(`isrc:${isrc}`);
                    if (searchResult.items && searchResult.items.length > 0) {
                        foundTrack = searchResult.items.find((t) => t.isrc === isrc) || searchResult.items[0];
                    }
                }

                if (!foundTrack && trackName && artistName) {
                    const searchQuery = `"${trackName}" ${artistName}`.trim();
                    const searchResult = await api.searchTracks(searchQuery);
                    if (searchResult.items && searchResult.items.length > 0) {
                        foundTrack = findBestMatch(searchResult.items, artistName, albumName, options);
                    }
                }

                if (foundTrack) {
                    if (isFavorite) foundTrack.isFavorite = true;
                    tracks.push(foundTrack);
                    if (playlistName) {
                        if (!playlists[playlistName]) {
                            playlists[playlistName] = [];
                        }
                        playlists[playlistName].push(foundTrack);
                    }
                } else {
                    missingItems.push({
                        type: 'track',
                        title: trackName,
                        artist: artistName,
                        album: albumName,
                        isrc: isrc,
                    });
                }
            } else if (itemType === 'album') {
                let foundAlbum = null;

                if (artistName && albumName) {
                    const searchQuery = `"${albumName}" ${artistName}`.trim();
                    const searchResult = await api.searchAlbums(searchQuery);
                    if (searchResult.items && searchResult.items.length > 0) {
                        foundAlbum = searchResult.items[0];
                    }
                }

                if (foundAlbum) {
                    if (isFavorite) foundAlbum.isFavorite = true;
                    albums.push(foundAlbum);
                } else {
                    missingItems.push({
                        type: 'album',
                        title: albumName,
                        artist: artistName,
                    });
                }
            } else if (itemType === 'artist') {
                let foundArtist = null;

                if (artistName) {
                    const searchResult = await api.searchArtists(artistName);
                    if (searchResult.items && searchResult.items.length > 0) {
                        foundArtist = searchResult.items[0];
                    }
                }

                if (foundArtist) {
                    if (isFavorite) foundArtist.isFavorite = true;
                    artists.push(foundArtist);
                } else {
                    missingItems.push({
                        type: 'artist',
                        name: artistName,
                    });
                }
            }
        } catch {
            missingItems.push({
                type: itemType,
                title: trackName || albumName,
                artist: artistName,
            });
        }
    }

    return {
        format: formatInfo.format,
        tracks,
        albums,
        artists,
        missingItems,
        playlists,
        stats: {
            totalItems,
            tracksFound: tracks.length,
            albumsFound: albums.length,
            artistsFound: artists.length,
            missingCount: missingItems.length,
            playlistCount: Object.keys(playlists).length,
        },
    };
}

/**
 * Imports CSV result to library
 * @param {Object} csvResult - Result from parseDynamicCSV
 * @param {Object} db - Database instance
 * @param {Function} onProgress - Progress callback
 * @param {Object} options - Import options
 * @returns {Promise<Object} - Results summary
 */
export async function importToLibrary(csvResult, db, onProgress, options = {}) {
    const { favoriteTracks = true, favoriteAlbums = true, favoriteArtists = true } = options;

    const results = {
        tracks: { added: 0, failed: 0 },
        albums: { added: 0, failed: 0 },
        artists: { added: 0, failed: 0 },
        playlists: { created: 0, tracksAdded: 0 },
    };

    const addedTrackIds = new Set();
    const addedAlbumIds = new Set();
    const addedArtistIds = new Set();

    for (const track of csvResult.tracks) {
        if (!addedTrackIds.has(track.id)) {
            try {
                if (favoriteTracks || track.isFavorite) {
                    await db.toggleFavorite('track', track);
                    results.tracks.added++;
                }
                addedTrackIds.add(track.id);
            } catch {
                results.tracks.failed++;
            }
        }
        if (onProgress) onProgress({ action: 'track', item: track.title });
    }

    for (const album of csvResult.albums) {
        if (!addedAlbumIds.has(album.id)) {
            try {
                if (favoriteAlbums || album.isFavorite) {
                    await db.toggleFavorite('album', album);
                    results.albums.added++;
                }
                addedAlbumIds.add(album.id);
            } catch {
                results.albums.failed++;
            }
        }
        if (onProgress) onProgress({ action: 'album', item: album.title });
    }

    for (const artist of csvResult.artists) {
        if (!addedArtistIds.has(artist.id)) {
            try {
                if (favoriteArtists || artist.isFavorite) {
                    await db.toggleFavorite('artist', artist);
                    results.artists.added++;
                }
                addedArtistIds.add(artist.id);
            } catch {
                results.artists.failed++;
            }
        }
        if (onProgress) onProgress({ action: 'artist', item: artist.name });
    }

    for (const [playlistName, playlistTracks] of Object.entries(csvResult.playlists)) {
        if (playlistTracks.length > 0) {
            try {
                await db.createPlaylist(playlistName, playlistTracks);
                results.playlists.created++;
                results.playlists.tracksAdded += playlistTracks.length;
            } catch {
                console.warn(`Failed to create playlist: ${playlistName}`);
            }
        }
        if (onProgress) onProgress({ action: 'playlist', item: playlistName });
    }

    return results;
}

export async function parseCSV(csvText, api, onProgress, importOptions = {}) {
    const lines = csvText.trim().split('\n');
    if (lines.length < 2) return { tracks: [], missingTracks: [] };

    const parseLine = (text) => {
        const values = [];
        let current = '';
        let inQuote = false;

        for (let i = 0; i < text.length; i++) {
            const char = text[i];

            if (char === '"') {
                inQuote = !inQuote;
            } else if (char === ',' && !inQuote) {
                values.push(current);
                current = '';
            } else {
                current += char;
            }
        }
        values.push(current);

        return values.map((v) => v.trim().replace(/^"|"$/g, '').replace(/""/g, '"').trim());
    };

    const headers = parseLine(lines[0]);
    const rows = lines.slice(1);

    const tracks = [];
    const missingTracks = [];
    const totalTracks = rows.length;

    for (let i = 0; i < rows.length; i++) {
        const row = rows[i];
        if (!row.trim()) continue;

        const values = parseLine(row);

        if (values.length >= headers.length) {
            let trackTitle = '';
            let artistNames = '';
            let albumName = '';

            headers.forEach((header, index) => {
                const value = values[index];
                if (!value) return;

                switch (header.toLowerCase()) {
                    case 'track name':
                    case 'title':
                    case 'song':
                    case 'name':
                        trackTitle = value;
                        break;
                    case 'artist name(s)':
                    case 'artist name':
                    case 'artist':
                    case 'artists':
                    case 'creator':
                        artistNames = value;
                        break;
                    case 'album':
                    case 'album name':
                        albumName = value;
                        break;
                }
            });

            if (onProgress) {
                onProgress({
                    current: i,
                    total: totalTracks,
                    currentTrack: trackTitle || 'Unknown track',
                    currentArtist: artistNames || '',
                });
            }

            if (trackTitle && artistNames) {
                await new Promise((resolve) => setTimeout(resolve, 300));

                try {
                    const searchQuery = `"${trackTitle}" ${artistNames}`.trim();
                    const searchResult = await api.searchTracks(searchQuery);

                    if (searchResult.items && searchResult.items.length > 0) {
                        const match = findBestMatch(searchResult.items, artistNames, albumName, importOptions);
                        if (match) tracks.push(match);
                        else missingTracks.push({ title: trackTitle, artist: artistNames, album: albumName });
                    } else {
                        missingTracks.push({ title: trackTitle, artist: artistNames, album: albumName });
                    }
                } catch {
                    missingTracks.push({ title: trackTitle, artist: artistNames, album: albumName });
                }
            }
        }
    }

    return { tracks, missingTracks };
}

/**
 * Parses JSPF (JSON Shareable Playlist Format)
 * @param {string} jspfText - JSPF JSON content
 * @param {Function} api - API instance for searching tracks
 * @param {Function} onProgress - Progress callback
 * @returns {Promise<{tracks: Array, missingTracks: Array}>}
 */
export async function parseJSPF(jspfText, api, onProgress) {
    try {
        const jspfData = JSON.parse(jspfText);

        if (!jspfData.playlist || !Array.isArray(jspfData.playlist.track)) {
            throw new Error('Invalid JSPF format: missing playlist or track array');
        }

        const playlist = jspfData.playlist;
        const tracks = [];
        const missingTracks = [];
        const totalTracks = playlist.track.length;

        for (let i = 0; i < playlist.track.length; i++) {
            const jspfTrack = playlist.track[i];
            const trackTitle = jspfTrack.title;
            const trackCreator = jspfTrack.creator;
            const trackAlbum = jspfTrack.album;

            if (onProgress) {
                onProgress({
                    current: i,
                    total: totalTracks,
                    currentTrack: trackTitle || 'Unknown track',
                    currentArtist: trackCreator || '',
                });
            }

            if (trackTitle && trackCreator) {
                await new Promise((resolve) => setTimeout(resolve, 300));

                try {
                    const searchQuery = `${trackTitle} ${trackCreator}`;
                    const searchResults = await api.searchTracks(searchQuery);

                    if (searchResults.items && searchResults.items.length > 0) {
                        tracks.push(searchResults.items[0]);
                    } else {
                        missingTracks.push({ title: trackTitle, artist: trackCreator, album: trackAlbum });
                    }
                } catch {
                    missingTracks.push({ title: trackTitle, artist: trackCreator, album: trackAlbum });
                }
            }
        }

        return { tracks, missingTracks };
    } catch (error) {
        throw new Error('Failed to parse JSPF: ' + error.message);
    }
}

/**
 * Parses XSPF (XML Shareable Playlist Format)
 * @param {string} xspfText - XSPF XML content
 * @param {Function} api - API instance for searching tracks
 * @param {Function} onProgress - Progress callback
 * @returns {Promise<{tracks: Array, missingTracks: Array}>}
 */
export async function parseXSPF(xspfText, api, onProgress) {
    // Validate input to prevent potential XXE attacks
    if (!xspfText || typeof xspfText !== 'string' || xspfText.length > 10 * 1024 * 1024) {
        throw new Error('Invalid XSPF content');
    }
    // Reject potential XXE payloads
    if (xspfText.includes('<!ENTITY') || xspfText.includes('<!DOCTYPE')) {
        throw new Error('XSPF content contains potentially dangerous declarations');
    }

    const parser = new DOMParser();
    const xmlDoc = parser.parseFromString(xspfText, 'application/xml');

    const trackList = xmlDoc.getElementsByTagName('track');
    const tracks = [];
    const missingTracks = [];
    const totalTracks = trackList.length;

    for (let i = 0; i < trackList.length; i++) {
        const trackEl = trackList[i];
        const title = trackEl.getElementsByTagName('title')[0]?.textContent || '';
        const creator = trackEl.getElementsByTagName('creator')[0]?.textContent || '';
        const album = trackEl.getElementsByTagName('album')[0]?.textContent || '';

        if (onProgress) {
            onProgress({
                current: i,
                total: totalTracks,
                currentTrack: title || 'Unknown track',
                currentArtist: creator || '',
            });
        }

        if (title && creator) {
            await new Promise((resolve) => setTimeout(resolve, 300));

            try {
                const searchQuery = `${title} ${creator}`;
                const searchResults = await api.searchTracks(searchQuery);

                if (searchResults.items && searchResults.items.length > 0) {
                    tracks.push(searchResults.items[0]);
                } else {
                    missingTracks.push({ title, artist: creator, album });
                }
            } catch {
                missingTracks.push({ title, artist: creator, album });
            }
        }
    }

    return { tracks, missingTracks };
}

/**
 * Parses generic XML playlist format
 * @param {string} xmlText - XML content
 * @param {Function} api - API instance for searching tracks
 * @param {Function} onProgress - Progress callback
 * @returns {Promise<{tracks: Array, missingTracks: Array}>}
 */
export async function parseXML(xmlText, api, onProgress) {
    // Validate input to prevent potential XXE attacks
    if (!xmlText || typeof xmlText !== 'string' || xmlText.length > 10 * 1024 * 1024) {
        throw new Error('Invalid XML content');
    }
    // Reject potential XXE payloads
    if (xmlText.includes('<!ENTITY') || xmlText.includes('<!DOCTYPE')) {
        throw new Error('XML content contains potentially dangerous declarations');
    }

    const parser = new DOMParser();
    const xmlDoc = parser.parseFromString(xmlText, 'application/xml');

    // Try different track element names
    let trackElements = xmlDoc.getElementsByTagName('track');
    if (trackElements.length === 0) {
        trackElements = xmlDoc.getElementsByTagName('song');
    }
    if (trackElements.length === 0) {
        trackElements = xmlDoc.getElementsByTagName('item');
    }

    const tracks = [];
    const missingTracks = [];
    const totalTracks = trackElements.length;

    for (let i = 0; i < trackElements.length; i++) {
        const trackEl = trackElements[i];

        // Try different element names for title/artist
        const title =
            trackEl.getElementsByTagName('title')[0]?.textContent ||
            trackEl.getElementsByTagName('name')[0]?.textContent ||
            '';
        const artist =
            trackEl.getElementsByTagName('artist')[0]?.textContent ||
            trackEl.getElementsByTagName('creator')[0]?.textContent ||
            trackEl.getElementsByTagName('performer')[0]?.textContent ||
            '';
        const album = trackEl.getElementsByTagName('album')[0]?.textContent || '';

        if (onProgress) {
            onProgress({
                current: i,
                total: totalTracks,
                currentTrack: title || 'Unknown track',
                currentArtist: artist || '',
            });
        }

        if (title && artist) {
            await new Promise((resolve) => setTimeout(resolve, 300));

            try {
                const searchQuery = `${title} ${artist}`;
                const searchResults = await api.searchTracks(searchQuery);

                if (searchResults.items && searchResults.items.length > 0) {
                    tracks.push(searchResults.items[0]);
                } else {
                    missingTracks.push({ title, artist, album });
                }
            } catch {
                missingTracks.push({ title, artist, album });
            }
        }
    }

    return { tracks, missingTracks };
}

/**
 * Parses M3U/M3U8 playlist format
 * @param {string} m3uText - M3U content
 * @param {Function} api - API instance for searching tracks
 * @param {Function} onProgress - Progress callback
 * @returns {Promise<{tracks: Array, missingTracks: Array}>}
 */
export async function parseM3U(m3uText, api, onProgress) {
    const lines = m3uText.trim().split('\n');
    const tracks = [];
    const missingTracks = [];

    const trackInfo = [];
    let currentInfo = null;

    // Parse M3U format
    for (const line of lines) {
        const trimmed = line.trim();
        if (!trimmed || trimmed.startsWith('#EXTM3U')) continue;

        if (trimmed.startsWith('#EXTINF:')) {
            // Parse EXTINF line: #EXTINF:duration,Artist - Title
            const match = trimmed.match(/#EXTINF:(-?\d+)?,(.+)/);
            if (match) {
                const displayName = match[2];
                const parts = displayName.split(' - ');
                currentInfo = {
                    title: parts.length > 1 ? parts.slice(1).join(' - ') : displayName,
                    artist: parts.length > 1 ? parts[0] : '',
                };
            }
        } else if (!trimmed.startsWith('#')) {
            // This is a file path line
            if (currentInfo) {
                trackInfo.push(currentInfo);
                currentInfo = null;
            }
        }
    }

    const totalTracks = trackInfo.length;

    for (let i = 0; i < trackInfo.length; i++) {
        const info = trackInfo[i];

        if (onProgress) {
            onProgress({
                current: i,
                total: totalTracks,
                currentTrack: info.title || 'Unknown track',
                currentArtist: info.artist || '',
            });
        }

        if (info.title) {
            await new Promise((resolve) => setTimeout(resolve, 300));

            try {
                const searchQuery = info.artist ? `${info.title} ${info.artist}` : info.title;
                const searchResults = await api.searchTracks(searchQuery);

                if (searchResults.items && searchResults.items.length > 0) {
                    tracks.push(searchResults.items[0]);
                } else {
                    missingTracks.push({ title: info.title, artist: info.artist, album: '' });
                }
            } catch {
                missingTracks.push({ title: info.title, artist: info.artist, album: '' });
            }
        }
    }

    return { tracks, missingTracks };
}

/**
 * Formats duration in MM:SS format
 * @param {number} seconds - Duration in seconds
 * @returns {string} Formatted duration
 */
function formatDuration(seconds) {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, '0')}`;
}

/**
 * Helper function to escape XML special characters
 */
function escapeXml(text) {
    if (!text) return '';
    return text
        .toString()
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
}

// Export all functions
export { getTrackArtists };
