# coding:utf-8
from django.db.models import Q
from django.template import RequestContext
from django.shortcuts import render_to_response, render
from jumpserver.api import *
from jperm.perm_api import user_have_perm
from django.http import HttpResponseNotFound
from jlog.log_api import renderTemplate

from jlog.models import Log, ExecLog, FileLog, TermLog
from jumpserver.settings import LOG_DIR
import zipfile
import json
import pyte


@require_role('admin')
def log_list(request, offset):
    """ 显示日志 """
    header_title, path1 = u'审计', u'操作审计'
    date_seven_day = request.GET.get('start', '')
    date_now_str = request.GET.get('end', '')
    username_list = request.GET.getlist('username', [])
    host_list = request.GET.getlist('host', [])
    cmd = request.GET.get('cmd', '')

    if offset == 'online':
        keyword = request.GET.get('keyword', '')
        posts = Log.objects.filter(is_finished=False).order_by('-start_time')
        if keyword:
            posts = posts.filter(Q(user__icontains=keyword) | Q(host__icontains=keyword) |
                                 Q(login_type_icontains=keyword))

    elif offset == 'exec':
        posts = ExecLog.objects.all().order_by('-id')
        keyword = request.GET.get('keyword', '')
        if keyword:
            posts = posts.filter(Q(user__icontains=keyword) | Q(host__icontains=keyword) | Q(cmd__icontains=keyword))
    elif offset == 'file':
        posts = FileLog.objects.all().order_by('-id')
        keyword = request.GET.get('keyword', '')
        if keyword:
            posts = posts.filter(
                Q(user__icontains=keyword) | Q(host__icontains=keyword) | Q(filename__icontains=keyword))
    else:
        posts = Log.objects.filter(is_finished=True).order_by('-start_time')
        username_all = set([log.user for log in Log.objects.all()])
        ip_all = set([log.host for log in Log.objects.all()])

        if date_seven_day and date_now_str:
            datetime_start = datetime.datetime.strptime(date_seven_day + ' 00:00:01', '%m/%d/%Y %H:%M:%S')
            datetime_end = datetime.datetime.strptime(date_now_str + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
            posts = posts.filter(start_time__gte=datetime_start).filter(start_time__lte=datetime_end)

        if username_list:
            posts = posts.filter(user__in=username_list)

        if host_list:
            posts = posts.filter(host__in=host_list)

        if cmd:
            log_id_list = set([log.log_id for log in TtyLog.objects.filter(cmd__contains=cmd)])
            posts = posts.filter(id__in=log_id_list)

        if not date_seven_day:
            date_now = datetime.datetime.now()
            date_now_str = date_now.strftime('%m/%d/%Y')
            date_seven_day = (date_now + datetime.timedelta(days=-7)).strftime('%m/%d/%Y')

    contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)

    session_id = request.session.session_key
    return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request))


@require_role('admin')
def log_detail(request):
    return my_render('jlog/exec_detail.html', locals(), request)


@require_role('admin')
def log_kill(request):
    """ 杀掉connect进程 """
    pid = request.GET.get('id', '')
    log = Log.objects.filter(pid=pid)
    if log:
        log = log[0]
        try:
            os.kill(int(pid), 9)
        except OSError:
            pass
        Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.datetime.now())
        return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request))
    else:
        return HttpResponseNotFound(u'没有此进程!')


@require_role('admin')
def log_history(request):
    """ 命令历史记录 """
    log_id = request.GET.get('id', 0)
    log = Log.objects.filter(id=log_id)
    if log:
        log = log[0]
        tty_logs = log.ttylog_set.all()

        if tty_logs:
            content = ''
            for tty_log in tty_logs:
                content += '%s: %s\n' % (tty_log.datetime.strftime('%Y-%m-%d %H:%M:%S'), tty_log.cmd)
            return HttpResponse(content)

    return HttpResponse('无日志记录!')


# @require_role('admin')
# def log_record(request):
#     log_id = request.GET.get('id', 0)
#     log = Log.objects.filter(id=int(log_id))
#     if log:
#         log = log[0]
#         log_file = log.log_path + '.log'
#         log_time = log.log_path + '.time'
#         if os.path.isfile(log_file) and os.path.isfile(log_time):
#             content = renderTemplate(log_file, log_time)
#             return HttpResponse(content)
#         else:
#             return HttpResponse('无日志记录!')
@require_role('admin')
def log_record(request):
    """
    Author: liuzheng712@gmail.com
    """
    if request.method == "GET":
        return render(request, 'jlog/record.html')
    elif request.method == "POST":
        log_id = request.REQUEST.get('id', None)
        if log_id:
            TermL = TermLogRecorder(request.user)
            log = Log.objects.get(id=int(log_id))
            return HttpResponse(TermL.load_full_log(filename=log.filename))
        else:
            return HttpResponse("ERROR")
    else:
        return HttpResponse("ERROR METHOD!")


@require_role('admin')
def log_detail(request, offset):
    log_id = request.GET.get('id')
    if offset == 'exec':
        log = get_object(ExecLog, id=log_id)
        assets_hostname = log.host.split(' ')
        try:
            result = eval(str(log.result))
        except (SyntaxError, NameError):
            result = {}
        return my_render('jlog/exec_detail.html', locals(), request)
    elif offset == 'file':
        log = get_object(FileLog, id=log_id)
        assets_hostname = log.host.split(' ')
        file_list = log.filename.split(' ')
        try:
            result = eval(str(log.result))
        except (SyntaxError, NameError):
            result = {}
        return my_render('jlog/file_detail.html', locals(), request)


class TermLogRecorder(object):
    """
    TermLogRecorder
    ---
    Author: liuzheng <liuzheng712@gmail>
    This class is use for record the terminal output log.
        self.commands is pure commands list, it will have empty item '' because in vi/vim model , I made it log noting.
        self.CMD is the command with timestamp, like this {'1458723794.88': u'ls', '1458723799.82': u'tree'}.
        self.log is the all output with delta time log.
        self.vim_pattern is the regexp for check vi/vim/fg model.
    Usage:
        recorder = TermLogRecorder(user=UserObject) # or recorder = TermLogRecorder(uid=UserID)
        recoder.write(messages)
        recoder.save() # save all log into database
        # The following methods all have `user`,`uid`,args. Same as __init__
        list = recoder.list() # will give a object about this user's all log info
        recoder.load_full_log(filemane) # will get full log
        recoder.load_history(filename) # will only get the command history list
        recoder.share_to(filename,user=UserObject) # or recoder.share_to(filename,uid=UserID). will share this commands to someone
        recoder.unshare_to(filename,user=UserObject) # or recoder.unshare_to(filename,uid=UserID). will unshare this commands to someone
        recoder.setid(id) # registered this term with an id, for monitor
    """
    loglist = dict()

    def __init__(self, user=None, uid=None):
        self.log = {}
        self.id = 0
        if isinstance(user, User):
            self.user = user
        elif uid:
            self.user = User.objects.get(id=uid)
        else:
            self.user = None
        self.recoderStartTime = time.time()
        self.__init_screen_stream()
        self.recoder = False
        self.commands = []
        self._lists = None
        self.file = None
        self.filename = None
        self._data = None
        self.vim_pattern = re.compile(r'\W?vi[m]?\s.* | \W?fg\s.*', re.X)
        self._in_vim = False
        self.CMD = {}

    def __init_screen_stream(self):
        """
        Initializing the virtual screen and the character stream
        """
        self._stream = pyte.ByteStream()
        self._screen = pyte.Screen(80, 24)
        self._stream.attach(self._screen)

    def _command(self):
        for i in self._screen.display:
            if i.strip().__len__() > 0:
                self.commands.append(i.strip())
                if not i.strip() == '':
                    self.CMD[str(time.time())] = self.commands[-1]
        self._screen.reset()

    def setid(self, id):
        self.id = id
        TermLogRecorder.loglist[str(id)] = [self]

    def write(self, msg):
        if self.recoder and (not self._in_vim):
            if self.commands.__len__() == 0:
                self._stream.feed(msg)
            elif not self.vim_pattern.search(self.commands[-1]):
                self._stream.feed(msg)
            else:
                self._in_vim = True
                self._command()
        else:
            if self._in_vim:
                if re.compile(r'\[\?1049', re.X).search(msg.decode('utf-8', 'replace')):
                    self._in_vim = False
                    self.commands.append('')
                self._screen.reset()
            else:
                self._command()
        try:
            self.write_message(msg)
        except:
            pass
        # print "<<<<<<<<<<<<<<<<"
        # print self.commands
        # print self.CMD
        # print ">>>>>>>>>>>>>>>>"
        self.log[str(time.time() - self.recoderStartTime)] = msg.decode('utf-8', 'replace')

    def save(self, path=LOG_DIR):
        date = datetime.datetime.now().strftime('%Y%m%d')
        filename = str(uuid.uuid4())
        self.filename = filename
        filepath = os.path.join(path, 'tty', date, filename + '.zip')
        if not os.path.isdir(os.path.join(path, 'tty', date)):
            os.makedirs(os.path.join(path, 'tty', date), mode=0777)
        while os.path.isfile(filepath):
            filename = str(uuid.uuid4())
            filepath = os.path.join(path, 'tty', date, filename + '.zip')
        password = str(uuid.uuid4())
        try:
            zf = zipfile.ZipFile(filepath, 'w', zipfile.ZIP_DEFLATED)
            zf.setpassword(password)
            zf.writestr(filename, json.dumps(self.log))
            zf.close()
            record = TermLog.objects.create(logPath=filepath, logPWD=password, filename=filename,
                                            history=json.dumps(self.CMD), timestamp=int(self.recoderStartTime))
            if self.user:
                record.user.add(self.user)
        except:
            record = TermLog.objects.create(logPath='locale', logPWD=password, log=json.dumps(self.log),
                                            filename=filename, history=json.dumps(self.CMD),
                                            timestamp=int(self.recoderStartTime))
            if self.user:
                record.user.add(self.user)
        try:
            del TermLogRecorder.loglist[str(self.id)]
        except KeyError:
            pass

    def list(self, user=None, uid=None):
        tmp = []
        if isinstance(user, User):
            user = user
        elif uid:
            user = User.objects.get(id=uid)
        else:
            user = self.user
        if user:
            self._lists = TermLog.objects.filter(user=user.id)
            for i in self._lists.all():
                tmp.append(
                    {'filename': i.filename, 'locale': i.logPath == 'locale', 'nick': i.nick, 'timestamp': i.timestamp,
                     'date': i.datetimestamp})
        return tmp

    def load_full_log(self, filename, user=None, uid=None):
        if isinstance(user, User):
            user = user
        elif uid:
            user = User.objects.get(id=uid)
        else:
            user = self.user
        if user:
            if self._lists:
                self.file = self._lists.get(filename=filename)
            else:
                self.file = TermLog.objects.get(user=user.id, filename=filename)
            if self.file.logPath == 'locale':
                return self.file.log
            else:
                try:
                    zf = zipfile.ZipFile(self.file.logPath, 'r', zipfile.ZIP_DEFLATED)
                    zf.setpassword(self.file.logPWD)
                    self._data = zf.read(zf.namelist()[0])
                    return self._data
                except KeyError:
                    return 'ERROR: Did not find %s file' % filename
        return 'ERROR User(None)'

    def load_history(self, filename, user=None, uid=None):
        if isinstance(user, User):
            user = user
        elif uid:
            user = User.objects.get(id=uid)
        else:
            user = self.user
        if user:
            if self._lists:
                self.file = self._lists.get(filename=filename)
            else:
                self.file = TermLog.objects.get(user=user.id, filename=filename)
            return self.file.history
        return 'ERROR User(None)'

    def share_to(self, filename, user=None, uid=None):
        if isinstance(user, User):
            user = user
        elif uid:
            user = User.objects.get(id=uid)
        else:
            pass
        if user:
            TermLog.objects.get(filename=filename).user.add(user)
            return True
        return False

    def unshare_to(self, filename, user=None, uid=None):
        if isinstance(user, User):
            user = user
        elif uid:
            user = User.objects.get(id=uid)
        else:
            pass
        if user:
            TermLog.objects.get(filename=filename).user.remove(user)
            return True
        return False
