import express from 'express';
import { globalState, getActiveSources, checkSiteStatus, rebuildTrendingFromCache } from '../utils/state.js';
import { sourceRegistry } from '../core/registry.js';

import apiLimiter from '../utils/rateLimiter.js';
import authMiddleware, { requireAdmin } from '../utils/authMiddleware.js';
import { sendToJDownloader } from '../utils/jdownloader.js';
import { MediaType, SearchResult } from '../types/source.js';
import { enrichSearchResults } from '../utils/tmdbEnricher.js';
import { CONFIG } from '../utils/config.js';
import { getAllUsers, createUserWithGeneratedPassword, deleteUser, resetPassword, updateUserPreferences } from '../utils/userStore.js';

const router = express.Router();

// ========================= STATUS & CONFIG =========================

router.get('/api/status', apiLimiter, authMiddleware, (req, res) => {
    const labels: Record<string, string> = {};
    for (const source of sourceRegistry.getAllRegistered()) {
        labels[source.name] = source.displayName || source.name.toUpperCase();
    }
    res.json({
        isOffline: globalState.isSiteOffline,
        message: globalState.siteOfflineMessage,
        activeSources: globalState.activeSources,
        availableSources: sourceRegistry.getAvailableNames(),
        sourceLabels: labels
    });
});

router.get('/api/trending', apiLimiter, authMiddleware, (req, res) => {
    res.json({
        films: globalState.trendingFilms || [],
        series: globalState.trendingSeries || [],
        recent: globalState.recentItems || [],
        isOffline: globalState.isSiteOffline,
        message: globalState.siteOfflineMessage
    });
});

// Toggle sources
router.post('/api/set-sources', apiLimiter, authMiddleware, async (req, res) => {
    const { sources } = req.body;
    if (!Array.isArray(sources)) {
        return res.status(400).json({ error: "Format invalide, un tableau de sources est attendu." });
    }

    const validSources = sources.filter(s => sourceRegistry.has(s));
    
    // Comparer les ensembles pour voir si l'ensemble des sources actives a changé (indépendamment de l'ordre)
    const oldSet = new Set(globalState.activeSources);
    const newSet = new Set(validSources);
    const hasSetChanged = oldSet.size !== newSet.size || [...oldSet].some(s => !newSet.has(s));
    
    globalState.activeSources = validSources;
    console.log(`[Source] Sources actives mises à jour: ${validSources.map(s => s.toUpperCase()).join(', ')}`);

    // Sauvegarde persistante des sources
    import('../utils/settingsManager.js').then(({ saveSettings }) => {
        saveSettings({ activeSources: validSources });
    });

    if (hasSetChanged) {
        // Uniquement si l'ensemble a changé, lancer un scan réseau
        await checkSiteStatus();
    } else {
        // Si seul l'ordre a changé, reconstruire les tendances depuis le cache en mémoire
        await rebuildTrendingFromCache();
    }

    res.json({ success: true, activeSources: globalState.activeSources });
});

// ========================= RECHERCHE =========================

router.post('/api/search', apiLimiter, authMiddleware, async (req, res) => {
    const { 
        title, 
        mediaType: rawTypeInput, 
        type: typeInput,
        source: reqSource, 
        src: reqSrc, 
        sources: reqSources,
        mergeResults: reqMergeResults,
        mergeresult: reqMergeResult
    } = req.body;

    // Handle both naming conventions and stringified booleans
    const mergeResults = (reqMergeResults !== undefined ? reqMergeResults : reqMergeResult) !== false && 
                         (reqMergeResults !== 'false' && reqMergeResult !== 'false');

    const rawType = rawTypeInput || typeInput || 'film';
    const mediaType = rawType === 'film' ? 'movie' : (rawType === 'serie' ? 'series' : rawType);

    if (!title) return res.status(400).json({ error: "Titre manquant." });

    // Determine which sources to query
    let sources = getActiveSources();
    const filterSources = reqSources || reqSource || reqSrc;
    
    if (filterSources) {
        const targetNames = Array.isArray(filterSources) ? filterSources : [filterSources];
        sources = sources.filter(s => targetNames.includes(s.name));
    }

    if (sources.length === 0) return res.status(500).json({ error: "Aucune source active correspondant à la demande." });

    console.log(`\n--- Recherche [${sources.map(s => s.name.toUpperCase()).join(', ')}]: "${title}" (${mediaType}) ---`);

    try {
        const resultsPromises = sources.map(async (source) => {
            try {
                const results = await source.search(title, mediaType as MediaType);
                return { sourceName: source.name, results };
            } catch (e: any) {
                console.error(`Erreur recherche sur ${source.name}:`, e.message);
                return { sourceName: source.name, error: e.message };
            }
        });

        const allResultsRaw = await Promise.all(resultsPromises);
        
        if (mergeResults) {
            let allResults: SearchResult[] = [];
            const errors: string[] = [];
            
            allResultsRaw.forEach(item => {
                if (item.results) {
                    allResults = allResults.concat(item.results);
                } else if (item.error) {
                    errors.push(`${item.sourceName}: ${item.error}`);
                }
            });

            if (!allResults.length) {
                if (errors.length > 0) {
                    return res.status(500).json({ error: `Erreur(s): ${errors.join(', ')}` });
                }
                return res.status(404).json({ error: "Aucun résultat trouvé." });
            }
            
            allResults = await enrichSearchResults(allResults);
            
            res.json(allResults);
        } else {
            // Return grouped results
            const grouped: Record<string, SearchResult[] | { error: string }> = {};
            for (const item of allResultsRaw) {
                if (item.results) {
                    grouped[item.sourceName] = await enrichSearchResults(item.results);
                } else {
                    grouped[item.sourceName] = { error: item.error! };
                }
            }
            res.json(grouped);
        }
    } catch (error: any) {
        console.error("Erreur /search:", error.message);
        res.status(500).json({ error: `Erreur serveur: ${error.message}` });
    }
});

// ========================= SÉLECTION =========================

const handleSelectContent: express.RequestHandler = async (req, res) => {
    const { hrefPath, title, type, source } = req.body;
    if (!hrefPath || !title || !source) return res.status(400).json({ error: "Données manquantes." });

    const activeSource = sourceRegistry.get(source);
    if (!activeSource) return res.status(500).json({ error: `Source "${source}" introuvable ou inactive.` });

    console.log(`\n--- Sélection [${activeSource.name.toUpperCase()}]: "${title}" ---`);

    try {
        globalState.currentTitleName = title;
        globalState.currentIdentifier = hrefPath;
        globalState.currentSelectionSource = source; // On enregistre la source de cette sélection
        globalState.directUrlMap = {};

        const selection = await activeSource.getSelection(hrefPath, type);

        globalState.isSeries = selection.isSeries;
        globalState.currentLiens = selection.links;

        // Tri des liens selon l'ordre préféré
        const { CONFIG } = await import('../utils/config.js');
        const preferredHosters: string[] = CONFIG.PREFERRED_HOSTERS || [];

        selection.links.sort((a: any, b: any) => {
            const getIndex = (host: string) => {
                if (!host) return 999;
                const h = host.toLowerCase();
                const idx = preferredHosters.findIndex(pref => h.includes(pref.toLowerCase()) || pref.toLowerCase().includes(h));
                return idx === -1 ? 999 : idx;
            };
            return getIndex(a.host) - getIndex(b.host);
        });

        selection.links.forEach((link: any, i: number) => {
            const key = link.id != null ? String(link.id) : String(i);
            if (link.url) globalState.directUrlMap[key] = link.url;
        });

        res.json({
            clientOptions: selection.links,
            hasNextPage: false,
            seasons: selection.seasons
        });

    } catch (error: any) {
        console.error("Erreur sélection:", error.message);
        res.status(500).json({ error: `Erreur serveur: ${error.message}` });
    }
};

router.post('/api/select-movie', apiLimiter, authMiddleware, handleSelectContent);
router.post('/api/select-trending', apiLimiter, authMiddleware, handleSelectContent);

// ========================= GET LINK =========================

router.post('/api/get-link', apiLimiter, authMiddleware, async (req, res) => {
    if (req.body.chosenId == null) return res.status(400).json({ error: "ID manquant." });
    const chosenId = String(req.body.chosenId);
    const useJD = req.body.useJD !== false && req.body.useJD !== 'false';

    const { currentTitleName, isSeries, directUrlMap, currentSelectionSource } = globalState;
    const activeSource = currentSelectionSource ? sourceRegistry.get(currentSelectionSource) : null;

    console.log(`\n--- Get Link [${activeSource?.name.toUpperCase()}]: ID ${chosenId} pour "${currentTitleName}" (JD: ${useJD}) ---`);

    try {
        let finalLink: any = null;

        if (directUrlMap[chosenId]) {
            finalLink = directUrlMap[chosenId];
        } else if (activeSource?.resolveLink) {
            finalLink = await activeSource.resolveLink(chosenId);
        }

        if (!finalLink) throw new Error("Impossible de résoudre le lien.");

        if (typeof finalLink === 'object' && finalLink.captcha) {
            return res.json({ status: 'challenge', challenge: finalLink });
        }

        console.log(`🎉 Lien final: ${finalLink}`);

        if (useJD) {
            await sendToJDownloader(finalLink, currentTitleName || 'Unknown', isSeries);
            res.json({ status: 'succès', message: 'Lien envoyé à JDownloader !', link: finalLink });
        } else {
            res.json({ status: 'succès', message: 'Lien récupéré !', link: finalLink });
        }
    } catch (error: any) {
        console.error("Erreur /get-link:", error.message);
        res.status(500).json({ error: `Erreur serveur: ${error.message}` });
    }
});

// ========================= GET LINKS BATCH =========================

router.post('/api/get-links-batch', apiLimiter, authMiddleware, async (req, res) => {
    const { chosenIds, useJD } = req.body;
    if (!chosenIds || !Array.isArray(chosenIds)) return res.status(400).json({ error: "Tableau d'IDs manquant." });
    
    const { currentTitleName, isSeries, directUrlMap, currentSelectionSource } = globalState;
    const activeSource = currentSelectionSource ? sourceRegistry.get(currentSelectionSource) : null;

    console.log(`\n--- Get Links Batch [${activeSource?.name.toUpperCase()}]: ${chosenIds.length} liens pour "${currentTitleName}" (JD: ${useJD !== false}) ---`);

    try {
        const results: string[] = [];
        const errors: string[] = [];

        for (const chosenId of chosenIds) {
            try {
                let finalLink: any = null;
                if (directUrlMap[String(chosenId)]) {
                    finalLink = directUrlMap[String(chosenId)];
                } else if (activeSource?.resolveLink) {
                    finalLink = await activeSource.resolveLink(String(chosenId));
                }

                if (finalLink && typeof finalLink === 'object' && finalLink.captcha) {
                    return res.json({ status: 'challenge', challenge: finalLink });
                }

                if (finalLink) {
                    results.push(finalLink);
                    if (useJD !== false && useJD !== 'false') {
                        await sendToJDownloader(finalLink, currentTitleName || 'Unknown', isSeries);
                    }
                } else {
                    errors.push(`ID ${chosenId} introuvable.`);
                }
            } catch (err: any) {
                errors.push(`Erreur pour ID ${chosenId}: ${err.message}`);
            }
        }

        if (results.length === 0) {
            return res.status(500).json({ error: "Aucun lien n'a pu être résolu.", details: errors });
        }

        if (useJD !== false && useJD !== 'false') {
            res.json({ status: 'succès', message: `${results.length} lien(s) envoyé(s) à JDownloader !`, errors: errors.length > 0 ? errors : undefined });
        } else {
            res.json({ status: 'succès', message: `${results.length} lien(s) récupéré(s) !`, links: results, errors: errors.length > 0 ? errors : undefined });
        }
    } catch (error: any) {
        console.error("Erreur /get-links-batch:", error.message);
        res.status(500).json({ error: `Erreur serveur: ${error.message}` });
    }
});

// ========================= MOVIEX DECODE =========================

router.get('/api/movix-decode/:lienId', apiLimiter, authMiddleware, async (req, res) => {
    try {
        const { lienId } = req.params;
        const sourceHydracker = sourceRegistry.get('hydracker') as any;
        
        if (!sourceHydracker) {
            return res.status(400).json({ error: "Le plugin Hydracker n'est pas actif." });
        }

        const link = await sourceHydracker.resolveMovixLink(lienId);
        
        if (link) {
            res.json({ success: true, link });
        } else {
            res.status(404).json({ error: "Impossible de débrider ce lien via Movix." });
        }
    } catch (error: any) {
        res.status(500).json({ error: error.message });
    }
});

// ========================= SAISONS =========================

router.post('/api/select-season', apiLimiter, authMiddleware, async (req, res) => {
    const { seasonValue } = req.body;
    const activeSource = globalState.currentSelectionSource ? sourceRegistry.get(globalState.currentSelectionSource) : null;

    console.log(`\n--- Changement de Saison [${activeSource?.name.toUpperCase()}] (${seasonValue}) ---`);

    try {
        if (!activeSource) throw new Error("Aucune source active.");

        const selection = await activeSource.getSelection(
            globalState.currentIdentifier!,
            undefined,
            seasonValue
        );

        globalState.directUrlMap = {};
        selection.links.forEach((link: any, i: number) => {
            const key = link.id != null ? String(link.id) : String(i);
            if (link.url) globalState.directUrlMap[key] = link.url;
        });
        globalState.currentLiens = selection.links;

        res.json({ clientOptions: selection.links, hasNextPage: false });
    } catch (error: any) {
        console.error("Erreur /select-season:", error.message);
        res.status(500).json({ error: "Erreur lors du changement de saison." });
    }
});

// ========================= JD DOWNLOAD STATUS =========================

router.get('/api/download-status', apiLimiter, authMiddleware, async (req, res) => {
    const jdQuery = {
        params: [{ "running": true, "name": true, "bytesLoaded": true, "bytesTotal": true, "uuid": true, "packageUUID": true, "finished": true }],
        id: Date.now(), methodName: "queryLinks"
    };
    try {
        const response = await fetch(`http://${CONFIG.JD_HOST}:${CONFIG.JD_API_PORT}/downloadsV2/queryLinks`, {
            method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(jdQuery)
        });
        if (!response.ok) throw new Error(`API JD non-OK: ${response.status}`);
        const data = await response.json();
        let items: any[] = [];
        if (data && data.data) {
            items = data.data.map((item: any) => {
                let percent = 0;
                if (item.bytesTotal > 0) percent = (item.bytesLoaded / item.bytesTotal) * 100;
                if (item.bytesLoaded > 0 && item.bytesLoaded === item.bytesTotal) percent = 100;
                return { name: item.name, percent, uuid: item.uuid, packageUUID: item.packageUUID, finished: item.finished || percent >= 100 };
            });
        }
        res.json(items);
    } catch (error: any) {
        if (error.code === 'ECONNREFUSED' || error.code === 'UND_ERR_CONNECT_TIMEOUT') { res.json([]); }
        else { res.status(500).json({ error: "Erreur API JDownloader" }); }
    }
});

// Suppression d'un lien JDownloader
router.post('/api/jd/remove-link', apiLimiter, authMiddleware, async (req, res) => {
    const { linkIds } = req.body;
    if (!linkIds || !linkIds.length) return res.status(400).json({ error: 'linkIds requis.' });
    try {
        const response = await fetch(`http://${CONFIG.JD_HOST}:${CONFIG.JD_API_PORT}/downloadsV2/removeLinks`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ params: [linkIds, []] })
        });
        if (!response.ok) throw new Error(`API JD non-OK: ${response.status}`);
        console.log(`[JD] Suppression de ${linkIds.length} lien(s).`);
        res.json({ success: true, message: `${linkIds.length} lien(s) supprimé(s).` });
    } catch (error: any) {
        console.error('[JD] Erreur suppression:', error.message);
        res.status(500).json({ error: 'Erreur lors de la suppression JDownloader.' });
    }
});

// ========================= ADMIN — GESTION DES USERS =========================

/** GET /admin/users — Liste tous les utilisateurs (admin only) */
router.get('/api/admin/users', apiLimiter, authMiddleware, requireAdmin, (req, res) => {
    try {
        const users = getAllUsers();
        res.json(users);
    } catch (error: any) {
        console.error('[Admin] Erreur liste users:', error.message);
        res.status(500).json({ error: 'Erreur serveur.' });
    }
});

/** POST /admin/users — Créer un utilisateur (admin only) */
router.post('/api/admin/users', apiLimiter, authMiddleware, requireAdmin, (req, res) => {
    const { username, role } = req.body;

    if (!username || typeof username !== 'string') {
        return res.status(400).json({ error: "Nom d'utilisateur requis." });
    }

    const userRole = role === 'admin' ? 'admin' : 'user';

    try {
        const result = createUserWithGeneratedPassword(username.trim(), userRole);
        console.log(`[Admin] Utilisateur créé par ${(req.session as any).user?.username}: ${result.user.username} (${userRole})`);
        res.json({
            success: true,
            user: result.user,
            generatedPassword: result.clearPassword,
        });
    } catch (error: any) {
        console.error('[Admin] Erreur création user:', error.message);
        res.status(400).json({ error: error.message });
    }
});

/** DELETE /admin/users/:id — Supprimer un utilisateur (admin only) */
router.delete('/api/admin/users/:id', apiLimiter, authMiddleware, requireAdmin, (req, res) => {
    const id = req.params.id as string;
    const currentUser = (req.session as any).user;

    // Interdire l'auto-suppression
    if (currentUser && currentUser.id === id) {
        return res.status(400).json({ error: 'Impossible de supprimer votre propre compte.' });
    }

    try {
        deleteUser(id);
        console.log(`[Admin] Utilisateur supprimé par ${currentUser?.username}: ID ${id}`);
        res.json({ success: true });
    } catch (error: any) {
        console.error('[Admin] Erreur suppression user:', error.message);
        res.status(400).json({ error: error.message });
    }
});

/** POST /admin/users/:id/reset-password — Reset le mot de passe (admin only) */
router.post('/api/admin/users/:id/reset-password', apiLimiter, authMiddleware, requireAdmin, (req, res) => {
    const id = req.params.id as string;

    try {
        const result = resetPassword(id);
        console.log(`[Admin] Password reset par ${(req.session as any).user?.username} pour ID ${id}`);
        res.json({
            success: true,
            generatedPassword: result.clearPassword,
        });
    } catch (error: any) {
        console.error('[Admin] Erreur reset password:', error.message);
        res.status(400).json({ error: error.message });
    }
});

/** POST /admin/plugins/save — Enregistre la config dynamique des plugins */
router.post('/api/admin/plugins/save', apiLimiter, authMiddleware, requireAdmin, async (req, res) => {
    try {
        const { config } = req.body;
        if (!config || typeof config !== 'object') {
            return res.status(400).json({ error: 'Configuration invalide.' });
        }

        const { configManager } = await import('../utils/config.js');
        for (const [key, value] of Object.entries(config)) {
            if (typeof value === 'string') {
                const trimmed = value.trim();
                if (trimmed !== '') {
                    configManager.set(key, trimmed);
                } else {
                    configManager.delete(key);
                }
            }
        }
        configManager.save();

        const { sourceRegistry } = await import('../core/registry.js');
        await sourceRegistry.initialize();

        const { globalState } = await import('../utils/state.js');
        const availableSources = sourceRegistry.getAvailableNames();
        
        // Add any newly available sources to globalState.activeSources
        for (const s of availableSources) {
            if (!globalState.activeSources.includes(s)) {
                globalState.activeSources.push(s);
            }
        }
        // Remove any sources that are no longer available
        globalState.activeSources = globalState.activeSources.filter(s => availableSources.includes(s));

        const { clearTmdbCache } = await import('../utils/tmdbEnricher.js');
        clearTmdbCache();

        const { checkSiteStatus } = await import('../utils/state.js');
        await checkSiteStatus(); // Re-scrape all sites and update trending

        res.json({ success: true, message: 'Configuration enregistrée et plugins rechargés.', activeSources: availableSources });
    } catch (e: any) {
        console.error('[API] Erreur save plugins:', e.message);
        res.status(500).json({ error: "Erreur interne lors de l'enregistrement de la configuration." });
    }
});

/** POST /admin/plugins/test — Teste et recharge les plugins */
router.post('/api/admin/plugins/test', apiLimiter, authMiddleware, requireAdmin, async (req, res) => {
    try {
        const { sourceRegistry } = await import('../core/registry.js');
        await sourceRegistry.initialize();
        
        const { globalState } = await import('../utils/state.js');
        const active = sourceRegistry.getAvailableNames();
        
        for (const s of active) {
            if (!globalState.activeSources.includes(s)) globalState.activeSources.push(s);
        }
        globalState.activeSources = globalState.activeSources.filter(s => active.includes(s));

        res.json({ success: true, activeSources: active });
    } catch (e: any) {
        console.error('[API] Erreur test plugins:', e.message);
        res.status(500).json({ error: "Erreur interne lors du test des plugins." });
    }
});

/** POST /admin/hosters/save — Enregistre l'ordre des hébergeurs préférés */
router.post('/api/admin/hosters/save', apiLimiter, authMiddleware, requireAdmin, async (req, res) => {
    try {
        const { preferredHosters } = req.body;
        if (!Array.isArray(preferredHosters)) {
            return res.status(400).json({ error: 'Format invalide.' });
        }

        const { configManager } = await import('../utils/config.js');
        configManager.set('PREFERRED_HOSTERS', preferredHosters);
        configManager.save();

        res.json({ success: true, message: 'Ordre enregistré.' });
    } catch (e: any) {
        console.error('[API] Erreur save hosters:', e.message);
        res.status(500).json({ error: "Erreur interne lors de l'enregistrement de l'ordre." });
    }
});

// ============================================================
//  PREFERENCES
// ============================================================

router.post('/api/preferences', apiLimiter, authMiddleware, (req, res) => {
    const session = req.session as any;
    const { key, value } = req.body;

    if (!key || typeof key !== 'string') {
        return res.status(400).json({ error: "Clé de préférence manquante ou invalide." });
    }

    try {
        updateUserPreferences(session.user.id, { [key]: value });
        // Mettre à jour la session en mémoire
        if (!session.user.preferences) session.user.preferences = {};
        session.user.preferences[key] = value;

        res.json({ success: true });
    } catch (e: any) {
        console.error('[API] Erreur sauvegarde préférence:', e.message);
        res.status(500).json({ error: "Impossible de sauvegarder la préférence." });
    }
});


/** GET /admin/check-update — Vérifie les mises à jour depuis l'API publique */
router.get('/api/admin/check-update', apiLimiter, authMiddleware, requireAdmin, async (req, res) => {
    try {
        const { CONFIG } = await import('../utils/config.js');
        const updateUrl = 'https://agora.nolhantirer.space/api/public/version';
        
        // Pour éviter de crasher si l'URL n'existe pas, on met un timeout court.
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), 5000);
        
        try {
            const response = await fetch(updateUrl, { signal: controller.signal });
            clearTimeout(timeoutId);
            
            if (response.ok) {
                const data = await response.json();
                res.json({ success: true, currentVersion: req.app.locals.appVersion, latestVersion: data.version, notes: data.notes, updateInfo: data });
            } else {
                res.status(502).json({ error: 'Serveur de mise à jour injoignable (Erreur HTTP ' + response.status + ').' });
            }
        } catch (fetchErr) {
            clearTimeout(timeoutId);
            res.status(502).json({ error: 'Serveur de mise à jour injoignable ou hors ligne.' });
        }
    } catch (e: any) {
        console.error('[API] Erreur check update:', e.message);
        res.status(500).json({ error: "Erreur inattendue lors de la vérification de la mise à jour." });
    }
});

export default router;