#!/usr/bin/env python

import pickle
import time
import traceback

import httplib2
import arrow
from oauth2client import tools
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage

# from pprint import pprint

from googleapiclient.discovery import build
from slacker import Slacker
import os
import sys
import argparse
from oauth2client.client import AccessTokenRefreshError


from yaml import load

try:
    from yaml import CLoader as Loader, CDumper as Dumper
except ImportError:
    from yaml import Loader, Dumper


class Gmail2Slack():
    def __init__(self, config, slack):
        self.slack = slack
        self.config = config

        # Check https://developers.google.com/admin-sdk/directory/v1/guides/authorizing for all available scopes
        OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.readonly'

        # Location of the credentials storage file
        STORAGE = Storage(self.config['gmail_storage'])

        # Start the OAuth flow to retrieve credentials
        flow = flow_from_clientsecrets(config['client_secret'], scope=OAUTH_SCOPE)
        http = httplib2.Http()

        # Try to retrieve credentials from storage or run the flow to generate them
        parser = argparse.ArgumentParser(parents=[tools.argparser])
        flags = parser.parse_args([])

        credentials = None

        storage = Storage(self.config['gmail2slack_oauth'])
        try:
            credentials = storage.get()
        except:
            sys.exit("Unable to retrieve credentials")

        if not credentials:
            credentials = tools.run_flow(flow, STORAGE, flags)

        storage.put(credentials)
        # Authorize the httplib2.Http object with our credentials
        http = credentials.authorize(http)

        # Build the Gmail service from discovery
        self.gmail_service = build('gmail', 'v1', http=http)
        self.user_id = 'me'

        if 'gmail_label' in self.config:
            self.label_name = self.config['gmail_label']
        else:
            self.label_name = 'INBOX'

        try:
            self.state = pickle.load(open(self.config['gmail2slack_pickle'], "rb"))
        except IOError:
            self.state = dict()
            self.state['timestamp'] = arrow.utcnow().timestamp

            # self.new_timestamp = arrow.utcnow().timestamp # BUG?  Move to gmail2slack?

    def save_state(self):
        # Save timestamp so we don't process the same files again
        # self.state['timestamp'] = self.new_timestamp
        self.state['timestamp'] = arrow.utcnow().timestamp
        pickle.dump(self.state, open(self.config['gmail2slack_pickle'], "wb"))

    def getLabelIdByName(self,name):
        response=self.gmail_service.users().labels().list(userId=self.user_id).execute()
        if "labels" in response:
            for label in response["labels"]:
                if label["name"] == name:
                    return label["id"]
        return None

    def gmail2slack(self):
        try:
            label_id = self.getLabelIdByName(self.label_name)
            if not label_id: raise Exception("target label name not found")
            response = self.gmail_service.users().messages().list(userId=self.user_id, labelIds=label_id).execute()
        except AccessTokenRefreshError:
            return

        message_ids = []
        if 'messages' in response:
            message_ids.extend(response['messages'])
        for msg_id in message_ids:
            message = self.gmail_service.users().messages().get(userId=self.user_id, id=msg_id['id']).execute()
            headers = dict()
            for header in message['payload']['headers']:
                headers[header['name']] = header['value']

            try:  # due to issue @ https://github.com/crsmithdev/arrow/issues/176
                from_ts = arrow.get(headers['Date'], 'ddd, D MMM YYYY HH:mm:ss ZZ').timestamp
            except:
                continue

            if from_ts < self.state['timestamp']:
                break
            from_date = arrow.get(from_ts).to('US/Eastern').format('YYYY-MM-DD HH:mm:ss ZZ')
            say = "New Email\n>From: %s\n>Date: %s\n>Subject: %s\n>\n>%s" % \
                  (headers['From'], from_date, headers['Subject'], message['snippet'])
            self.slack.direct_message(say, self.config['slack_user_id'], self.config['slack_from'])
        self.save_state()


class Slack():
    def __init__(self, apikey):
        self.slack = Slacker(apikey)

    def get_name_id(self, name):
        users = self.slack.users.list()
        user_id = None
        for member in users.body['members']:
            if member['name'] == name:
                user_id = member['id']
                break
        return user_id

    def direct_message(self, message, user_id, slack_from):
        self.slack.chat.post_message(user_id, message, username=slack_from)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-v", "--verbose", help="verbosity", action="store_true")
    parser.add_argument("-c", "--config", help="path to gmail2slack.yaml", action="store",
                        default=os.getenv("HOME") + "/.config/gmail2slack/default.yaml")
    parser.add_argument("-l", "--loop", help="loop every x seconds", action="store", default=0)
    args = parser.parse_args()

    try:
        config = load(open(args.config, "r"), Loader=Loader)
    except IOError:
        sys.exit("Unable to open config file %s" % args.config)

    slack = Slack(config['slack_apikey'])
    # validate config
    if not 'slack_user_id' in config:
        config['slack_user_id'] = slack.get_name_id(config['slack_user'])
    if not config['slack_user_id']:
        sys.exit("Could not find slack id for user %s" % config['slack_user'])
    # Make sure all paths are absolute
    if 'dir' not in config or not os.path.isdir(config['dir']):
        config['dir'] = os.path.basename(args.config)
    for key in ['client_secret', 'gmail2slack_pickle', 'gmail2slack_oauth', 'gmail_storage']:
        if not os.path.isabs(config[key]):
            config[key] = "%s/%s" % (os.path.dirname(args.config), config[key])
    if not os.path.isfile(config['client_secret']):
        sys.exit("Unable to open client_secret file %s" % config['client_secret'])

    g2s = Gmail2Slack(config, slack)
    if int(args.loop) > 0:
        delay = int(args.loop)
    else:
        delay = 0
    while True:
        try:
            g2s.gmail2slack()
        except:
            traceback.print_exc()
        if delay:
            time.sleep(delay)
        else:
            break


if __name__ == "__main__":
    main()