# -*- coding: utf8 -*-
"""
.. module:: burpui.api.client
    :platform: Unix
    :synopsis: Burp-UI client api module.

.. moduleauthor:: Ziirish <hi+burpui@ziirish.me>

"""
# This is a submodule we can also use "from ..api import api"
from . import api, cache_key
from .custom import fields, Resource
from ..exceptions import BUIserverException
from flask.ext.login import current_user

ns = api.namespace('client', 'Client methods')


@ns.route('/browse/<name>/<int:backup>',
          '/<server>/browse/<name>/<int:backup>',
          endpoint='client_tree')
class ClientTree(Resource):
    """The :class:`burpui.api.client.ClientTree` resource allows you to
    retrieve a list of files in a given backup.

    This resource is part of the :mod:`burpui.api.client` module.

    An optional ``GET`` parameter called ``serverName`` is supported when running
    in multi-agent mode.
    A mandatory ``GET`` parameter called ``root`` is used to know what path we
    are working on.
    """
    parser = api.parser()
    parser.add_argument('serverName', type=str, help='Which server to collect data from when in multi-agent mode')
    parser.add_argument('root', type=str, help='Root path to expand')
    node_fields = api.model('ClientTree', {
        'date': fields.DateTime(required=True, dt_format='iso8601', description='Human representation of the backup date'),
        'gid': fields.Integer(required=True, description='gid owner of the node'),
        'inodes': fields.Integer(required=True, description='Inodes of the node'),
        'mode': fields.String(required=True, description='Human readable mode. Example: "drwxr-xr-x"'),
        'name': fields.String(required=True, description='Node name'),
        'parent': fields.String(required=True, description='Parent node name'),
        'size': fields.String(required=True, description='Human readable size. Example: "12.0KiB"'),
        'type': fields.String(required=True, description='Node type. Example: "d"'),
        'uid': fields.Integer(required=True, description='uid owner of the node'),
    })

    @api.cache.cached(timeout=3600, key_prefix=cache_key)
    @api.marshal_list_with(node_fields, code=200, description='Success')
    @api.doc(
        params={
            'server': 'Which server to collect data from when in multi-agent mode',
            'name': 'Client name',
            'backup': 'Backup number',
            'root': 'Root path to expand',
        },
        responses={
            '403': 'Insufficient permissions',
            '500': 'Internal failure',
        },
        parser=parser
    )
    def get(self, server=None, name=None, backup=None):
        """Returns a list of 'nodes' under a given path

        **GET** method provided by the webservice.

        The *JSON* returned is:
        ::

            [
              {
                "date": "2015-05-21 14:54:49",
                "gid": "0",
                "inodes": "173",
                "mode": "drwxr-xr-x",
                "name": "/",
                "parent": "",
                "size": "12.0KiB",
                "type": "d",
                "uid": "0"
              },
            ]


        The output is filtered by the :mod:`burpui.misc.acl` module so that you
        only see stats about the clients you are authorized to.

        :param server: Which server to collect data from when in multi-agent mode
        :type server: str

        :param name: The client we are working on
        :type name: str

        :param backup: The backup we are working on
        :type backup: int

        :returns: The *JSON* described above.
        """
        args = self.parser.parse_args()
        server = server or args['serverName']
        j = []
        if not name or not backup:  # pargma: no cover
            return j
        root = args['root']
        try:
            if (api.bui.acl and
                    (not api.bui.acl.is_admin(current_user.get_id()) and not
                     api.bui.acl.is_client_allowed(current_user.get_id(),
                                                   name,
                                                   server))):
                api.abort(403, 'Sorry, you are not allowed to view this client')
            j = api.bui.cli.get_tree(name, backup, root, agent=server)
        except BUIserverException as e:
            api.abort(500, str(e))
        return j


@ns.route('/report/<name>',
          '/<server>/report/<name>',
          '/report/<name>/<int:backup>',
          '/<server>/report/<name>/<int:backup>',
          endpoint='client_report')
class ClientReport(Resource):
    """The :class:`burpui.api.client.ClientStats` resource allows you to
    retrieve a report on a given backup for a given client.

    This resource is part of the :mod:`burpui.api.client` module.

    An optional ``GET`` parameter called ``serverName`` is supported when running
    in multi-agent mode.
    """
    parser = api.parser()
    parser.add_argument('serverName', type=str, help='Which server to collect data from when in multi-agent mode')
    report_tpl_fields = api.model('ClientReportTpl', {
        'changed': fields.Integer(required=True, description='Number of changed files', default=0),
        'deleted': fields.Integer(required=True, description='Number of deleted files', default=0),
        'new': fields.Integer(required=True, description='Number of new files', default=0),
        'scanned': fields.Integer(required=True, description='Number of scanned files', default=0),
        'total': fields.Integer(required=True, description='Total number of files', default=0),
        'unchanged': fields.Integer(required=True, description='Number of scanned files', default=0),
    })
    report_fields = api.model('ClientReport', {
        'dir': fields.Nested(report_tpl_fields, required=True),
        'duration': fields.Integer(required=True, description='Backup duration in seconds'),
        'efs': fields.Nested(report_tpl_fields, required=True),
        'encrypted': fields.Boolean(required=True, description='Is the backup encrypted'),
        'end': fields.Integer(required=True, description='Timestamp of the end date of the backup'),
        'files': fields.Nested(report_tpl_fields, required=True),
        'files_enc': fields.Nested(report_tpl_fields, required=True),
        'hardlink': fields.Nested(report_tpl_fields, required=True),
        'meta': fields.Nested(report_tpl_fields, required=True),
        'meta_enc': fields.Nested(report_tpl_fields, required=True),
        'number': fields.Integer(required=True, description='Backup number'),
        'received': fields.Integer(required=True, description='Bytes received'),
        'softlink': fields.Nested(report_tpl_fields, required=True),
        'special': fields.Nested(report_tpl_fields, required=True),
        'start': fields.Integer(required=True, description='Timestamp of the beginning of the backup'),
        'totsize': fields.Integer(required=True, description='Total size of the backup'),
        'vssfooter': fields.Nested(report_tpl_fields, required=True),
        'vssfooter_enc': fields.Nested(report_tpl_fields, required=True),
        'vssheader': fields.Nested(report_tpl_fields, required=True),
        'vssheader_enc': fields.Nested(report_tpl_fields, required=True),
        'windows': fields.Boolean(required=True, description='Is the client a windows system'),
    })

    @api.cache.cached(timeout=1800, key_prefix=cache_key)
    @api.marshal_with(report_fields, code=200, description='Success')
    @api.doc(
        params={
            'server': 'Which server to collect data from when in multi-agent mode',
            'name': 'Client name',
            'backup': 'Backup number',
        },
        responses={
            '403': 'Insufficient permissions',
            '500': 'Internal failure',
        },
        parser=parser
    )
    def get(self, server=None, name=None, backup=None):
        """Returns a global report of a given backup/client

        **GET** method provided by the webservice.

        The *JSON* returned is:
        ::

            {
              "dir": {
                "changed": 0,
                "deleted": 0,
                "new": 394,
                "scanned": 394,
                "total": 394,
                "unchanged": 0
              },
              "duration": 5,
              "efs": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "encrypted": true,
              "end": 1422189124,
              "files": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "files_enc": {
                "changed": 0,
                "deleted": 0,
                "new": 1421,
                "scanned": 1421,
                "total": 1421,
                "unchanged": 0
              },
              "hardlink": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "meta": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "meta_enc": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "number": 1,
              "received": 1679304,
              "softlink": {
                "changed": 0,
                "deleted": 0,
                "new": 1302,
                "scanned": 1302,
                "total": 1302,
                "unchanged": 0
              },
              "special": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "start": 1422189119,
              "total": {
                "changed": 0,
                "deleted": 0,
                "new": 3117,
                "scanned": 3117,
                "total": 3117,
                "unchanged": 0
              },
              "totsize": 5345361,
              "vssfooter": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "vssfooter_enc": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "vssheader": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "vssheader_enc": {
                "changed": 0,
                "deleted": 0,
                "new": 0,
                "scanned": 0,
                "total": 0,
                "unchanged": 0
              },
              "windows": "false"
            }


        The output is filtered by the :mod:`burpui.misc.acl` module so that you
        only see stats about the clients you are authorized to.

        :param server: Which server to collect data from when in multi-agent mode
        :type server: str

        :param name: The client we are working on
        :type name: str

        :param backup: The backup we are working on
        :type backup: int

        :returns: The *JSON* described above.
        """
        server = server or self.parser.parse_args()['serverName']
        j = []
        if not name:
            err = [[1, 'No client defined']]
            api.abort(400, err)
        if (api.bui.acl and not
                api.bui.acl.is_client_allowed(current_user.get_id(),
                                              name,
                                              server)):
            api.abort(403, 'You don\'t have rights to view this client report')
        if backup:
            try:
                j = api.bui.cli.get_backup_logs(backup, name, agent=server)
            except BUIserverException as e:
                api.abort(500, str(e))
        else:
            try:
                cl = api.bui.cli.get_client(name, agent=server)
            except BUIserverException as e:
                api.abort(500, str(e))
            err = []
            for c in cl:
                try:
                    j.append(api.bui.cli.get_backup_logs(c['number'], name, agent=server))
                except BUIserverException as e:
                    temp = [2, str(e)]
                    if temp not in err:
                        err.append(temp)
            if err:
                api.abort(500, err)
        return j


@ns.route('/stats/<name>',
          '/<server>/stats/<name>',
          endpoint='client_stats')
class ClientStats(Resource):
    """The :class:`burpui.api.client.ClientReport` resource allows you to
    retrieve a list of backups for a given client.

    This resource is part of the :mod:`burpui.api.client` module.

    An optional ``GET`` parameter called ``serverName`` is supported when running
    in multi-agent mode.
    """
    parser = api.parser()
    parser.add_argument('serverName', type=str, help='Which server to collect data from when in multi-agent mode')
    client_fields = api.model('ClientStats', {
        'number': fields.Integer(required=True, description='Backup number'),
        'received': fields.Integer(required=True, description='Bytes received'),
        'size': fields.Integer(required=True, description='Total size'),
        'encrypted': fields.Boolean(required=True, description='Is the backup encrypted'),
        'deletable': fields.Boolean(required=True, description='Is the backup deletable'),
        'date': fields.DateTime(required=True, dt_format='iso8601', description='Human representation of the backup date'),
    })

    @api.cache.cached(timeout=1800, key_prefix=cache_key)
    @api.marshal_list_with(client_fields, code=200, description='Success')
    @api.doc(
        params={
            'server': 'Which server to collect data from when in multi-agent mode',
            'name': 'Client name',
        },
        responses={
            '403': 'Insufficient permissions',
            '500': 'Internal failure',
        },
        parser=parser
    )
    def get(self, server=None, name=None):
        """Returns a list of backups for a given client

        **GET** method provided by the webservice.

        The *JSON* returned is:
        ::

            [
              {
                "date": "2015-01-25 13:32:00",
                "deletable": true,
                "encrypted": true,
                "received": 123,
                "size": 1234,
                "number": 1
              },
            ]

        The output is filtered by the :mod:`burpui.misc.acl` module so that you
        only see stats about the clients you are authorized to.

        :param server: Which server to collect data from when in multi-agent mode
        :type server: str

        :param name: The client we are working on
        :type name: str

        :returns: The *JSON* described above.
        """
        server = server or self.parser.parse_args()['serverName']
        try:
            if (api.bui.acl and (
                    not api.bui.acl.is_admin(current_user.get_id()) and
                    not api.bui.acl.is_client_allowed(current_user.get_id(),
                                                      name,
                                                      server))):
                api.abort(403, 'Sorry, you cannot access this client')
            j = api.bui.cli.get_client(name, agent=server)
        except BUIserverException as e:
            api.abort(500, str(e))
        return j
