'use strict';

const cache = require('./src/cache');
const twitter = require('./src/factory.twitter')(cache);
const auth = require('./src/factory.auth')(cache);
const notifications = require('./src/notifications');
const service = require('./src/factory.service')(cache, twitter, notifications);
const {http, getDateToNearestMinute, timezones} = require('./src/utils');
const twitterSignIn = require('twittersignin')({
    consumerKey: process.env.TWITTER_CONSUMER_KEY,
    consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
    accessToken: process.env.TWITTER_ACCESS_TOKEN,
    accessTokenSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
});

module.exports.handleAccountActivity = async (event, context) => {
    const body = JSON.parse(event.body);
    console.log(body);

    if (!body.tweet_create_events) {
        return http.success(`No new tweets`);
    }

    const screenName = process.env.TWITTER_SCREEN_NAME;
    const allMentions = body.tweet_create_events.filter(tweet => {
        // ignore retweets, but accept quotes
        if (tweet.retweeted_status && !tweet.is_quote_status) {
            return false;
        }

        // exclude manual retweets
        if (tweet.text.startsWith(`RT @${screenName}:`)) {
            return false;
        }

        // ignore tweets by myself
        if (tweet.user.screen_name === screenName) {
            return false;
        }

        return true;

    }).map(tweetObject => {
        return {
            id: tweetObject.id_str,
            created_at: tweetObject.created_at,
            text: tweetObject.full_text || tweetObject.text,
            referencing_tweet: tweetObject.in_reply_to_status_id_str,
            author: tweetObject.user.screen_name,
        };
    });

    try {

        if (allMentions.length) {
            // for failure/recovery purposes
            await cache.setAsync('lastTweetRetrieved', allMentions[allMentions.length - 1].id);
            let results = await Promise.all(allMentions.map(service.handleMention));
            await Promise.all(results.map(service.handleParsingResult));
        }
    } catch (err) {
        if (err instanceof (require("redis")).ReplyError) {
            console.log(`Redis error: ${err.command} ${err.args} ${err}`);
        }
        throw err;
    }

    return http.success(`Handled ${allMentions.length} tweets`);
};

module.exports.handleTwitterCrc = async (event, context) => {
    const crypto = require('crypto');
    const hmac = crypto.createHmac('sha256', process.env.TWITTER_CONSUMER_SECRET)
        .update(event.queryStringParameters.crc_token).digest('base64');
    const response = {
        statusCode: 200,
        headers: {
            'content-type': 'application/json',
        },
        body: JSON.stringify({response_token: 'sha256=' + hmac})
    };
    console.log("CRC handled");
    return response;
};

module.exports.remind = async (event, context) => {
    const tweet = event.data;
    await Promise.all([
        twitter.replyWithReminder(tweet),
        service.cleanup(event.ruleName),
    ]);

    return http.success(`Reminded for tweet: ${JSON.stringify(tweet)}`);
};

module.exports.checkForRemindersAndSend = async (event, context) => {
    let reminders = [];
    try {
        const key = getDateToNearestMinute().toISOString();
        reminders = await cache.lrangeAsync(key, 0, -1);
        reminders = reminders.map(JSON.parse);
        await Promise.all([
                ...reminders.map(service.remind),
            cache.delAsync(key)
        ]);
    } catch (err) {
        if (err instanceof (require("redis")).ReplyError) {
            console.log(`Redis error: ${err.command} ${err.args} ${err}`);
        }
        throw err;
    }

    return http.success(`Reminded for ${reminders.length} tweets`);
};

module.exports.retryFailedTasks = async (event, context) => {
    const failedTasks = await cache.lrangeAsync(event.queue || 'PARSE_TIME_FAILURE', 0, -1);

    if (!failedTasks.length) {
        return http.success(`No tasks for retrying in queue ${event.queue}`);
    }

    await cache.delAsync(event.queue);
    let results = await Promise.all(failedTasks.map(service.parseReminderTime));
    await Promise.all(results.map(service.handleParsingResult));

    return http.success(`Retried ${failedTasks.length} tasks from ${event.queue} queue`);
};

module.exports.getPage = async (event, context) => {
    switch (event.pathParameters.page) {
        case 'login': {
            if (await auth.session(event)) {
                return http.redirect('/settings');
            }

            return http.redirect('/_/starttwittersignin');
        }

        case 'firebase-messaging-sw.js':
            return http.sendTextFile('firebase-messaging-sw.js', {'content-type': 'text/javascript; charset=UTF-8'});

        case 'settings': {
            const session = await auth.session(event);

            if (!session) {
                return http.redirect('/_/starttwittersignin');
            }

            const username = session.username;
            const settings = await service.getUserSettings(username);

            return http.render('settings', {username, settings, timezones});
        }
        
        default:
            return { statusCode: 404 };
    }
};

module.exports.getHomePage = async (event, context) => {
    return http.renderHtml('home');
};

module.exports.updateSettings = async (event, context) => {
    console.log(event.body);
    const session = await auth.session(event);
    if (!session) {
        return http.redirect('/_/starttwittersignin');
    }

    const username = session.username;
    const body = require('querystring').decode(event.body);
    
    const settings = await service.getUserSettings(username);
    if (body.utcOffset) {
        settings.utcOffset = body.utcOffset;
    }
    if (body['notifications.enabled']) {
        settings.notifications.enabled = body['notifications.enabled'] === '0' ? false : true;
    }
    if (body['notifications.fbtoken']) {
        settings.notifications.fbtoken = body['notifications.fbtoken'];
    }

    await service.setUserSettings(username, settings);

    return http.redirect('/settings');
};

module.exports.startTwitterSignIn = async (event, context) => {
    const {
        oauth_token: requestToken,
        oauth_token_secret: requestTokenSecret,
        oauth_callback_confirmed
    } = await twitterSignIn.getRequestToken({
        x_auth_access_type: "read",
    });
    if (!oauth_callback_confirmed) {
        throw new Error('OAuth callback not confirmed!');
    }
    await cache.setAsync(`tokens-${requestToken}`, requestTokenSecret, 'EX', 5 * 60);
    return http.redirect('https://api.twitter.com/oauth/authenticate?oauth_token=' + requestToken);
};

module.exports.completeTwitterSignIn = async (event, context) => {
    const requestToken = event.queryStringParameters.oauth_token;
    const oauthVerifier = event.queryStringParameters.oauth_verifier;

    const requestTokenSecret = await cache.getAsync(`tokens-${requestToken}`);
    const {oauth_token, oauth_token_secret, screen_name} =
        await twitterSignIn.getAccessToken(requestToken, requestTokenSecret, oauthVerifier);

    const user = await twitterSignIn.getUser(oauth_token, oauth_token_secret, {
        include_entities: false,
        skip_status: true,
    });

    const sessionId = await auth.createSession(user);

    return http.redirect('/settings', `rmotid=${sessionId}`);
};
