# Author: Nic Wolfe <nic@wolfeden.ca>
# URL: http://code.google.com/p/sickbeard/
#
# This file is part of SickRage.
#
# SickRage is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SickRage is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import with_statement
import os
import re
import sys
import logging
import logging.handlers
import threading
import platform
import locale

import sickbeard
from sickbeard import classes, encodingKludge as ek
from github import Github, InputFileContent
import codecs

# log levels
ERROR = logging.ERROR
WARNING = logging.WARNING
INFO = logging.INFO
DEBUG = logging.DEBUG
DB = 5

reverseNames = {u'ERROR': ERROR,
                u'WARNING': WARNING,
                u'INFO': INFO,
                u'DEBUG': DEBUG,
                u'DB': DB}

censoredItems = {}


class NullHandler(logging.Handler):
    def emit(self, record):
        pass


class CensoredFormatter(logging.Formatter, object):
    def __init__(self, *args, **kwargs):
        super(CensoredFormatter, self).__init__(*args, **kwargs)

    def format(self, record):
        msg = super(CensoredFormatter, self).format(record)
        for k, v in censoredItems.iteritems():
            if v and len(v) > 0 and v in msg:
                msg = msg.replace(v, len(v) * '*')
        # Needed because Newznab apikey isn't stored as key=value in a section.
        msg = re.sub(r'([&?]r|[&?]apikey|[&?]api_key)=[^&]*([&\w]?)',r'\1=**********\2', msg)
        return msg


class Logger(object):
    def __init__(self):
        self.logger = logging.getLogger('sickrage')

        self.loggers = [
            logging.getLogger('sickrage'),
            logging.getLogger('tornado.general'),
            logging.getLogger('tornado.application'),
            # logging.getLogger('tornado.access'),
        ]

        self.consoleLogging = False
        self.fileLogging = False
        self.debugLogging = False
        self.logFile = None

        self.submitter_running = False

    def initLogging(self, consoleLogging=False, fileLogging=False, debugLogging=False):
        self.logFile = self.logFile or os.path.join(sickbeard.LOG_DIR, 'sickrage.log')
        self.debugLogging = debugLogging
        self.consoleLogging = consoleLogging
        self.fileLogging = fileLogging

        # add a new logging level DB
        logging.addLevelName(DB, 'DB')

        # nullify root logger
        logging.getLogger().addHandler(NullHandler())

        # set custom root logger
        for logger in self.loggers:
            if logger is not self.logger:
                logger.root = self.logger
                logger.parent = self.logger

        # set minimum logging level allowed for loggers
        for logger in self.loggers:
            logger.setLevel(DB)

        # console log handler
        if self.consoleLogging:
            console = logging.StreamHandler()
            console.setFormatter(CensoredFormatter(u'%(asctime)s %(levelname)s::%(message)s', '%H:%M:%S'))
            console.setLevel(INFO if not self.debugLogging else DEBUG)

            for logger in self.loggers:
                logger.addHandler(console)

        # rotating log file handler
        if self.fileLogging:
            rfh = logging.handlers.RotatingFileHandler(self.logFile, maxBytes=sickbeard.LOG_SIZE, backupCount=sickbeard.LOG_NR, encoding='utf-8')
            rfh.setFormatter(CensoredFormatter(u'%(asctime)s %(levelname)-8s %(message)s', '%Y-%m-%d %H:%M:%S'))
            rfh.setLevel(DEBUG)

            for logger in self.loggers:
                logger.addHandler(rfh)
                
    def shutdown(self):
        
        logging.shutdown()
        
    def log(self, msg, level=INFO, *args, **kwargs):
        meThread = threading.currentThread().getName()
        message = meThread + u" :: " + msg

        # pass exception information if debugging enabled

        if level == ERROR:
            #Replace the SSL error with a link to information about how to fix it.
            message = re.sub(r'error \[Errno 1\] _ssl.c:\d{3}: error:\d{8}:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert internal error', r'See: http://git.io/vJrkM', message)
            self.logger.exception(message, *args, **kwargs)
            classes.ErrorViewer.add(classes.UIError(message))

            # if sickbeard.GIT_AUTOISSUES:
            #    self.submit_errors()
        else:
            self.logger.log(level, message, *args, **kwargs)

    def log_error_and_exit(self, error_msg, *args, **kwargs):
        self.log(error_msg, ERROR, *args, **kwargs)

        if not self.consoleLogging:
            sys.exit(error_msg.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace'))
        else:
            sys.exit(1)

    def submit_errors(self):
        if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and len(classes.ErrorViewer.errors) > 0):
            self.log('Please set your GitHub username and password in the config, unable to submit issue ticket to GitHub!')
            return

        if self.submitter_running:
            return 'RUNNING'

        self.submitter_running = True

        gh_org = sickbeard.GIT_ORG or 'SiCKRAGETV'
        gh_repo = 'sickrage-issues'

        gh = Github(login_or_token=sickbeard.GIT_USERNAME, password=sickbeard.GIT_PASSWORD, user_agent="SiCKRAGE")

        try:
            # read log file
            log_data = None

            if os.path.isfile(self.logFile):
                with ek.ek(codecs.open, *[self.logFile, 'r', 'utf-8']) as f:
                    log_data = f.readlines()
                    
            for i in range (1 , int(sickbeard.LOG_NR)):
                if os.path.isfile(self.logFile + "." + str(i)) and (len(log_data) <= 500):
                    with ek.ek(codecs.open, *[self.logFile + "." + str(i), 'r', 'utf-8']) as f:
                            log_data += f.readlines()

            log_data = [line for line in reversed(log_data)]

            # parse and submit errors to issue tracker
            for curError in sorted(classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]:
                #Skip SSL Error, we pointed them to a URL.
                if re.search('http://git.io/vJrkM', curError.message):
                    classes.ErrorViewer.errors.remove(curError)
                    continue

                try:
                    title_Error = str(curError.title)
                    if not len(title_Error) or title_Error == 'None':
                        title_Error = re.match("^[A-Z0-9\-\[\] :]+::\s*(.*)$", ek.ss(str(curError.message))).group(1)
                    if len(title_Error) > 1024:
                        title_Error = title_Error[0:1024]
                except Exception as e:
                    self.log("Unable to get error title : " + sickbeard.exceptions.ex(e), ERROR)

                gist = None
                regex = "^(%s)\s+([A-Z]+)\s+([0-9A-Z\-]+)\s*(.*)$" % curError.time
                for i, x in enumerate(log_data):
                    x = ek.ss(x)
                    match = re.match(regex, x)
                    if match:
                        level = match.group(2)
                        if reverseNames[level] == ERROR:
                            paste_data = "".join(log_data[i:i+50])
                            if paste_data:
                                gist = gh.get_user().create_gist(True, {"sickrage.log": InputFileContent(paste_data)})
                            break
                    else:
                        gist = 'No ERROR found'

                message = u"### INFO\n"
                message += u"Python Version: **" + sys.version[:120].replace('\n','') + "**\n"
                message += u"Operating System: **" + platform.platform() + "**\n"
                if not 'Windows' in platform.platform():
                    try:
                        message += u"Locale: " + locale.getdefaultlocale()[1] + "\n"
                    except:
                        message += u"Locale: unknown" + "\n"                        
                message += u"Branch: **" + sickbeard.BRANCH + "**\n"
                message += u"Commit: SiCKRAGETV/SickRage@" + sickbeard.CUR_COMMIT_HASH + "\n"
                if gist and gist != 'No ERROR found':
                    message += u"Link to Log: " + gist.html_url + "\n"
                else:
                    message += u"No Log available with ERRORS: " + "\n"
                message += u"### ERROR\n"
                message += u"```\n"
                message += curError.message + "\n"
                message += u"```\n"
                message += u"---\n"
                message += u"_STAFF NOTIFIED_: @SiCKRAGETV/owners @SiCKRAGETV/moderators"

                title_Error = u"[APP SUBMITTED]: " + title_Error
                reports = gh.get_organization(gh_org).get_repo(gh_repo).get_issues()

                issue_found = False
                issue_id = 0
                for report in reports:
                    if title_Error == report.title:
                        comment = report.create_comment(message)
                        if comment:
                            issue_id = report.number
                            self.log('Commented on existing issue #%s successfully!'  % issue_id )
                            issue_found = True
                        break

                if not issue_found:
                    issue = gh.get_organization(gh_org).get_repo(gh_repo).create_issue(title_Error, message)
                    if issue:
                        issue_id = issue.number
                        self.log('Your issue ticket #%s was submitted successfully!'  % issue_id )

                # clear error from error list
                classes.ErrorViewer.errors.remove(curError)

                self.submitter_running = False
                return issue_id
        except Exception as e:
            self.log(sickbeard.exceptions.ex(e), ERROR)

        self.submitter_running = False

class Wrapper(object):
    instance = Logger()

    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        try:
            return getattr(self.wrapped, name)
        except AttributeError:
            return getattr(self.instance, name)


_globals = sys.modules[__name__] = Wrapper(sys.modules[__name__])