const {Search, Chapter, Manga,SearchByTag, isValidID, Modules, Baker, Crypto, cookieParser} = require('../lib.js')
const mongoose = require('mongoose')
const FavoriteModel = require('../models/favorite.js');
const ChapterModel = require('../models/readChapter.js');
const fs = require('fs');
const path = require('path');
const { isNullOrUndefined } = require('util');

exports.home = async (req,res)=>
{
    const cookieStr = req.headers.cookie;
    if(!cookieStr) return res.render('home.ejs',{data:null});
    const cookie  = cookieParser(cookieStr, 'hash');
    res.render('home.ejs', {data:cookie});
}
exports.searchBar = (req, res)=>
{
    res.render('searchBar.ejs');
}
exports.search = async (req, res)=>
{
    if(!req.body?.searchstring) return res.sendStatus(404)
    var data = await new Search(req.body.searchstring).search();
    if(!Array.isArray(data)) return res.sendStatus(404);
    const favorites = await FavoriteModel.find()
    const favoriteScanlators = favorites.map(favorite => favorite.scanlator);
    data.forEach((item, index) => 
    {
        if (favoriteScanlators.includes(item.scanlator)) 
        {
            item.Results.find(result => 
            {
                const matchingFavorite = favorites.find(favorite => 
                {
                    return result.title === favorite.title && item.scanlator === favorite.scanlator;
                });
                if (matchingFavorite) 
                {
                    result.favorite = matchingFavorite._id;
                    return true;
                }
                return false;
            });
        }
    });
    data = data.filter(item => item.Results.length > 0)
    data.sort((a,b)=>
    {
        const scanlatorA = a.scanlator.toLowerCase();
        const scanlatorB = b.scanlator.toLowerCase();
        return scanlatorA.localeCompare(scanlatorB);
    })
    res.render('searchResults.ejs', {data})
}


exports.bookmark = (worker)=>
{
    return async (req, res)=>
    {
        if (!req.params || typeof req.params !== 'object' || !('scanlator' in req.params) || !('title' in req.params)) return res.sendStatus(404);
        const {scanlator, title, idorLink} = req.params;  
        const {cookie} = req.headers;
        if(!cookie) return res.sendStatus(404);
        const config = require('../config.json');
        if(cookieParser(cookie, 'hash')!=config.hash) return res.sendStatus(404);
        if(isValidID(idorLink)) 
        {
            worker.send({ action:'remove', idorLink})
            return await FavoriteModel.findOneAndDelete({_id:idorLink}).then((deleted)=>
            {
                return res.render('bookmark.ejs', {scanlator, title, link:encodeURIComponent(deleted.link)})
            })
        }
        
        const scanlatorExists = await new Modules(scanlator).exists();
        if(!scanlatorExists) return res.sendStatus(404);
        const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
        if(!scanlatorHasTitle) return res.sendStatus(404);

        const fav = new FavoriteModel()
        fav.scanlator = scanlator;
        fav.title = title;
        fav.link = idorLink;
        await fav.save();   

        worker.send({action:'add', id:fav._id})
        res.render('bookmarked.ejs', {scanlator, title, link:idorLink, id:fav._id});
        let manga;
        try {
            manga = await new Manga(scanlator, idorLink, title).get();
        } catch (error) {
            return FavoriteModel.findByIdAndDelete(fav._id)
        }
        const {tags} = manga;
        FavoriteModel.findByIdAndUpdate(fav._id,{tags}, { new: true})
            .then((doc, err) => 
            {
                if (err) console.error('test',err); 
            });
    }
}

exports.manga = async (req, res)=>
{
    const {scanlator, link, title} = req.params;
    const scanlatorExists = await new Modules(scanlator).exists();
    if(!scanlatorExists) return res.sendStatus(404);
    const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
    if(!scanlatorHasTitle) return res.sendStatus(404);
    let manga; 
    try 
    {
        manga = await new Manga(scanlator,link, title).get()    
    }
    catch (error) 
    {
        res.sendStatus(404);
    }
    res.render('mangaPage.ejs', {data:{...manga, scanlator}});

}
exports.chapter = async (req, res)=>
{
    const {scanlator, title, chapter, link} = req.params;
    //IF its already there even with wrong things its there just show it
    const fileExists = fs.existsSync(`./public/Saved/${scanlator}/${title}/CH_${chapter}`);
    if(fileExists) 
    {
        const imgs =fs.readdirSync(`./public/Saved/${scanlator}/${title}/CH_${chapter}`);
        const latestChap = fs.readdirSync(`./public/Saved/${scanlator}/${title}`).length
        const List = imgs.map(filename => {return `./Saved/${scanlator}/${title}/CH_${chapter}/${filename}`});
        const mangaLink = await new Chapter(scanlator, link, title, chapter).getMangaLink();
        return res.render('display.ejs', {data:{title,latestChap,scanlator, chapterNum:parseInt(chapter), mangaLink, List}});
    }
    //If it isn't make sure there is no bad params being passed
    const scanlatorExists = await new Modules(scanlator).exists();
    if(!scanlatorExists) return res.sendStatus(404);
    const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
    if(!scanlatorHasTitle) return res.sendStatus(404);
    let manga;
    try 
    {
        manga = await new Chapter(scanlator, link, title,chapter).get();
    } 
    catch (error) 
    {
        return res.sendStatus(404);
    }
    res.render('display.ejs', {data:{...manga, scanlator, chapterNum:parseInt(chapter)}});
    
}
exports.favorites = async (req, res)=>
{
    const favs = await FavoriteModel.find();
    var mangas = [];
    if(favs.length == 0) return res.render('favorites.ejs', {isEmpty:true, mangas}) 
    await Promise.all(Object.keys(favs).map(async (key) => 
    {
        const manga = favs[key];
        var aux = await new Manga(manga.scanlator, manga.link, manga.title).get();
        if(aux)
        {
            aux.favorite = manga._id;
            mangas.push(aux);
        } 
    }));
    function getUniqueScanlators(items) 
    {
        const scanlators = {};
        items.forEach(item => {
          scanlators[item.scanlator] = true;
        });
        return Object.keys(scanlators);
    }
    const uniqueScanlators = getUniqueScanlators(mangas);
    const scanlatorsArray = uniqueScanlators.map(scanlator => 
    {
        var scanlatorItems = mangas.filter(manga => manga.scanlator === scanlator);
        if(!scanlatorItems) return res.sendStatus(500);
        scanlatorItems.sort((a, b) => 
        {
            if (a.title.toUpperCase() <  b.title.toUpperCase()) return -1;
            else return 1;
        });
        return {
            scanlator,
            Results: scanlatorItems
        };
    }); 
    scanlatorsArray.sort((a,b)=>
    {
        const scanlatorA = a.scanlator.toLowerCase();
        const scanlatorB = b.scanlator.toLowerCase();
        return scanlatorA.localeCompare(scanlatorB);
    })
    res.render('favorites.ejs', {isEmpty:false, mangas:scanlatorsArray})
} 
exports.recommended = async (req,res)=>
{
    const favorites = await FavoriteModel.find();
    const favoriteScanlators = favorites.map(favorite => favorite.scanlator);
    let favTags = [];
    for(var i = 0; i<favorites.length; i++)
    {
        favTags.push(...favorites[i].tags)
    }
    const counted = favTags.reduce((acc, curr)=>
    {
        acc[curr] = (acc[curr]||0)+1;
        return acc;
    }, {});
    const mostCommonTags = [...new Set(favTags)].sort((a,b)=>
    {
        if(counted[b] !== counted[a])
        {
            return counted[b]-counted[a];
        }
        else
        {
            return favTags.indexOf(a)-favTags.indexOf(b);
        }
    })
    const firstTwoTags =favoriteScanlators.length <=0 ? ['action', 'shounen'] : mostCommonTags.slice(0,2);
    let recommended = [];
    for(var i =0; i<2; i++)
    {
        const results = await new SearchByTag(firstTwoTags[i]).search();
        recommended.push(results);
    }
    const groupedByScanlator = recommended.reduce((acc, currentArray) => 
    {
        currentArray.forEach(({ Results, scanlator }) => 
        {
            if (!acc[scanlator]) 
            {
                acc[scanlator] = { Results: [], scanlator };
            }
            const uniqueResults = Results.filter(result => 
                !acc[scanlator].Results.some(existingResult => existingResult.title === result.title)
            );
            acc[scanlator].Results = acc[scanlator].Results.concat(uniqueResults);
        });
        return acc;
    }, {});     
    recommended = Object.values(groupedByScanlator);

    recommended.forEach((item, index) => 
    {
        if (favoriteScanlators.includes(item.scanlator)) 
        {
            item.Results.find(result => 
            {
                const matchingFavorite = favorites.find(favorite => 
                {
                    return result.title === favorite.title && item.scanlator === favorite.scanlator;
                });
                if (matchingFavorite) 
                {
                    result.favorite = matchingFavorite._id;
                    return true;
                }
                return false;
            });
        }
    });
    recommended.sort((a,b)=>
    {
        const scanlatorA = a.scanlator.toLowerCase();
        const scanlatorB = b.scanlator.toLowerCase();
        return scanlatorA.localeCompare(scanlatorB);
    })
    res.render('searchResults.ejs', {data:recommended});
}
exports.chapterNavInfo = async (req, res)=>
{
    const {scanlator, title, mangaLink} = req.params;
    const scanlatorExists = await new Modules(scanlator).exists();
    if(!scanlatorExists) return res.sendStatus(404);
    const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
    if(!scanlatorHasTitle) return res.sendStatus(404);
    let chapter = parseInt(req.params.chapter);
    let manga;
    try
    {
        manga = await new Manga(scanlator, mangaLink, title).get();
    }
    catch (error)
    {
        return res.sendStatus(404);
    }
    const {latestChap, List} = manga;
    List.sort((a,b)=> {return a.num - b.num});
    let nextChapterLink = chapter>= latestChap? '' : List[chapter].link;
    let prevChapterLink = chapter<=0? '' : List[chapter-1].link;
    res.render('chapterNav.ejs', {data:{title,latestChap,scanlator, chapterNum:chapter,mangaLink, nextChapterLink, prevChapterLink}})
}
exports.hasConfig = (req, res, next) => 
{
    const exists = fs.existsSync('./config.json');
    if(!exists) return this.config(req,res)
    next();
}
exports.config = async (req, res)=>
{
    function getSubDirectories(dir) 
    {
        let protectedSubDirs = ['node_modules', '.git', 'controllers', 'models', 'Modules', 'css','images', 'js', 'routes', 'views'];   
        const map = new Map();
        const dirs = fs.readdirSync(dir)
                    .filter(file => fs.statSync(path.join(dir, file)).isDirectory())
                    .filter(file => !protectedSubDirs.includes(file))
                    .filter(file=> !file.includes('scans'));
        for(let i =0; i<dirs.length; i++)
        {
            map.set(dirs[i], getSubDirectories(path.join(dir, dirs[i])));
        }
        return map.size==0?'':map;
    }
    function  getPaths(map, prefix='.')
    {
        const paths = [];
        for (const [key, value] of map.entries())
        {
            const fullPath = `${prefix}/${key}`;
            if (value instanceof Map) 
            {
                paths.push(...getPaths(value, fullPath));
            }
            else 
            {
                paths.push(fullPath);
            }
        }
        return paths;
    }

    const subDirs = await getSubDirectories('./');
    const data = getPaths(subDirs)

    res.render('config.ejs', {data});
}
exports.configPost = async (req, res)=>
{
    const {username, password, password2, saveFolder} = req.body;
    const {body} = req;
    if(!username || !password || !password2) return res.sendStatus(404);
    if(username.length < 4) return res.sendStatus(404);
    if(password !== password2) return res.sendStatus(404);
    if(!checkValidPath(saveFolder)) return res.sendStatus(404);
    if(!saveFolder) body.saveFolder = './public/Saved'
    body['hash'] = new Crypto(username, password).Hash;
    await fs.writeFileSync('./config.json', JSON.stringify(body));
    res.redirect('/');
}
/**
 * @param {String} dir 
 * @returns {Boolean}
 */
function checkValidPath(dir)
{
    if(!dir) return true; //by pass when there is no savePath (use default);
    dir = dir;
    return !path.isAbsolute(dir)
}
exports.loginPage = async (req, res)=>
{
    res.render('login.ejs');
}
exports.login = async (req, res)=>
{
    const {username, password} = req.body;
    if(!username || !password ) return res.sendStatus(404);
    const config = require('../config.json');
    if(username != config.username || password != config.password) return res.sendStatus(404);
    await Baker(res, 'hash', config.hash, 24)
    //TODO: change this to dashboard
    res.redirect('/');
}
exports.logout = async (req, res)=>
{
    const config = require('../config.json');
    const cookieStr = req.headers.cookie;
    if(!cookieStr) return res.redirect('/');
    const hash  = cookieParser(cookieStr, 'hash');
    if(hash != config.hash) return res.redirect('/');
    await Baker(res, 'hash', '')
    res.render('dashboard.ejs');
}

exports.errorPage = (req, res)=>
{
    res.render('error.ejs')
}

exports.chapterRead = async (req, res)=>
{
    const {scanlator, title, chapter, link} = req.params;
    const scanlatorExists = await new Modules(scanlator).exists();
    if(!scanlatorExists) return res.sendStatus(404);
    const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
    if(!scanlatorHasTitle) return res.sendStatus(404);
    const manga = await new Manga(scanlator, link, title).get();
    const {latestChap} = manga;
    if(chapter > latestChap || chapter < 0) return res.sendStatus(404);
    const chap = new ChapterModel();
    let chapterNum = chapter;
    const  chaps = await ChapterModel.find({scanlator, title, link, chapterNum});
    if(chaps.length>0) return res.sendStatus(404);
    chap.scanlator = scanlator;
    chap.title = title;
    chap.link = link;
    chap.chapterNum = chapter;
    chap.completely = true;
    chap.save()
    res.sendStatus(200);
    //Send to db;
}
exports.dashboard = async (req, res)=>
{
    let data = 0;
    res.render('dashboard.ejs', {data});
}