import route from 'koa-route';
import Purest from 'purest';
import models from 'db/models';
import findUser from 'db/utils/find_user';
import { esc, escAttrs } from 'db/models';

const facebook = new Purest({ provider: 'facebook' });
const reddit = new Purest({ provider: 'reddit' });

function logErrorAndRedirect(ctx, where, error) {
    const s = ctx.session;
    let msg = 'unknown';
    if (error.toString()) msg = error.toString();
    else
        msg =
            error.error && error.error.message
                ? error.error.message
                : error.msg || JSON.stringify(error);
    console.error(
        `oauth error [${where}|${s.user}|${s.uid}]|${
            ctx.req.headers['user-agent']
        }: ${msg}`
    );
    if (process.env.NODE_ENV === 'development') console.log(error.stack);
    ctx.flash = { alert: `${where} error: ${msg}` };
    ctx.redirect('/');
    return null;
}

function getRemoteIp(req) {
    const remote_address =
        req.headers['x-forwarded-for'] || req.connection.remoteAddress;
    const ip_match = remote_address
        ? remote_address.match(/(\d+\.\d+\.\d+\.\d+)/)
        : null;
    return ip_match ? ip_match[1] : esc(remote_address);
}

function retrieveFacebookUserData(access_token) {
    return new Promise((resolve, reject) => {
        facebook
            .query()
            .get('me?fields=name,email,location,picture{url},verified')
            .auth(access_token)
            .request((err, res) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(res.body);
                }
            });
    });
}

function* handleFacebookCallback() {
    console.log(
        '-- /handle_facebook_callback -->',
        this.session.uid,
        this.query
    );
    let email = null;
    try {
        if (this.query['error[error][message]']) {
            return logErrorAndRedirect(
                this,
                'facebook:1',
                this.query['error[error][message]']
            );
        }
        const u = yield retrieveFacebookUserData(this.query.access_token);
        email = u.email;
        const attrs = {
            uid: this.session.uid,
            name: u.name,
            email: u.email,
            first_name: u.first_name,
            last_name: u.last_name,
            birthday: u.birthday ? new Date(u.birthday) : null,
            gender: u.gender,
            picture_small: u.picture ? u.picture.data.url : null,
            location_id: u.location ? u.location.id : null,
            location_name: u.location ? u.location.name : null,
            locale: u.locale,
            timezone: u.timezone,
            remote_ip: getRemoteIp(this.request.req),
            verified: u.verified,
            waiting_list: false,
            facebook_id: u.id,
        };
        const i_attrs = {
            provider: 'facebook',
            uid: u.id,
            name: u.name,
            email: u.email,
            verified: u.verified,
            provider_user_id: u.id,
        };
        // const i_attrs_email = {
        //     provider: 'email',
        //     email: u.email,
        //     verified: false
        // };

        let user = yield findUser({ email: u.email, provider_user_id: u.id });
        console.log(
            '-- /handle_facebook_callback user id -->',
            this.session.uid,
            user ? user.id : 'not found'
        );

        let account_recovery_record = null;
        const provider = (this.session.prv = 'facebook');
        if (this.session.arec) {
            const arec = yield models.AccountRecoveryRequest.findOne({
                attributes: ['id', 'created_at', 'account_name', 'owner_key'],
                where: { id: this.session.arec },
            });
            if (arec) {
                const seconds_ago = (Date.now() - arec.created_at) / 1000;
                console.log(
                    '-- /handle_facebook_callback arec -->',
                    this.session.uid,
                    seconds_ago,
                    arec.created_at
                );
                if (seconds_ago < 600) account_recovery_record = arec;
            }
        }
        if (account_recovery_record) {
            if (user) {
                const existing_account = yield models.Account.findOne({
                    attributes: ['id'],
                    where: {
                        user_id: user.id,
                        name: account_recovery_record.account_name,
                    },
                    order: 'id DESC',
                });
                if (existing_account) {
                    console.log(
                        '-- arec: confirmed user for account -->',
                        this.session.uid,
                        provider,
                        account_recovery_record.id,
                        existing_account.name,
                        this.session.uid,
                        account_recovery_record.owner_key
                    );
                    account_recovery_record.update({
                        user_id: user.id,
                        status: 'confirmed',
                    });
                    this.redirect('/recover_account_step_2');
                } else {
                    console.log(
                        '-- arec: failed to confirm user for account (no account) -->',
                        this.session.uid,
                        provider,
                        account_recovery_record.id,
                        user.id,
                        this.session.uid,
                        account_recovery_record.owner_key
                    );
                    account_recovery_record.update({
                        user_id: user.id,
                        status: 'account not found',
                    });
                    this.body =
                        'We cannot verify the user account. Please contact support@steemit.com';
                }
            } else {
                console.log(
                    '-- arec: failed to confirm user for account (no user) -->',
                    this.session.uid,
                    provider,
                    this.session.uid,
                    this.session.email
                );
                account_recovery_record.update({ status: 'user not found' });
                this.body =
                    'We cannot verify the user account. Please contact support@steemit.com';
            }
            return null;
        }
        // no longer necessary since there is phone verification now
        // if (!u.email) {
        //     console.log('-- /handle_facebook_callback no email -->', this.session.uid, u);
        //     this.flash = {alert: 'Facebook login didn\'t provide any email addresses. Please make sure your Facebook account has a primary email address and try again.'};
        //     this.redirect('/');
        //     return;
        // }
        // if (!u.verified) {
        //     throw new Error('Not verified Facebook account. Please verify your Facebook account and try again to sign up to Steemit.');
        // }

        if (user) {
            attrs.id = user.id;
            yield models.User.update(attrs, { where: { id: user.id } });
            yield models.Identity.update(i_attrs, {
                where: { user_id: user.id, provider: 'facebook' },
            });
            console.log(
                '-- fb updated user -->',
                this.session.uid,
                user.id,
                u.name,
                u.email
            );
        } else {
            user = yield models.User.create(attrs);
            i_attrs.user_id = user.id;
            console.log('-- fb created user -->', user.id, u.name, u.email);
            const identity = yield models.Identity.create(i_attrs);
            console.log(
                '-- fb created identity -->',
                this.session.uid,
                identity.id
            );
            // if (i_attrs_email.email) {
            //     i_attrs_email.user_id = user.id
            //     const email_identity = yield models.Identity.create(i_attrs_email);
            //     console.log('-- fb created email identity -->', this.session.uid, email_identity.id);
            // }
        }
        this.session.user = user.id;
    } catch (error) {
        return logErrorAndRedirect(this, 'facebook:2', error);
    }
    this.flash = { success: 'Successfully authenticated with Facebook' };
    this.redirect('/enter_email' + (email ? `?email=${email}` : ''));
    return null;
}

function retrieveRedditUserData(access_token) {
    return new Promise((resolve, reject) => {
        reddit
            .query()
            .get('https://oauth.reddit.com/api/v1/me.json?raw_json=1')
            .headers({
                Authorization: `bearer ${access_token}`,
                'User-Agent': 'Steembot/1.0 (+http://steemit.com)',
                Accept: 'application/json',
                'Content-type': 'application/json',
            })
            .request((err, res) => {
                if (err) {
                    reject(err);
                } else {
                    delete res.body.features;
                    resolve(res.body);
                }
            });
    });
}

function* handleRedditCallback() {
    try {
        const u = yield retrieveRedditUserData(this.query.access_token);
        console.log('-- /handle_reddit_callback  -->', this.session.uid, u);
        let user = yield findUser({ provider_user_id: u.id });
        console.log(
            '-- /handle_reddit_callback user id -->',
            this.session.uid,
            user ? user.id : 'not found'
        );

        let account_recovery_record = null;
        const provider = (this.session.prv = 'reddit');
        if (this.session.arec) {
            const arec = yield models.AccountRecoveryRequest.findOne({
                attributes: ['id', 'created_at', 'account_name', 'owner_key'],
                where: { id: this.session.arec },
            });
            if (arec) {
                const seconds_ago = (Date.now() - arec.created_at) / 1000;
                if (seconds_ago < 600) account_recovery_record = arec;
            }
        }
        if (account_recovery_record) {
            if (user) {
                const existing_account = yield models.Account.findOne({
                    attributes: ['id'],
                    where: {
                        user_id: user.id,
                        name: account_recovery_record.account_name,
                    },
                    order: 'id DESC',
                });
                if (existing_account) {
                    console.log(
                        '-- arec: confirmed user for account -->',
                        this.session.uid,
                        provider,
                        account_recovery_record.id,
                        existing_account.name,
                        this.session.uid,
                        account_recovery_record.owner_key
                    );
                    account_recovery_record.update({
                        user_id: user.id,
                        status: 'confirmed',
                    });
                    this.redirect('/recover_account_step_2');
                } else {
                    console.log(
                        '-- arec: failed to confirm user for account (no account) -->',
                        this.session.uid,
                        provider,
                        account_recovery_record.id,
                        user.id,
                        this.session.uid,
                        account_recovery_record.owner_key
                    );
                    account_recovery_record.update({
                        user_id: user.id,
                        status: 'account not found',
                    });
                    this.body =
                        'We cannot verify the user account. Please contact support@steemit.com';
                }
            } else {
                console.log(
                    '-- arec: failed to confirm user for account (no user) -->',
                    this.session.uid,
                    provider,
                    this.session.arec,
                    this.session.email
                );
                account_recovery_record.update({ status: 'user not found' });
                this.body =
                    'We cannot verify the user account. Please contact support@steemit.com';
            }
            return null;
        }

        const waiting_list = !u.comment_karma || u.comment_karma < 5;
        const i_attrs = {
            provider: 'reddit',
            provider_user_id: u.id,
            name: u.name,
            score: u.comment_karma,
        };
        const attrs = {
            id: user ? user.id : null,
            uid: this.session.uid,
            name: u.name,
            remote_ip: getRemoteIp(this.req),
            verified: false,
        };
        if (user) {
            if (!waiting_list) attrs.waiting_list = false;
            yield models.User.update(attrs, { where: { id: user.id } });
            yield models.Identity.update(i_attrs, {
                where: { user_id: user.id, provider: 'reddit' },
            });
            console.log(
                '-- reddit updated user -->',
                this.session.uid,
                user.id,
                u.name
            );
        } else {
            attrs.waiting_list = waiting_list;
            user = yield models.User.create(attrs);
            console.log(
                '-- reddit created user -->',
                this.session.uid,
                user.id,
                u.name
            );
            i_attrs.user_id = user.id;
            const identity = yield models.Identity.create(i_attrs);
            console.log(
                '-- reddit created identity -->',
                this.session.uid,
                identity.id
            );
        }
        this.session.user = user.id;
        if (waiting_list) {
            this.redirect('/waiting_list.html');
            return null;
        }
    } catch (error) {
        return logErrorAndRedirect(this, 'reddit', error);
    }
    this.redirect('/enter_email');
    return null;
}

export default function useOauthLogin(app) {
    app.use(route.get('/handle_facebook_callback', handleFacebookCallback));
    app.use(route.get('/handle_reddit_callback', handleRedditCallback));
}
