#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import smtplib
import imaplib
import email
import re
import logging
import json
import time
import datetime
import dateutil.parser
import requests
import direct_juggler.juggler as dj

sys.path.insert(0, '/opt/downtime-db-utils')
import downtime_db_utils as utils

requests.packages.urllib3.disable_warnings()

SERVICE_NAME = "noc-mail-reader.working"
ALLDB_CONFIG = "/etc/yandex-direct/alldb-config.json"
IMAP_HOST = 'imap.yandex-team.ru'
ROBOT_LOGIN = 'robot-direct-mail'
SUB_LOCATION = "Yandex|noc-windy"
with open("/etc/direct-tokens/password_robot-direct-mail", "r") as fh:
    ROBOT_PASSWORD = fh.read().strip()

TIME_PATTERNS = [
    ur'\(?(\d{4}[-\.]{1}\d{2}[-\.]{1}\d{2})\)?\s+в\s+(\d{2}[-:]\d{2})',
    ur'\(?(\d{2}[-\.]{1}\d{2}[-\.]{1}\d{4})\)?\s+в\s+(\d{2}[-:]\d{2})',
    ur'(Завтра|Сегодня).+в\s+(\d{2}[-:]\d{2})',
]

def get_first_text_block(email_message):
    maintype = email_message.get_content_maintype()
    if maintype == 'multipart':
        for part in email_message.get_payload():
            if part.get_content_maintype() == 'text':
                return part.get_payload(decode=True)

    elif maintype == 'text':
        return email_message.get_payload(decode=True)


def get_direct_db_hosts():
    for i in xrange(5):
        try:
            return set(requests.get('https://c.yandex-team.ru/api/groups2hosts/direct').content.splitlines())
        except:
            logging.exception("can't get direct hosts from conductor")
            time.sleep(1)

    # если кондуктор не доступен, берем хосты из alldb-конфига
    with open(ALLDB_CONFIG, "r") as fh:
        config = json.load(fh)['instances']

    return set(host['host'] for instance in config for host in config[instance]["replicas"])


def get_event_datetime(text):
    time = None

    for pattern in TIME_PATTERNS:
        time = re.search(pattern, text, re.I | re.U)
        if time:
            break

    if not time:
        return None
    elif time.group(1).lower() == u"завтра":
        return dateutil.parser.parse(time.group(2).replace("-", ":")) + datetime.timedelta(days=1)
    elif time.group(1).lower() == u"сегодня":
        return dateutil.parser.parse(time.group(2).replace("-", ":"))
    
    return dateutil.parser.parse(u" ".join(time.groups()))


def check_single_mail(raw_mail, mail, direct_db_hosts):
    msg = email.message_from_string(raw_mail[1])
    text = get_first_text_block(msg).decode("utf-8")
    text = text.replace("\r", "")

    skip = not re.search(
        ur'Это письмо создано скриптом из RackTables\..+' +
        ur'Список ваших затронутых хостов можно посмотреть тут:.+' +
        ur'Работы повлияют на сетевую связность следующих устройств:',
        text, re.S | re.U
    )

    if skip:
        return

    subject = email.Header.decode_header(msg['subject'])[0][0].decode("utf-8")
    email_info = u"%s (%s)" % (subject, msg['date'])

    if text.find(u"ОТМЕНЕНЫ") != -1 or text.find(u"отменены") != -1:
        logging.warning(u"found cancellation email: %s\n%s" % (email_info, text))
        return

    logging.info(u"found target email: %s" % (email_info))

    event_datetime = get_event_datetime(text)
    if not event_datetime:
        logging.error(u"can't get time of event in email:\n%s" % text)
        return

    hosts_email = re.search(
        ur'Работы повлияют на сетевую связность следующих устройств:\n\n?(.+?)\n?Если',
        text, re.M | re.S | re.U
    )
    if not hosts_email:
        logging.error("can't get hosts:\n%s" % text)
        return

    hosts_direct = set(hosts_email.group(1).split("\n")) & direct_db_hosts
    if not hosts_direct:
        logging.info("no direct hosts")
    else:
        logging.info("!!! FOUND DIRECT HOSTS: %s !!!" % ", ".join(hosts_direct))
        utils.send_data_to_zk(hosts_direct, event_datetime, "nocmail: %s" % email_info)

    logging.info("success")
    return


def main():
    utils.zkh.start()
    utils.get_lock()

    direct_db_hosts = get_direct_db_hosts()

    mail = imaplib.IMAP4_SSL(IMAP_HOST)
    mail.login(ROBOT_LOGIN, ROBOT_PASSWORD)
    mail.select(SUB_LOCATION)
    mail_uids = mail.uid('search', None, '(UNSEEN)')[1][0].split()
    #mail_uids = list(reversed(mail.uid('search', None, '(ALL)')[1][0].split()))[:500]

    if not mail_uids:
        logging.info("no new mails")
        return

    mail_data = [
        raw_mail for raw_mail in mail.uid('fetch', ",".join(mail_uids), '(RFC822)')[1] if isinstance(raw_mail, tuple)
    ]
    mail.uid('store', ",".join(mail_uids), '-FLAGS', '(\\Seen)')

    for raw_mail, uid in zip(mail_data, mail_uids):
        try:
            check_single_mail(raw_mail, mail, direct_db_hosts)
        except:
            raise
        else:
            mail.uid('store', uid, '+FLAGS', '(\\Seen)')

    mail.logout()
    utils.zkh.stop()


if __name__ == '__main__':
    logging.basicConfig(
        format=u'%(levelname)-8s [%(asctime)s] %(message)s',
        level=logging.INFO,
    )

    try:
        logging.info("START")
        main()
        dj.queue_events([{'service': SERVICE_NAME, 'status': 'OK', 'description': ''}])
        logging.info("END")

    except Exception as e:
        logging.exception("unexpected exception")
        dj.queue_events([{
            'service': SERVICE_NAME,
            'status': 'CRIT',
            'description': "unexpected exception (%s) %s" % (type(e), e)
        }])

