import { LogUsageChart } from '../../graphing';
import { parse } from 'path';

/**
 * Element which displays usage logs for the given entity.
 */
angular.module('quay').directive('logsView', function () {
  var directiveDefinitionObject = {
    priority: 0,
    templateUrl: '/static/directives/logs-view.html',
    replace: false,
    transclude: false,
    restrict: 'C',
    scope: {
      'organization': '=organization',
      'user': '=user',
      'makevisible': '=makevisible',
      'repository': '=repository',
      'allLogs': '@allLogs'
    },
    controller: function($scope, $element, $sce, Restangular, ApiService, TriggerService,
                         StringBuilderService, ExternalNotificationData, UtilService,
                         Features) {
      $scope.Features = Features;
      $scope.loading = true;
      $scope.loadCounter = -1;
      $scope.logs = null;
      $scope.kindsAllowed = null;
      $scope.chartVisible = true;
      $scope.chartLoading = true;

      $scope.options = {};
      $scope.context = {};

      var datetime = new Date();
      $scope.options.logStartDate = new Date(datetime.getUTCFullYear(), datetime.getUTCMonth(), datetime.getUTCDate() - 7);
      $scope.options.logEndDate = new Date(datetime.getUTCFullYear(), datetime.getUTCMonth(), datetime.getUTCDate());
      $scope.options.monthAgo = moment().subtract(1, 'month').calendar();
      $scope.options.now = new Date(datetime.getUTCFullYear(), datetime.getUTCMonth(), datetime.getUTCDate());

      var getOffsetDate = function(date, days) {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);
      };

      var defaultPermSuffix = function(metadata) {
        if (metadata.activating_username) {
          return ', when creating user is {activating_username}';
        }
        return '';
      };

      var getServiceKeyTitle = function(metadata) {
        if (metadata.name) {
          return metadata.name;
        }

        return metadata.kind.substr(0, 12);
      };

      var logDescriptions = {
          'account_change_plan': 'Change plan',
          'account_change_cc': 'Update credit card',
          'account_change_password': 'Change password',
          'account_convert': 'Convert account to organization',
          'create_robot': 'Create Robot Account {robot}',
          'delete_robot': 'Delete Robot Account {robot}',
          'create_repo': 'Create Repository {namespace}/{repo}',
          'push_repo': function(metadata) {
            if (metadata.tag) {
              return 'Push of {tag} to repository {namespace}/{repo}';
            } else if (metadata.release) {
              return 'Push of {release} to repository {namespace}/{repo}';
            } else {
              return 'Repository push to {namespace}/{repo}';
            }
          },
          'repo_verb': function(metadata) {
            var prefix = '';
            if (metadata.verb == 'squash') {
              prefix = 'Pull of squashed tag {tag} from {namespace}/{repo}'
            }

            if (metadata.token) {
              if (metadata.token_type == 'build-worker') {
                prefix += ' by <b>build worker</b>';
              } else {
                prefix += ' via token';
              }
            } else if (metadata.username) {
              prefix += ' by {username}';
            } else {
              prefix += ' by {_ip}';
            }

            return prefix;
          },
          'pull_repo': function(metadata) {
            var description = 'repository {namespace}/{repo}';
            if (metadata.tag) {
              description = 'tag {tag} from repository {namespace}/{repo}';
            } else if (metadata.manifest_digest) {
              description = 'digest {manifest_digest} from repository {namespace}/{repo}';
            } else if (metadata.release) {
              description = 'release {release}';
              if (metadata.channel) {
                description += ' via channel {channel}';
              }
              if (metadata.mediatype) {
                description += ' for {mediatype}';
              }
              description += ' from repository {namespace}/{repo}';
            }

            if (metadata.token) {
              if (metadata.token_type == 'build-worker') {
                return 'Pull of ' + description + ' by <b>build worker</b>';
              } else {
                return 'Pull of ' + description + ' via token';
              }
              return prefix;
            } else if (metadata.username) {
              return 'Pull ' + description + ' by {username}';
            } else {
              return 'Public pull of ' + description + ' by {_ip}';
            }
          },
          'delete_repo': 'Delete repository: {repo}',
          'change_repo_permission': function(metadata) {
            if (metadata.username) {
              return 'Change permission for user {username} in repository {namespace}/{repo} to {role}';
            } else if (metadata.team) {
                return 'Change permission for team {team} in repository {namespace}/{repo} to {role}';
            } else if (metadata.token) {
              return 'Change permission for token {token} in repository {namespace}/{repo} to {role}';
            }
          },
          'delete_repo_permission': function(metadata) {
            if (metadata.username) {
              return 'Remove permission for user {username} from repository {namespace}/{repo}';
            } else if (metadata.team) {
                return 'Remove permission for team {team} from repository {namespace}/{repo}';
            } else if (metadata.token) {
              return 'Remove permission for token {token} from repository {namespace}/{repo}';
            }
          },
          'revert_tag': function(metadata) {
            if (metadata.original_image) {
              return 'Tag {tag} restored to image {image} from image {original_image}';
            } else {
              return 'Tag {tag} recreated pointing to image {image}';
            }
          },
          'delete_tag': 'Tag {tag} deleted in repository {namespace}/{repo} by user {username}',
          'create_tag': 'Tag {tag} created in repository {namespace}/{repo} on image {image} by user {username}',
          'move_tag': 'Tag {tag} moved from image {original_image} to image {image} in repository {namespace}/{repo} by user {username}',
          'change_repo_visibility': 'Change visibility for repository {namespace}/{repo} to {visibility}',
          'change_repo_trust': function(metadata) {
            if (metadata.trust_enabled) {
              return 'Trust enabled for {namespace}/{repo}';
            } else {
              return 'Trust disabled for {namespace}/{repo}';
            }
          },
          'add_repo_accesstoken': 'Create access token {token} in repository {repo}',
          'delete_repo_accesstoken': 'Delete access token {token} in repository {repo}',
          'set_repo_description': 'Change description for repository {namespace}/{repo}',
          'build_dockerfile': function(metadata) {
            if (metadata.trigger_id) {
              var triggerDescription = TriggerService.getDescription(
                metadata['service'], metadata['config']);
              return 'Build image from Dockerfile for repository {namespace}/{repo} triggered by ' + triggerDescription;
            }
            return 'Build image from Dockerfile for repository {namespace}/{repo}';
          },
          'org_create_team': 'Create team {team}',
          'org_delete_team': 'Delete team {team}',
          'org_add_team_member': 'Add member {member} to team {team}',
          'org_remove_team_member': 'Remove member {member} from team {team}',
          'org_invite_team_member': function(metadata) {
            if (metadata.user) {
              return 'Invite {user} to team {team}';
            } else {
              return 'Invite {email} to team {team}';
            }
          },
          'org_delete_team_member_invite': function(metadata) {
            if (metadata.user) {
              return 'Rescind invite of {user} to team {team}';
            } else {
              return 'Rescind invite of {email} to team {team}';
            }
          },

          'org_team_member_invite_accepted': 'User {member}, invited by {inviter}, joined team {team}',
          'org_team_member_invite_declined': 'User {member}, invited by {inviter}, declined to join team {team}',

          'org_set_team_description': 'Change description of team {team}: {description}',
          'org_set_team_role': 'Change permission of team {team} to {role}',
          'create_prototype_permission': function(metadata) {
            if (metadata.delegate_user) {
              return 'Create default permission: {role} for {delegate_user}' + defaultPermSuffix(metadata);
            } else if (metadata.delegate_team) {
              return 'Create default permission: {role} for {delegate_team}' + defaultPermSuffix(metadata);
            }
          },
          'modify_prototype_permission': function(metadata) {
            if (metadata.delegate_user) {
              return 'Modify default permission: {role} (from {original_role}) for {delegate_user}' + defaultPermSuffix(metadata);
            } else if (metadata.delegate_team) {
              return 'Modify default permission: {role} (from {original_role}) for {delegate_team}' + defaultPermSuffix(metadata);
            }
          },
          'delete_prototype_permission': function(metadata) {
            if (metadata.delegate_user) {
              return 'Delete default permission: {role} for {delegate_user}' + defaultPermSuffix(metadata);
            } else if (metadata.delegate_team) {
              return 'Delete default permission: {role} for {delegate_team}' + defaultPermSuffix(metadata);
            }
          },
          'setup_repo_trigger': function(metadata) {
            var triggerDescription = TriggerService.getDescription(
              metadata['service'], metadata['config']);
            return 'Setup build trigger - ' + triggerDescription;
          },
          'delete_repo_trigger': function(metadata) {
            var triggerDescription = TriggerService.getDescription(
              metadata['service'], metadata['config']);
            return 'Delete build trigger - ' + triggerDescription;
          },
          'toggle_repo_trigger': function(metadata) {
            var triggerDescription = TriggerService.getDescription(
              metadata['service'], metadata['config']);
            if (metadata.enabled) {
              return 'Build trigger enabled - ' + triggerDescription;              
            } else {
              return 'Build trigger disabled - ' + triggerDescription;
            }
          },
          'create_application': 'Create application {application_name} with client ID {client_id}',
          'update_application': 'Update application to {application_name} for client ID {client_id}',
          'delete_application': 'Delete application {application_name} with client ID {client_id}',
          'reset_application_client_secret': 'Reset the Client Secret of application {application_name} ' +
            'with client ID {client_id}',

          'add_repo_notification': function(metadata) {
            var eventData = ExternalNotificationData.getEventInfo(metadata.event);
            return 'Add notification of event "' + eventData['title'] + '" for repository {namespace}/{repo}';
          },

          'delete_repo_notification': function(metadata) {
            var eventData = ExternalNotificationData.getEventInfo(metadata.event);
            return 'Delete notification of event "' + eventData['title'] + '" for repository {namespace}/{repo}';
          },

          'reset_repo_notification': function(metadata) {
            var eventData = ExternalNotificationData.getEventInfo(metadata.event);
            return 'Re-enable notification of event "' + eventData['title'] + '" for repository {namespace}/{repo}';
          },

          'regenerate_robot_token': 'Regenerated token for robot {robot}',

          'service_key_create': function(metadata) {
            if (metadata.preshared) {
              return 'Manual creation of preshared service key {kid} for service {service}';
            } else {
              return 'Creation of service key {kid} for service {service} by {user_agent}';
            }
          },

          'service_key_approve': 'Approval of service key {kid}',
          'service_key_modify': 'Modification of service key {kid}',
          'service_key_delete': 'Deletion of service key {kid}',
          'service_key_extend': 'Change of expiration of service key {kid} from {old_expiration_date} to {expiration_date}',
          'service_key_rotate': 'Automatic rotation of service key {kid} by {user_agent}',

          'take_ownership': function(metadata) {
            if (metadata.was_user) {
              return 'Superuser {superuser} took ownership of user namespace {namespace}';
            } else {
              return 'Superuser {superuser} took ownership of organization {namespace}';
            }
          },

          'manifest_label_add': 'Label {key} added to manifest {manifest_digest} under repository {namespace}/{repo}',
          'manifest_label_delete': 'Label {key} deleted from manifest {manifest_digest} under repository {namespace}/{repo}',

          'change_tag_expiration': function(metadata) {
            if (metadata.expiration_date && metadata.old_expiration_date) {
              return 'Tag {tag} set to expire on {expiration_date} (previously {old_expiration_date})';
            } else if (metadata.expiration_date) {
              return 'Tag {tag} set to expire on {expiration_date}';
            } else if (metadata.old_expiration_date) {
              return 'Tag {tag} set to no longer expire (previously {old_expiration_date})';
            } else {
              return 'Tag {tag} set to no longer expire';
            }
          },

          'create_app_specific_token': 'Created external application token {app_specific_token_title}',
          'revoke_app_specific_token': 'Revoked external application token {app_specific_token_title}',
  
          // Note: These are deprecated.
          'add_repo_webhook': 'Add webhook in repository {repo}',
          'delete_repo_webhook': 'Delete webhook in repository {repo}'
      };

      var logKinds = {
        'account_change_plan': 'Change plan',
        'account_change_cc': 'Update credit card',
        'account_change_password': 'Change password',
        'account_convert': 'Convert account to organization',
        'create_robot': 'Create Robot Account',
        'delete_robot': 'Delete Robot Account',
        'create_repo': 'Create Repository',
        'push_repo': 'Push to repository',
        'repo_verb': 'Pull Repo Verb',
        'pull_repo': 'Pull repository',
        'delete_repo': 'Delete repository',
        'change_repo_permission': 'Change repository permission',
        'delete_repo_permission': 'Remove user permission from repository',
        'change_repo_visibility': 'Change repository visibility',
        'change_repo_trust': 'Change repository trust settings',
        'add_repo_accesstoken': 'Create access token',
        'delete_repo_accesstoken': 'Delete access token',
        'set_repo_description': 'Change repository description',
        'build_dockerfile': 'Build image from Dockerfile',
        'delete_tag': 'Delete Tag',
        'create_tag': 'Create Tag',
        'move_tag': 'Move Tag',
        'revert_tag':'Restore Tag',
        'org_create_team': 'Create team',
        'org_delete_team': 'Delete team',
        'org_add_team_member': 'Add team member',
        'org_invite_team_member': 'Invite team member',
        'org_delete_team_member_invite': 'Rescind team member invitation',
        'org_remove_team_member': 'Remove team member',
        'org_team_member_invite_accepted': 'Team invite accepted',
        'org_team_member_invite_declined': 'Team invite declined',
        'org_set_team_description': 'Change team description',
        'org_set_team_role': 'Change team permission',
        'create_prototype_permission': 'Create default permission',
        'modify_prototype_permission': 'Modify default permission',
        'delete_prototype_permission': 'Delete default permission',
        'setup_repo_trigger': 'Setup build trigger',
        'delete_repo_trigger': 'Delete build trigger',
        'toggle_repo_trigger': 'Enable/disable build trigger',
        'create_application': 'Create Application',
        'update_application': 'Update Application',
        'delete_application': 'Delete Application',
        'reset_application_client_secret': 'Reset Client Secret',
        'add_repo_notification': 'Add repository notification',
        'delete_repo_notification': 'Delete repository notification',
        'reset_repo_notification': 'Re-enable repository notification',
        'regenerate_robot_token': 'Regenerate Robot Token',
        'service_key_create': 'Create Service Key',
        'service_key_approve': 'Approve Service Key',
        'service_key_modify': 'Modify Service Key',
        'service_key_delete': 'Delete Service Key',
        'service_key_extend': 'Extend Service Key Expiration',
        'service_key_rotate': 'Automatic rotation of Service Key',
        'take_ownership': 'Take Namespace Ownership',
        'manifest_label_add': 'Add Manifest Label',
        'manifest_label_delete': 'Delete Manifest Label',
        'change_tag_expiration': 'Change tag expiration',
        'create_app_specific_token': 'Create external app token',
        'revoke_app_specific_token': 'Revoke external app token',
        
        // Note: these are deprecated.
        'add_repo_webhook': 'Add webhook',
        'delete_repo_webhook': 'Delete webhook'
      };

      var getDateString = function(date) {
        return (date.getMonth() + 1) + '/' + date.getDate() + '/' + date.getFullYear();
      };

      var getUrl = function(suffix) {
        var url = UtilService.getRestUrl('user/' + suffix);
        if ($scope.organization) {
          url = UtilService.getRestUrl('organization', $scope.organization.name, suffix);
        }
        if ($scope.repository) {
          url = UtilService.getRestUrl('repository', $scope.repository.namespace, $scope.repository.name, suffix);
        }

        if ($scope.allLogs) {
          url = UtilService.getRestUrl('superuser', suffix)
        }

        url.setQueryParameter('starttime', getDateString($scope.options.logStartDate));
        url.setQueryParameter('endtime', getDateString($scope.options.logEndDate));
        return url;
      };

      var update = function() {
        var hasValidUser = !!$scope.user;
        var hasValidOrg = !!$scope.organization;
        var hasValidRepo = $scope.repository && $scope.repository.namespace;
        var isValidEndpoint = hasValidUser || hasValidOrg || hasValidRepo || $scope.allLogs;

        var hasValidLogStartDate = !!$scope.options.logStartDate;
        var hasValidLogEndDate = !!$scope.options.logEndDate;

        var isValid = isValidEndpoint && hasValidLogStartDate && hasValidLogEndDate;

        if (!$scope.makevisible || !isValid || ($scope.loading && $scope.loadCounter >= 0)) {
          return;
        }

        if (Features.AGGREGATED_LOG_COUNT_RETRIEVAL) {
          $scope.chartLoading = true;

          var aggregateUrl = getUrl('aggregatelogs').toString();
          var loadAggregate = Restangular.one(aggregateUrl);
          loadAggregate.customGET().then(function(resp) {
            $scope.chart = new LogUsageChart(logKinds);
            $($scope.chart).bind('filteringChanged', function(e) {
              $scope.$apply(function() { $scope.kindsAllowed = e.allowed; });
            });

            $scope.chart.draw('bar-chart', resp.aggregated, $scope.options.logStartDate,
                              $scope.options.logEndDate);
            $scope.chartLoading = false;
          });
        }

        $scope.nextPageToken = null;
        $scope.hasAdditional = true;
        $scope.loading = false;
        $scope.logs = [];
        $scope.nextPage();
      };

      $scope.nextPage = function() {
        if ($scope.loading || !$scope.hasAdditional) { return; }

        $scope.loading = true;
        $scope.loadCounter++;

        var currentCounter = $scope.loadCounter;
        var url = getUrl('logs');
        url.setQueryParameter('next_page', $scope.nextPageToken);

        var loadLogs = Restangular.one(url.toString());
        loadLogs.customGET().then(function(resp) {
          if ($scope.loadCounter != currentCounter) {
            // Loaded logs data is out of date.
            return;
          }

          resp.logs.forEach(function(log) {
            $scope.logs.push(log);
          });

          $scope.loading = false;
          $scope.nextPageToken = resp.next_page;
          $scope.hasAdditional = !!resp.next_page;
        });
      };

      $scope.toggleChart = function() {
        $scope.chartVisible = !$scope.chartVisible;
      };

      $scope.isVisible = function(allowed, kind) {
        return allowed == null || allowed.hasOwnProperty(kind);
      };

      $scope.getColor = function(kind, chart) {
        if (!chart) { return ''; }
        return chart.getColor(kind);
      };

      $scope.getDescription = function(log) {
        log.metadata['_ip'] = log.ip ? log.ip : null;

        // Note: This is for back-compat for logs that previously did not have namespaces.
        var namespace = '';
        if (log.namespace) {
          namespace = log.namespace.username || log.namespace.name;
        }

        log.metadata['namespace'] = log.metadata['namespace'] || namespace || '';
        return StringBuilderService.buildString(logDescriptions[log.kind] || log.kind, log.metadata);
      };

      $scope.showExportLogs = function() {
        $scope.exportLogsInfo = {};
      };

      $scope.exportLogs = function(exportLogsInfo, callback) {
        if (!exportLogsInfo.urlOrEmail) {
          callback(false);
          return;
        }

        var exportURL = getUrl('exportlogs').toString();
        var runExport = Restangular.one(exportURL);

        var urlOrEmail = exportLogsInfo.urlOrEmail;
        var data = {};
        if (urlOrEmail.indexOf('http://') == 0 || urlOrEmail.indexOf('https://') == 0) {
          data['callback_url'] = urlOrEmail;
        } else {
          data['callback_email'] = urlOrEmail;
        }

        runExport.customPOST(data).then(function(resp) {
          bootbox.alert('Usage logs export queued with ID `' + resp['export_id'] + '`')
          callback(true);
        }, ApiService.errorDisplay('Could not start logs export', callback));
      };

      $scope.$watch('organization', update);
      $scope.$watch('user', update);
      $scope.$watch('repository', update);
      $scope.$watch('makevisible', update);

      $scope.$watch('options.logStartDate', update);
      $scope.$watch('options.logEndDate', update);
    }
  };

  return directiveDefinitionObject;
});