'use strict';

const Promise = require('bluebird');
const bhttp = require('bhttp');
const cheerio = require('cheerio');
const moment = require('moment');

async function fetchPhotos(photoPath) {
    const res = await bhttp.get(`https://21sextury.com${photoPath}`);

    return res.body.toString();
}

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

    const unlockedPhotos = $('.preview .imgLink.pgUnlocked')
        .map((photoIndex, photoElement) => $(photoElement).attr('href')).toArray();

    const lockedThumbnails = $('.preview .imgLink.lockedPicture img')
        .map((photoIndex, photoElement) => $(photoElement)
            .attr('src'))
        // .replace('_tb.jpg', '.jpg')) does not always work
        .toArray();

    return unlockedPhotos.concat(lockedThumbnails);
}

async function getPhotos(photoPath) {
    if (!photoPath || photoPath.match('join')) {
        return [];
    }

    const html = await fetchPhotos(photoPath);
    const $ = cheerio.load(html, { normalizeWhitespace: true });
    const photos = scrapePhotos(html);
    const pages = $('.paginatorPages a').map((pageIndex, pageElement) => $(pageElement).attr('href')).toArray();

    const otherPhotos = await Promise.map(pages, async (pagePath) => {
        const pageHtml = await fetchPhotos(pagePath);

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

    return photos.concat(otherPhotos.flat());
}

function scrape(html, site) {
    const $ = cheerio.load(html, { normalizeWhitespace: true });
    const scenesElements = $('li[data-itemtype=scene]').toArray();

    return scenesElements.reduce((accReleases, element) => {
        const siteName = $(element).find('.studioName a').attr('title');

        if (!site.url && siteName.toLowerCase() !== site.name.toLowerCase()) {
            // using generic overview as fallback, scene from different site
            return accReleases;
        }

        const sceneLinkElement = $(element).find('.sceneTitle a');
        const url = `${site.url || 'https://www.21sextury.com'}${sceneLinkElement.attr('href')}`;
        const title = sceneLinkElement.attr('title').trim();

        const entryId = $(element).attr('data-itemid');

        const date = moment
            .utc($(element).find('.sceneDate').text(), 'MM-DD-YYYY')
            .toDate();

        const actors = $(element).find('.sceneActors a')
            .map((actorIndex, actorElement) => $(actorElement).attr('title'))
            .toArray();

        const poster = $(element).find('.imgLink img').attr('data-original');
        const trailer = `https://videothumb.gammacdn.com/307x224/${entryId}.mp4`;

        const [likes, dislikes] = $(element).find('.value')
            .toArray()
            .map(value => Number($(value).text()));

        return [
            ...accReleases,
            {
                url,
                entryId,
                title,
                actors,
                date,
                poster,
                trailer: {
                    src: trailer,
                },
                rating: {
                    likes,
                    dislikes,
                },
                site,
            },
        ];
    }, []);
}

async function scrapeScene(html, url, site) {
    const $ = cheerio.load(html, { normalizeWhitespace: true });
    const sceneElement = $('#videoWrapper');
    const json = $('script[type="application/ld+json"]').html();
    const videoJson = $('script:contains("ScenePlayerOptions")').html();
    const videoDataString = videoJson.slice(videoJson.indexOf('= {') + 2, videoJson.indexOf('};') + 1);

    const data = JSON.parse(json)[0];
    const videoData = JSON.parse(videoDataString);
    const entryId = new URL(url).pathname.split('/').slice(-1)[0];

    const title = videoData?.playerOptions?.sceneInfos?.sceneTitle || (data.isPartOf && data.isPartOf !== 'TBD' ? data.isPartOf.name : data.name);
    const dataDate = moment.utc(videoData?.playerOptions?.sceneInfos?.sceneReleaseDate, 'YYYY-MM-DD');

    const date = dataDate.isValid()
        ? dataDate.toDate()
        : moment.utc(sceneElement.find('.updatedDate').text().trim(), 'MM-DD-YYYY').toDate();

    const actors = data.actor.map(actor => actor.name);

    const description = data.description || null; // prevent empty string
    const likes = Number(sceneElement.find('.rating .state_1 .value').text());
    const dislikes = Number(sceneElement.find('#infoWrapper .rating .state_2 .value').text());

    const duration = moment.duration(data.duration.slice(2).split(':')).asSeconds();

    const poster = videoData.picPreview;
    const trailer = `${videoData.playerOptions.host}${videoData.url}`;

    const photoPath = $('.picturesItem a').attr('href');
    const photos = await getPhotos(photoPath, site);

    const tags = data.keywords.split(', ');
    const siteName = data.productionCompany ? data.productionCompany.name : $('#logoLink a').attr('title');
    const channel = siteName && siteName.replace(/\s+/g, '').toLowerCase();

    return {
        url,
        entryId,
        title,
        date,
        actors,
        description,
        duration,
        tags,
        poster,
        photos,
        trailer: {
            src: trailer,
        },
        rating: {
            likes,
            dislikes,
        },
        site,
        channel,
    };
}

async function fetchLatest(site, page = 1) {
    const url = `${site.url || 'https://21sextury.com'}/en/videos/All-Categories/0/All-Pornstars/0/latest/${page}`;
    const res = await bhttp.get(url);

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

async function fetchUpcoming(site) {
    const url = `${site.url || 'https://21sextury.com'}/en/videos/All-Categories/0/All-Pornstars/0/upcoming`;
    const res = await bhttp.get(url);

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

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

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

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