#
# Copyright (c) SAS Institute Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#


from mod_python import apache
from email import MIMEText
import logging
import os
import sys
import time
import smtplib
import tempfile
import traceback

from conary.lib import coveragehook
from conary.lib import log
from conary.lib import util
from conary.lib.formattrace import formatTrace
from conary.repository.netrepos import netserver
from conary.repository.netrepos import proxy
from conary.server.apachemethods import get, post, putFile

cresthooks = None
try:
    from crest import webhooks as cresthooks
except ImportError:
    pass

def formatRequestInfo(req):
    c = req.connection
    req.add_common_vars()
    info_dict = {
        'local_addr'     : c.local_ip + ':' + str(c.local_addr[1]),
        'remote_addr'    : c.remote_ip + ':' + str(c.remote_addr[1]),
        'remote_host'    : c.remote_host,
        'remote_logname' : c.remote_logname,
        'aborted'        : c.aborted,
        'keepalive'      : c.keepalive,
        'double_reverse' : c.double_reverse,
        'keepalives'     : c.keepalives,
        'local_host'     : c.local_host,
        'connection_id'  : c.id,
        'notes'          : c.notes,
        'the_request'    : req.the_request,
        'proxyreq'       : req.proxyreq,
        'header_only'    : req.header_only,
        'protocol'       : req.protocol,
        'proto_num'      : req.proto_num,
        'hostname'       : req.hostname,
        'request_time'   : time.ctime(req.request_time),
        'status_line'    : req.status_line,
        'status'         : req.status,
        'method'         : req.method,
        'allowed'        : req.allowed,
        'headers_in'     : req.headers_in,
        'headers_out'    : req.headers_out,
        'uri'            : req.uri,
        'unparsed_uri'   : req.unparsed_uri,
        'args'           : req.args,
        'parsed_uri'     : req.parsed_uri,
        'filename'       : req.filename,
        'subprocess_env' : req.subprocess_env,
        'referer'        : req.headers_in.get('referer', 'N/A')
    }
    l = []
    for key, val in sorted(info_dict.items()):
        if hasattr(val, 'iteritems') and str(val) > 120:
            l.append('  %-15s: %s' %(key, '{'))
            for k, v in sorted(val.items()):
                l.append('    %s : %s,' % (repr(k), repr(v)))
            l[-1] += '}'
        else:
            l.append('  %-15s: %s' %(key, val))
    info = '\n'.join(l)
    return info

def sendMail(fromEmail, fromEmailName, toEmail, subject, body):
    msg = MIMEText.MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = '"%s" <%s>' % (fromEmailName, fromEmail)
    msg['To'] = toEmail

    s = smtplib.SMTP()
    s.connect()
    s.sendmail(fromEmail, [toEmail], msg.as_string())
    s.close()

def logAndEmail(req, cfg, header, msg):
    timeStamp = time.ctime(time.time())

    if not cfg.bugsFromEmail or not cfg.bugsToEmail:
        return
    log.error('sending mail to %s' % cfg.bugsToEmail)

    # send email
    body = header + '\n'
    body += 'Time of occurrence: %s\n' % timeStamp
    body += 'Conary repository server: %s\n\n' % req.hostname
    body += msg + '\n'
    body += '\nConnection Information:\n'
    body += formatRequestInfo(req)

    sendMail(cfg.bugsFromEmail, cfg.bugsEmailName, cfg.bugsToEmail,
             cfg.bugsEmailSubject, body)

def logErrorAndEmail(req, cfg, exception, e, bt):
    timeStamp = time.ctime(time.time())

    header = 'Unhandled exception from conary repository: %s\n%s: %s\n' % (
        req.hostname, exception.__name__, e)

    # Nicely format the exception
    tbFd, tbPath = tempfile.mkstemp('.txt', 'repos-error-')
    out = os.fdopen(tbFd, 'w+')
    formatTrace(exception, e, bt, stream = out, withLocals = False)
    out.write("\nFull stack:\n")
    formatTrace(exception, e, bt, stream = out, withLocals = True)
    out.seek(0)
    msg = out.read()

    logAndEmail(req, cfg, header, msg)
    # log error
    lines = traceback.format_exception(*sys.exc_info())
    lines.insert(0, "Unhandled exception from conary repository:\n")
    lines.append('Extended traceback at %s\n' % (tbPath,))
    log.error(''.join(lines))


def handler(req):
    coveragehook.install()
    try:
        return _handler(req)
    finally:
        coveragehook.save()

def _handler(req):
    log.setupLogging(consoleLevel=logging.INFO, consoleFormat='apache')
    # Keep CONARY_LOG entries from being double-logged
    log.logger.handlers = []

    repName = req.filename
    if repName in repositories:
        repServer, proxyServer, restHandler = repositories[repName]
    else:
        cfg = netserver.ServerConfig()
        cfg.read(req.filename)

        # Throw away any subdir portion.
        if cfg.baseUri:
            baseUri = cfg.baseUri
        else:
            baseUri = req.uri[:-len(req.path_info)] + '/'

        urlBase = "%%(protocol)s://%s:%%(port)d" % \
                        (req.server.server_hostname) + baseUri

        if not cfg.repositoryDB and not cfg.proxyContentsDir:
            log.error("repositoryDB or proxyContentsDir is required in %s" %
                      req.filename)
            return apache.HTTP_INTERNAL_SERVER_ERROR
        elif cfg.repositoryDB and cfg.proxyContentsDir:
            log.error("only one of repositoryDB or proxyContentsDir may be specified "
                      "in %s" % req.filename)
            return apache.HTTP_INTERNAL_SERVER_ERROR

        if cfg.repositoryDB: