import { config } from './config.js';
import { state } from './state.js';
import { fetchWithTimeout } from './utils.js';
import { getFromDB } from './db.js';
import { _ } from './utils.js';

export async function fetchTMDB(endpoint, params = {}, signal) {
    const langMap = {
        'es': 'es-ES',
        'en': 'en-US',
        'fr': 'fr-FR',
        'de': 'de-DE',
        'it': 'it-IT',
        'pt': 'pt-BR'
    };
    const tmdbLang = langMap[state.settings.language] || 'en-US';

    const [path, existingQuery] = endpoint.split('?');
    const finalParams = new URLSearchParams(existingQuery);

    finalParams.set('api_key', state.settings.apiKey);
    finalParams.set('language', tmdbLang);

    for (const [key, value] of Object.entries(params)) {
        if (value) {
            finalParams.set(key, value);
        }
    }

    if (state.settings.watchRegion && !finalParams.has('watch_region')) {
        finalParams.set('watch_region', state.settings.watchRegion);
    }
    
    const url = `https://api.themoviedb.org/3/${path}?${finalParams.toString()}`;

    const response = await fetch(url, { signal });
    if (!response.ok) {
        const errorData = await response.json().catch(() => ({ status_message: "Unknown error" }));
        throw new Error(`HTTP error! status: ${response.status} - ${errorData.status_message}`);
    }
    return response.json();
}

export async function fetchPlexSessions(server) {
    const { protocolo, ip, puerto, token } = server;
    const url = `${protocolo}://${ip}:${puerto}/status/sessions?X-Plex-Token=${token}`;
    const response = await fetchWithTimeout(url, { headers: { 'Accept': 'application/json' } }, 8000);
    if (!response.ok) {
        throw new Error(`Error ${response.status}`);
    }
    const data = await response.json();
    return data.MediaContainer.Metadata || [];
}

export async function getMusicUrlsFromPlex(token, protocolo, ip, puerto, artistaId) {
    const url = `${protocolo}://${ip}:${puerto}/library/metadata/${artistaId}/allLeaves?X-Plex-Token=${token}`;
    try {
        const response = await fetchWithTimeout(url, {}, 15000);
        if (!response.ok) throw new Error(`Failed to fetch tracks: ${response.status}`);

        const data = await response.text();
        const parser = new DOMParser();
        const xmlDoc = parser.parseFromString(data, "text/xml");
        if (xmlDoc.querySelector('parsererror')) throw new Error("Failed to parse track XML");

        const tracks = Array.from(xmlDoc.querySelectorAll("Track")).map(track => {
            const part = track.querySelector("Part");
            if (!part || !part.getAttribute("key")) return null;

            const fileKey = part.getAttribute("key");
            const fileUrl = `${protocolo}://${ip}:${puerto}${fileKey}?X-Plex-Token=${token}`;

            const thumb = track.getAttribute("thumb");
            const parentThumb = track.getAttribute("parentThumb");
            const grandparentThumb = track.getAttribute("grandparentThumb");

            let coverUrl = 'img/no-poster.png';
            if (thumb) {
                coverUrl = `${protocolo}://${ip}:${puerto}${thumb}?X-Plex-Token=${token}`;
            } else if (parentThumb) {
                coverUrl = `${protocolo}://${ip}:${puerto}${parentThumb}?X-Plex-Token=${token}`;
            } else if (grandparentThumb) {
                coverUrl = `${protocolo}://${ip}:${puerto}${grandparentThumb}?X-Plex-Token=${token}`;
            }

            return {
                url: fileUrl,
                titulo: track.getAttribute("title") || 'Pista desconocida',
                album: track.getAttribute("parentTitle") || 'Álbum desconocido',
                artista: track.getAttribute("grandparentTitle") || 'Artista desconocido',
                cover: coverUrl,
                extension: part.getAttribute("container") || "mp3",
                id: track.getAttribute("ratingKey"),
                artistId: track.getAttribute("grandparentRatingKey") || artistaId,
                year: track.getAttribute("parentYear") || track.getAttribute("year"),
                genre: Array.from(track.querySelectorAll("Genre")).map(g => g.getAttribute('tag')).join(', ') || '',
                index: parseInt(track.getAttribute("index") || 0, 10),
                albumIndex: parseInt(track.getAttribute("parentIndex") || 0, 10)
            };
        }).filter(track => track !== null);

        tracks.sort((a, b) => {
            if (a.albumIndex !== b.albumIndex) {
                return a.albumIndex - b.albumIndex;
            }
            return a.index - b.index;
        });

        return tracks;

    } catch (error) {
        console.error("Error in getMusicUrlsFromPlex:", error);
        throw error;
    }
}

export async function fetchAllStreamsFromPlex(busqueda, tipoContenido) {
    if (!busqueda || !tipoContenido) return { success: false, streams: [], message: _('invalidStreamInfo') };
    if (!state.db) return { success: false, streams: [], message: _('dbUnavailableForStreams') };

    const plexSearchType = tipoContenido === 'movie' ? '1' : '2';
    const servers = await getFromDB('conexiones_locales');
    if (!servers || servers.length === 0) return { success: false, streams: [], message: _('noPlexServersForStreams') };

    const searchTasks = servers.map(async (server) => {
        const { ip, puerto, token, protocolo = 'http', nombre: serverName = 'Servidor Desconocido' } = server;
        if (!ip || !puerto || !token) return [];

        const searchUrl = `${protocolo}://${ip}:${puerto}/search?type=${plexSearchType}&query=${encodeURIComponent(busqueda)}&X-Plex-Token=${token}`;
        let serverStreams = [];

        try {
            const response = await fetchWithTimeout(searchUrl, { headers: { 'Accept': 'application/xml' } });
            if (!response.ok) return [];

            const data = await response.text();
            const parser = new DOMParser();
            const xml = parser.parseFromString(data, "text/xml");
            if (xml.querySelector('parsererror')) return [];

            if (tipoContenido === 'movie') {
                const videos = Array.from(xml.querySelectorAll("Video"));
                let videosToProcess = videos;
                const exactMatch = videos.find(v => v.getAttribute('title')?.toLowerCase() === busqueda.toLowerCase());
                if (exactMatch) {
                    videosToProcess = [exactMatch];
                }

                videosToProcess.forEach(video => {
                    const part = video.querySelector("Part");
                    if (part && part.getAttribute("key")) {
                        const movieTitle = video.getAttribute("title") || busqueda;
                        const movieYear = video.getAttribute("year");
                        const streamUrl = `${protocolo}://${ip}:${puerto}${part.getAttribute("key")}?X-Plex-Token=${token}`;
                        const extinfName = `${movieTitle}${movieYear ? ` (${movieYear})` : ''}`;
                        const logoUrl = video.getAttribute("thumb") ? `${protocolo}://${ip}:${puerto}${video.getAttribute("thumb")}?X-Plex-Token=${token}` : '';
                        const groupTitle = extinfName.replace(/"/g, "'");
                        serverStreams.push({
                            url: streamUrl,
                            title: extinfName,
                            extinf: `#EXTINF:-1 tvg-name="${extinfName.replace(/"/g, "'")}" tvg-logo="${logoUrl}" group-title="${groupTitle}",${extinfName}`
                        });
                    }
                });
            } else {
                const directories = Array.from(xml.querySelectorAll('Directory[type="show"]'));
                let directoryToProcess = directories.find(d => d.getAttribute('title')?.toLowerCase() === busqueda.toLowerCase());
                if (!directoryToProcess && directories.length > 0) {
                    directoryToProcess = directories[0];
                }

                if (directoryToProcess && directoryToProcess.getAttribute("ratingKey")) {
                    const serieKey = directoryToProcess.getAttribute("ratingKey");
                    const serieTitulo = directoryToProcess.getAttribute("title") || busqueda;
                    const serieYear = directoryToProcess.getAttribute("year");
                    const leavesUrl = `${protocolo}://${ip}:${puerto}/library/metadata/${serieKey}/allLeaves?X-Plex-Token=${token}`;

                    const leavesResponse = await fetchWithTimeout(leavesUrl, { headers: { 'Accept': 'application/xml' } });
                    if (leavesResponse.ok) {
                        const leavesData = await leavesResponse.text();
                        const leavesXml = parser.parseFromString(leavesData, "text/xml");
                        if (!leavesXml.querySelector('parsererror')) {
                            const episodes = Array.from(leavesXml.querySelectorAll("Video"));

                            episodes.sort((a, b) => {
                                const seasonA = parseInt(a.getAttribute("parentIndex") || 0, 10);
                                const seasonB = parseInt(b.getAttribute("parentIndex") || 0, 10);
                                if (seasonA !== seasonB) return seasonA - seasonB;
                                const episodeA = parseInt(a.getAttribute("index") || 0, 10);
                                const episodeB = parseInt(b.getAttribute("index") || 0, 10);
                                return episodeA - episodeB;
                            });

                            episodes.forEach(episode => {
                                const part = episode.querySelector("Part");
                                if (part && part.getAttribute("key")) {
                                    const seasonNum = episode.getAttribute("parentIndex") || 'S';
                                    const episodeNum = episode.getAttribute("index") || 'E';
                                    const episodeTitle = episode.getAttribute("title") || 'Episodio';
                                    const streamUrl = `${protocolo}://${ip}:${puerto}${part.getAttribute("key")}?X-Plex-Token=${token}`;
                                    const groupTitle = `${serieTitulo}${serieYear ? ` (${serieYear})` : ''} - Temporada ${seasonNum}`.replace(/"/g, "'");
                                    const extinfName = `${serieTitulo} T${seasonNum}E${episodeNum} ${episodeTitle}`;
                                    const logoUrl = episode.getAttribute("grandparentThumb") || episode.getAttribute("parentThumb") || episode.getAttribute("thumb");
                                    const fullLogoUrl = logoUrl ? `${protocolo}://${ip}:${puerto}${logoUrl}?X-Plex-Token=${token}` : '';
                                    serverStreams.push({
                                        url: streamUrl,
                                        title: extinfName,
                                        extinf: `#EXTINF:-1 tvg-name="${extinfName.replace(/"/g, "'")}" tvg-logo="${fullLogoUrl}" group-title="${groupTitle}",${extinfName}`
                                    });
                                }
                            });
                        }
                    }
                }
            }
            return serverStreams;
        } catch (error) {
            console.warn(`Error buscando streams en ${serverName}:`, error.message);
            return [];
        }
    });

    const results = await Promise.allSettled(searchTasks);
    const allFoundStreams = results
        .filter(r => r.status === 'fulfilled' && Array.isArray(r.value))
        .flatMap(r => r.value);

    const uniqueStreams = [];
    const seenUrls = new Set();
    for (const stream of allFoundStreams) {
        if (!seenUrls.has(stream.url)) {
            uniqueStreams.push(stream);
            seenUrls.add(stream.url);
        }
    }

    if (tipoContenido === 'movie') {
        uniqueStreams.sort((a, b) => (a.title || '').localeCompare(b.title || ''));
    }

    if (uniqueStreams.length > 0) {
        return { success: true, streams: uniqueStreams };
    } else {
        return { success: false, streams: [], message: _('notFoundOnServers', busqueda) };
    }
}

export async function fetchAllStreamsFromJellyfin(busqueda, tipoContenido) {
    if (!busqueda || !tipoContenido) return { success: false, streams: [], message: _('invalidStreamInfo') };

    const { url, userId, apiKey } = state.jellyfinSettings;
    if (!url || !userId || !apiKey) return { success: false, streams: [], message: _('noJellyfinCredentials') };

    const jellyfinSearchType = tipoContenido === 'movie' ? 'Movie' : 'Series';
    const searchUrl = `${url}/Users/${userId}/Items?searchTerm=${encodeURIComponent(busqueda)}&IncludeItemTypes=${jellyfinSearchType}&Recursive=true`;

    try {
        const response = await fetch(searchUrl, { headers: { 'X-Emby-Token': apiKey } });
        if (!response.ok) throw new Error(`Error buscando en Jellyfin: ${response.status}`);
        const searchData = await response.json();

        if (!searchData.Items || searchData.Items.length === 0) {
            return { success: false, streams: [], message: _('notFoundOnJellyfin', busqueda) };
        }

        const item = searchData.Items.find(i => i.Name.toLowerCase() === busqueda.toLowerCase()) || searchData.Items[0];
        const itemId = item.Id;
        const itemName = item.Name;
        const itemYear = item.ProductionYear;
        const posterTag = item.ImageTags?.Primary;
        const posterUrl = posterTag ? `${url}/Items/${itemId}/Images/Primary?tag=${posterTag}` : '';

        let streams = [];

        if (item.Type === 'Movie') {
            const streamUrl = `${url}/Videos/${itemId}/stream?api_key=${apiKey}`;
            const extinfName = `${itemName}${itemYear ? ` (${itemYear})` : ''}`;
            const groupTitle = extinfName.replace(/"/g, "'");

            streams.push({
                url: streamUrl,
                title: extinfName,
                extinf: `#EXTINF:-1 tvg-name="${extinfName.replace(/"/g, "'")}" tvg-logo="${posterUrl}" group-title="${groupTitle}",${extinfName}`
            });
        } else if (item.Type === 'Series') {
            const episodesUrl = `${url}/Shows/${itemId}/Episodes?userId=${userId}`;
            const episodesResponse = await fetch(episodesUrl, { headers: { 'X-Emby-Token': apiKey } });
            if (!episodesResponse.ok) throw new Error(`Error obteniendo episodios: ${episodesResponse.status}`);
            const episodesData = await episodesResponse.json();

            const sortedEpisodes = episodesData.Items.sort((a, b) => {
                if (a.ParentIndexNumber !== b.ParentIndexNumber) return (a.ParentIndexNumber || 0) - (b.ParentIndexNumber || 0);
                return (a.IndexNumber || 0) - (b.IndexNumber || 0);
            });

            sortedEpisodes.forEach(ep => {
                const streamUrl = `${url}/Videos/${ep.Id}/stream?api_key=${apiKey}`;
                const seasonNum = ep.ParentIndexNumber || 'S';
                const episodeNum = ep.IndexNumber || 'E';
                const episodeTitle = ep.Name || 'Episodio';
                const groupTitle = `${itemName} - Temporada ${seasonNum}`.replace(/"/g, "'");
                const extinfName = `${itemName} T${seasonNum}E${episodeNum} ${episodeTitle}`;

                streams.push({
                    url: streamUrl,
                    title: extinfName,
                    extinf: `#EXTINF:-1 tvg-name="${extinfName.replace(/"/g, "'")}" tvg-logo="${posterUrl}" group-title="${groupTitle}",${extinfName}`
                });
            });
        }

        return { success: true, streams };

    } catch (error) {
        console.error("Error fetching streams from Jellyfin:", error);
        return { success: false, streams: [], message: error.message };
    }
}

export async function fetchAllAvailableStreams(title, type) {
    const plexPromise = fetchAllStreamsFromPlex(title, type);
    const jellyfinPromise = fetchAllStreamsFromJellyfin(title, type);

    const results = await Promise.allSettled([plexPromise, jellyfinPromise]);

    let allStreams = [];
    const errorMessages = [];

    results.forEach((result, index) => {
        const sourceName = index === 0 ? 'Plex' : 'Jellyfin';
        if (result.status === 'fulfilled' && result.value.success) {
            allStreams.push(...result.value.streams);
        } else if (result.status === 'fulfilled' && !result.value.success) {
            if (result.value.message !== _('noPlexServersForStreams') && result.value.message !== _('noJellyfinCredentials')) {
                errorMessages.push(`${sourceName}: ${result.value.message}`);
            }
        } else if (result.status === 'rejected') {
            errorMessages.push(`${sourceName}: ${result.reason.message}`);
        }
    });

    const uniqueStreamsMap = new Map(allStreams.map(stream => [stream.url, stream]));
    const uniqueStreams = Array.from(uniqueStreamsMap.values());

    if (uniqueStreams.length > 0) {
        return { success: true, streams: uniqueStreams, message: `Found ${uniqueStreams.length} streams.` };
    } else {
        return { success: false, streams: [], message: errorMessages.join('; ') || _('notFoundOnAnyServer', title) };
    }
}