'use strict';

// const Promise = require('bluebird');
const bhttp = require('bhttp');
const cheerio = require('cheerio');
const { JSDOM } = require('jsdom');
const moment = require('moment');

const logger = require('../logger');
const { heightToCm } = require('../utils/convert');

async function fetchPhotos(url) {
    const res = await bhttp.get(url);

    return res.body.toString();
}

function scrapePhotos(html, type) {
    const $ = cheerio.load(html, { normalizeWhitespace: true });

    const photos = $('.photo_gallery_thumbnail_wrapper .thumbs')
        .toArray()
        .map((photoElement) => {
            const src = $(photoElement).attr('src');

            // high res often available in alternative directories, but not always, provide original as fallback
            if (type === 'caps') {
                return [
                    src.replace('capthumbs/', 'caps/'),
                    src,
                ];
            }

            return [
                src.replace('thumbs/', 'photos/'),
                src.replace('thumbs/', '1600watermarked/'),
                src.replace('thumbs/', '1280watermarked/'),
                src.replace('thumbs/', '1024watermarked/'),
                src,
            ];
        });

    return photos;
}

async function getPhotosLegacy(entryId, site, type = 'highres', page = 1) {
    const albumUrl = `${site.url}/trial/gallery.php?id=${entryId}&type=${type}&page=${page}`;

    logger.warn(`Jules Jordan is using legacy photo scraper for ${albumUrl} (page ${page})`);

    const html = await fetchPhotos(albumUrl);
    const $ = cheerio.load(html, { normalizeWhitespace: true });

    // don't add first URL to pages to prevent unnecessary duplicate request
    const photos = scrapePhotos(html, type);
    const pages = Array.from(new Set($('.page_numbers a').toArray().map(el => $(el).attr('href'))));

    const otherPhotos = pages
        ? await Promise.map(pages, async (pageX) => {
            const pageUrl = `https://www.julesjordan.com/trial/${pageX}`;
            const pageHtml = await fetchPhotos(pageUrl);

            return scrapePhotos(pageHtml, type);
        }, {
            concurrency: 2,
        })
        : [];

    const allPhotos = photos.concat(otherPhotos.flat());

    if (allPhotos.length === 0 && type === 'highres') {
        // photos not available, try for screencaps instead
        return getPhotosLegacy(entryId, site, 'caps', 1);
    }

    return allPhotos;
}

async function getPhotos(entryId, site, type = 'highres', page = 1) {
    const albumUrl = `${site.url}/trial/gallery.php?id=${entryId}&type=${type}&page=${page}`;

    const res = await bhttp.get(albumUrl);
    const html = res.body.toString();

    const sourceLines = html.split(/\n/).filter(line => line.match(/ptx\["\w+"\]/));
    const sources = sourceLines.reduce((acc, sourceLine) => {
        const quality = sourceLine.match(/\["\w+"\]/)[0].slice(2, -2);
        const source = sourceLine.slice(sourceLine.indexOf('/trial'), sourceLine.indexOf('.jpg') + 4);

        if (!source) return acc;
        if (!acc[quality]) acc[quality] = [];

        acc[quality].push(`${site.url}${source}`);

        return acc;
    }, {});

    if (type === 'highres') {
        if (sources['1600'] && sources['1600'].length > 0) return sources['1600'];
        if (sources['1280'] && sources['1280'].length > 0) return sources['1280'];
        if (sources['1024'] && sources['1024'].length > 0) return sources['1024'];
        if (sources.Thumbs && sources.Thumbs.length > 0) return sources.Thumbs;

        // no photos available, try for screencaps instead
        return getPhotos(entryId, site, 'caps', 1);
    }

    if (sources.jpg && sources.jpg.length > 0) return sources.jpg;
    if (sources['Video Cap Thumbs'] && sources['Video Cap Thumbs'].length > 0) return sources['Video Cap Thumbs'];

    // no screencaps available either, try legacy scraper just in case
    return getPhotosLegacy(entryId, site, 'highres', 1);
}

function scrapeLatest(html, site) {
    const $ = cheerio.load(html, { normalizeWhitespace: true });
    const scenesElements = $('.update_details').toArray();

    return scenesElements.map((element) => {
        const release = {};

        const sceneLinkElement = $(element).find('a[title*=" "]');
        release.url = sceneLinkElement.attr('href');
        release.title = sceneLinkElement.text();

        release.entryId = $(element).attr('data-setid');

        release.date = moment
            .utc($(element).find('.update_date').text(), 'MM/DD/YYYY')
            .toDate();

        release.actors = $(element).find('.update_models a')
            .map((actorIndex, actorElement) => $(actorElement).text())
            .toArray();

        const photoElement = $(element).find('a img.thumbs');
        const photoCount = Number(photoElement.attr('cnt'));
        [release.poster, ...release.photos] = Array.from({ length: photoCount }, (value, index) => {
            const src = photoElement.attr(`src${index}_1x`) || photoElement.attr(`src${index}`);

            if (!src) return null;
            if (src.match(/^http/)) return src;

            return `${site.url}${src}`;
        }).filter(photoUrl => photoUrl);

        const teaserScript = $(element).find('script').html();
        if (teaserScript) {
            const src = teaserScript.slice(teaserScript.indexOf('http'), teaserScript.indexOf('.mp4') + 4);
            if (src) release.teaser = { src };
        }

        return release;
    });
}

function scrapeUpcoming(html, site) {
    const $ = cheerio.load(html, { normalizeWhitespace: true });
    const scenesElements = $('#coming_soon_carousel').find('.table').toArray();

    return scenesElements.map((element) => {
        const entryId = $(element).find('.upcoming_updates_thumb').attr('id').match(/\d+/)[0];

        const details = $(element).find('.update_details_comingsoon')
            .eq(1)
            .children()
            .remove();

        const title = details
            .end()
            .text()
            .trim();

        const actors = details
            .text()
            .trim()
            .split(', ');

        const date = moment
            .utc($(element).find('.update_date_comingsoon').text().slice(7), 'MM/DD/YYYY')
            .toDate();

        const photoElement = $(element).find('a img.thumbs');
        const posterPath = photoElement.attr('src');
        const poster = posterPath.match(/^http/) ? posterPath : `${site.url}${posterPath}`;

        const videoClass = $(element).find('.update_thumbnail div').attr('class');
        const videoScript = $(element).find(`script:contains(${videoClass})`).html();
        const teaser = videoScript.slice(videoScript.indexOf('https://'), videoScript.indexOf('.mp4') + 4);

        return {
            url: null,
            entryId,
            title,
            date,
            actors,
            poster,
            teaser: {
                src: teaser,
            },
            rating: null,
            site,
        };
    });
}

async function scrapeScene(html, url, site) {
    const $ = cheerio.load(html, { normalizeWhitespace: true });

    const release = { url, site };

    release.title = $('.title_bar_hilite').text().trim();

    const entryId = html.match(/showtagform\((\d+)\)/);

    if (entryId) release.entryId = entryId[1];
    else {
        const setIdIndex = html.indexOf('setid:"');
        if (setIdIndex) release.entryId = html.slice(setIdIndex, html.indexOf(',', setIdIndex)).match(/\d+/)[0];
    }

    const dateElement = $('.update_date').text().trim();
    const dateComment = $('*')
        .contents()
        .toArray()
        .find(({ type, data }) => type === 'comment' && data.match('Date OFF'));

    if (dateElement) {
        release.date = moment
            .utc($('.update_date').text(), 'MM/DD/YYYY')
            .toDate();
    }

    if (dateComment) {
        release.date = moment
            .utc(dateComment.nodeValue.match(/\d{2}\/\d{2}\/\d{4}/), 'MM/DD/YYYY')
            .toDate();
    }

    release.description = $('.update_description').text().trim();

    release.actors = $('.backgroundcolor_info > .update_models a, .item .update_models a')
        .map((_actorIndex, actorElement) => $(actorElement).text())
        .toArray();

    const infoLines = $('script:contains("useimage")')
        .html()
        .split('\n');

    const posterPath = infoLines.find(line => line.match('useimage')).replace('useimage = "', '').slice(0, -2);
    if (posterPath) release.poster = posterPath.match(/^http/) ? posterPath : `${site.url}${posterPath}`;

    const trailerLine = infoLines.find(line => line.match('movie["Trailer_720"]'));

    if (site.slug !== 'manuelferrara') {
        release.trailer = {
            src: trailerLine.slice(trailerLine.indexOf('path:"') + 6, trailerLine.indexOf('",movie')),
            quality: 720,
        };
    }

    release.photos = await getPhotos(release.entryId, site);

    release.tags = $('.update_tags a').map((tagIndex, tagElement) => $(tagElement).text()).toArray();
    release.movie = $('.update_dvds a').attr('href');

    release.stars = Number($('.avg_rating').text().trim().replace(/[\s|Avg Rating:]/g, ''));

    return release;
}

function scrapeMovie(html, url, site) {
    const { document } = new JSDOM(html).window;
    const movie = { url, site };

    movie.entryId = document.querySelector('.dvd_details_overview .rating_box').dataset.id;
    movie.title = document.querySelector('.title_bar span').textContent;
    movie.covers = Array.from(document.querySelectorAll('#dvd-cover-flip > a'), el => el.href);
    movie.channel = document.querySelector('.update_date a').textContent;
    movie.date = new Date();
    movie.releases = Array.from(document.querySelectorAll('.cell.dvd_info > a'), el => el.href);

    return movie;
}

function scrapeProfile(html, url, actorName) {
    const { document } = new JSDOM(html).window;

    const bio = document.querySelector('.model_bio').textContent;
    const avatarEl = document.querySelector('.model_bio_pic');

    const profile = {
        name: actorName,
    };

    const heightString = bio.match(/\d+ feet \d+ inches/);
    const ageString = bio.match(/Age:\s*\d{2}/);
    const measurementsString = bio.match(/\w+-\d+-\d+/);

    if (heightString) profile.height = heightToCm(heightString[0]);
    if (ageString) profile.age = Number(ageString[0].match(/\d{2}/)[0]);
    if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString[0].split('-');

    if (avatarEl) {
        const src = avatarEl.innerHTML.slice(avatarEl.innerHTML.indexOf('src') + 5, avatarEl.innerHTML.indexOf('set.jpg') + 7).trim();
        const src0 = avatarEl.innerHTML.slice(avatarEl.innerHTML.indexOf('src0') + 6, avatarEl.innerHTML.indexOf('set.jpg') + 7).trim();
        const src1 = avatarEl.innerHTML.slice(avatarEl.innerHTML.indexOf('src0_1x') + 9, avatarEl.innerHTML.indexOf('1x.jpg') + 6).trim();
        const src2 = avatarEl.innerHTML.slice(avatarEl.innerHTML.indexOf('src0_2x') + 9, avatarEl.innerHTML.indexOf('2x.jpg') + 6).trim();
        const src3 = avatarEl.innerHTML.slice(avatarEl.innerHTML.indexOf('src0_3x') + 9, avatarEl.innerHTML.indexOf('3x.jpg') + 6).trim();

        const avatar = src3 || src2 || src1 || src0 || src;

        if (avatar) profile.avatar = avatar;
    }

    profile.releases = Array.from(document.querySelectorAll('.category_listing_block .update_details > a:first-child'), el => el.href);

    return profile;
}

async function fetchLatest(site, page = 1) {
    const res = await bhttp.get(`${site.url}/trial/categories/movies_${page}_d.html`);

    return scrapeLatest(res.body.toString(), site);
}

async function fetchUpcoming(site) {
    const res = await bhttp.get(`${site.url}/trial/index.php`);

    return scrapeUpcoming(res.body.toString(), site);
}

async function fetchScene(url, site) {
    const res = await bhttp.get(url);

    return scrapeScene(res.body.toString(), url, site);
}

async function fetchMovie(url, site) {
    const res = await bhttp.get(url);

    return scrapeMovie(res.body.toString(), url, site);
}

async function fetchProfile(actorName) {
    const actorSlugA = actorName.toLowerCase().replace(/\s+/g, '-');
    const actorSlugB = actorName.toLowerCase().replace(/\s+/g, '');

    const urlA = `https://julesjordan.com/trial/models/${actorSlugA}.html`;
    const urlB = `https://julesjordan.com/trial/models/${actorSlugB}.html`;

    const resA = await bhttp.get(urlA);

    if (resA.statusCode === 200) {
        const profile = scrapeProfile(resA.body.toString(), urlA, actorName);

        return profile;
    }

    const resB = await bhttp.get(urlB);

    if (resB.statusCode === 200) {
        const profile = scrapeProfile(resB.body.toString(), urlB, actorName);

        return profile;
    }

    return null;
}

module.exports = {
    fetchLatest,
    fetchMovie,
    fetchProfile,
    fetchUpcoming,
    fetchScene,
};
