/*******************************************************************************
 * Copyright 2018 The MIT Internet Trust Consortium
 *
 * 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.
 *******************************************************************************/

var AccessTokenModel = Backbone.Model.extend({
  idAttribute: "id",

  defaults: {
    id: null,
    value: null,
    refreshTokenId: null,
    scopes: [],
    clientId: null,
    userId: null,
    expiration: null,
  },

  urlRoot: "api/tokens/access",
});

var AccessTokenCollection = Backbone.Collection.extend({
  idAttribute: "id",

  model: AccessTokenModel,

  url: "api/tokens/access",
});

var AccessTokenView = Backbone.View.extend({
  tagName: "tr",

  initialize: function (options) {
    this.options = options;

    if (!this.template) {
      this.template = _.template($("#tmpl-access-token").html());
    }

    if (!this.scopeTemplate) {
      this.scopeTemplate = _.template($("#tmpl-scope-list").html());
    }

    if (!this.moreInfoTemplate) {
      this.moreInfoTemplate = _.template(
        $("#tmpl-client-more-info-block").html(),
      );
    }

    this.model.bind("change", this.render, this);
  },

  events: {
    "click .btn-delete": "deleteToken",
    "click .token-substring": "showTokenValue",
    "click .toggleMoreInformation": "toggleMoreInformation",
  },

  render: function (eventName) {
    var expirationDate = this.model.get("expiration");

    if (expirationDate == null) {
      expirationDate = "Never";
    } else if (!moment(expirationDate).isValid()) {
      expirationDate = "Unknown";
    } else {
      expirationDate = moment(expirationDate).calendar();
    }

    var json = {
      token: this.model.toJSON(),
      client: this.options.client.toJSON(),
      formattedExpiration: expirationDate,
    };

    this.$el.html(this.template(json));

    // hide full value
    $(".token-full", this.el).hide();

    // show scopes
    $(".scope-list", this.el).html(
      this.scopeTemplate({
        scopes: this.model.get("scopes"),
        systemScopes: this.options.systemScopeList,
      }),
    );

    $(".client-more-info-block", this.el).html(
      this.moreInfoTemplate({
        client: this.options.client.toJSON(),
      }),
    );

    $(this.el).i18n();
    return this;
  },

  deleteToken: function (e) {
    e.preventDefault();

    if (confirm($.t("token.token-table.confirm"))) {
      var _self = this;

      this.model.destroy({
        dataType: false,
        processData: false,
        success: function () {
          _self.$el.fadeTo("fast", 0.0, function () {
            // fade
            $(this).slideUp("fast", function () {
              // slide up
              $(this).remove(); // then remove from the DOM
              // refresh the table in case we removed an id token,
              // too
              _self.parentView.refreshTable();
            });
          });
        },
        error: app.errorHandlerView.handleError(),
      });

      this.parentView.delegateEvents();
    }

    return false;
  },

  toggleMoreInformation: function (e) {
    e.preventDefault();
    if ($(".moreInformation", this.el).is(":visible")) {
      // hide it
      $(".moreInformation", this.el).hide("fast");
      $(".toggleMoreInformation i", this.el).attr(
        "class",
        "icon-chevron-right",
      );
      $(".moreInformationContainer", this.el)
        .removeClass("alert")
        .removeClass("alert-info")
        .addClass("muted");
    } else {
      // show it
      $(".moreInformation", this.el).show("fast");
      $(".toggleMoreInformation i", this.el).attr("class", "icon-chevron-down");
      $(".moreInformationContainer", this.el)
        .addClass("alert")
        .addClass("alert-info")
        .removeClass("muted");
    }
  },

  close: function () {
    $(this.el).unbind();
    $(this.el).empty();
  },

  showTokenValue: function (e) {
    e.preventDefault();
    $(".token-substring", this.el).hide();
    $(".token-full", this.el).show();
  },
});

var RefreshTokenModel = Backbone.Model.extend({
  idAttribute: "id",

  defaults: {
    id: null,
    value: null,
    scopes: [],
    clientId: null,
    userId: null,
    expiration: null,
  },

  urlRoot: "api/tokens/refresh",
});

var RefreshTokenCollection = Backbone.Collection.extend({
  idAttribute: "id",

  model: RefreshTokenModel,

  url: "api/tokens/refresh",
});

var RefreshTokenView = Backbone.View.extend({
  tagName: "tr",

  initialize: function (options) {
    this.options = options;

    if (!this.template) {
      this.template = _.template($("#tmpl-refresh-token").html());
    }

    if (!this.scopeTemplate) {
      this.scopeTemplate = _.template($("#tmpl-scope-list").html());
    }

    if (!this.moreInfoTemplate) {
      this.moreInfoTemplate = _.template(
        $("#tmpl-client-more-info-block").html(),
      );
    }

    this.model.bind("change", this.render, this);
  },

  events: {
    "click .btn-delete": "deleteToken",
    "click .token-substring": "showTokenValue",
    "click .toggleMoreInformation": "toggleMoreInformation",
  },

  render: function (eventName) {
    var expirationDate = this.model.get("expiration");

    if (expirationDate == null) {
      expirationDate = "Never";
    } else if (!moment(expirationDate).isValid()) {
      expirationDate = "Unknown";
    } else {
      expirationDate = moment(expirationDate).calendar();
    }

    var json = {
      token: this.model.toJSON(),
      client: this.options.client.toJSON(),
      formattedExpiration: expirationDate,
      accessTokenCount: this.options.accessTokenCount,
    };

    this.$el.html(this.template(json));

    // hide full value
    $(".token-full", this.el).hide();

    // show scopes
    $(".scope-list", this.el).html(
      this.scopeTemplate({
        scopes: this.model.get("scopes"),
        systemScopes: this.options.systemScopeList,
      }),
    );

    $(".client-more-info-block", this.el).html(
      this.moreInfoTemplate({
        client: this.options.client.toJSON(),
      }),
    );

    $(this.el).i18n();
    return this;
  },

  deleteToken: function (e) {
    e.preventDefault();

    if (confirm($.t("token.token-table.confirm-refresh"))) {
      var _self = this;

      this.model.destroy({
        dataType: false,
        processData: false,
        success: function () {
          _self.$el.fadeTo("fast", 0.0, function () {
            // fade
            $(this).slideUp("fast", function () {
              // slide up
              $(this).remove(); // then remove from the DOM
              // refresh the table in case the access tokens have
              // changed, too
              _self.parentView.refreshTable();
            });
          });
        },
        error: app.errorHandlerView.handleError(),
      });

      _self.parentView.delegateEvents();
    }

    return false;
  },

  toggleMoreInformation: function (e) {
    e.preventDefault();
    if ($(".moreInformation", this.el).is(":visible")) {
      // hide it
      $(".moreInformation", this.el).hide("fast");
      $(".toggleMoreInformation i", this.el).attr(
        "class",
        "icon-chevron-right",
      );
      $(".moreInformationContainer", this.el)
        .removeClass("alert")
        .removeClass("alert-info")
        .addClass("muted");
    } else {
      // show it
      $(".moreInformation", this.el).show("fast");
      $(".toggleMoreInformation i", this.el).attr("class", "icon-chevron-down");
      $(".moreInformationContainer", this.el)
        .addClass("alert")
        .addClass("alert-info")
        .removeClass("muted");
    }
  },

  close: function () {
    $(this.el).unbind();
    $(this.el).empty();
  },

  showTokenValue: function (e) {
    e.preventDefault();
    $(".token-substring", this.el).hide();
    $(".token-full", this.el).show();
  },
});

var TokenListView = Backbone.View.extend({
  tagName: "span",

  initialize: function (options) {
    this.options = options;
  },

  events: {
    "click .refresh-table": "refreshTable",
    "page .paginator-access": "changePageAccess",
    "page .paginator-refresh": "changePageRefresh",
  },

  load: function (callback) {
    if (
      this.model.access.isFetched &&
      this.model.refresh.isFetched &&
      this.options.clientList.isFetched &&
      this.options.systemScopeList.isFetched
    ) {
      callback();
      return;
    }

    $("#loadingbox").sheet("show");
    $("#loading").html(
      '<span class="label" id="loading-access">' +
        $.t("token.token-table.access-tokens") +
        "</span> " +
        '<span class="label" id="loading-refresh">' +
        $.t("token.token-table.refresh-tokens") +
        "</span> " +
        '<span class="label" id="loading-clients">' +
        $.t("common.clients") +
        "</span> " +
        '<span class="label" id="loading-scopes">' +
        $.t("common.scopes") +
        "</span> ",
    );

    $.when(
      this.model.access.fetchIfNeeded({
        success: function (e) {
          $("#loading-access").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
      this.model.refresh.fetchIfNeeded({
        success: function (e) {
          $("#loading-refresh").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
      this.options.clientList.fetchIfNeeded({
        success: function (e) {
          $("#loading-clients").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
      this.options.systemScopeList.fetchIfNeeded({
        success: function (e) {
          $("#loading-scopes").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
    ).done(function () {
      $("#loadingbox").sheet("hide");
      callback();
    });
  },

  changePageAccess: function (event, num) {
    $(".paginator-access", this.el).bootpag({
      page: num,
    });
    $("#access-token-table tbody tr", this.el).each(function (index, element) {
      if (Math.ceil((index + 1) / 10) != num) {
        $(element).hide();
      } else {
        $(element).show();
      }
    });
  },

  changePageRefresh: function (event, num) {
    $(".paginator-refresh", this.el).bootpag({
      page: num,
    });
    $("#refresh-token-table tbody tr", this.el).each(function (index, element) {
      if (Math.ceil((index + 1) / 10) != num) {
        $(element).hide();
      } else {
        $(element).show();
      }
    });
  },

  refreshTable: function (e) {
    $("#loadingbox").sheet("show");
    $("#loading").html(
      '<span class="label" id="loading-access">' +
        $.t("token.token-table.access-tokens") +
        "</span> " +
        '<span class="label" id="loading-refresh">' +
        $.t("token.token-table.refresh-tokens") +
        "</span> " +
        '<span class="label" id="loading-clients">' +
        $.t("common.clients") +
        "</span> " +
        '<span class="label" id="loading-scopes">' +
        $.t("common.scopes") +
        "</span> ",
    );
    var _self = this;
    $.when(
      this.model.access.fetch({
        success: function (e) {
          $("#loading-access").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
      this.model.refresh.fetch({
        success: function (e) {
          $("#loading-refresh").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
      this.options.clientList.fetch({
        success: function (e) {
          $("#loading-clients").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
      this.options.systemScopeList.fetch({
        success: function (e) {
          $("#loading-scopes").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
    ).done(function () {
      _self.render();
      $("#loadingbox").sheet("hide");
    });
  },

  togglePlaceholder: function () {
    if (this.model.access.length > 0) {
      $("#access-token-table", this.el).show();
      $("#access-token-table-empty", this.el).hide();
    } else {
      $("#access-token-table", this.el).hide();
      $("#access-token-table-empty", this.el).show();
    }
    if (this.model.refresh.length > 0) {
      $("#refresh-token-table", this.el).show();
      $("#refresh-token-table-empty", this.el).hide();
    } else {
      $("#refresh-token-table", this.el).hide();
      $("#refresh-token-table-empty", this.el).show();
    }

    $("#access-token-count", this.el).html(this.model.access.length);
    $("#refresh-token-count", this.el).html(this.model.refresh.length);
  },

  render: function (eventName) {
    // append and render the table structure
    $(this.el).html($("#tmpl-token-table").html());

    var _self = this;

    // set up pagination
    var numPagesAccess = Math.ceil(this.model.access.length / 10);
    if (numPagesAccess > 1) {
      $(".paginator-access", this.el).show();
      $(".paginator-access", this.el).bootpag({
        total: numPagesAccess,
        page: 1,
      });
    } else {
      $(".paginator-access", this.el).hide();
    }

    // count up refresh tokens
    var refreshCount = {};

    _.each(this.model.access.models, function (token, index) {
      // look up client
      var client = _self.options.clientList.getByClientId(
        token.get("clientId"),
      );
      var view = new AccessTokenView({
        model: token,
        client: client,
        systemScopeList: _self.options.systemScopeList,
      });
      view.parentView = _self;
      var element = view.render().el;
      $("#access-token-table", _self.el).append(element);
      if (Math.ceil((index + 1) / 10) != 1) {
        $(element).hide();
      }

      // console.log(token.get('refreshTokenId'));
      var refId = token.get("refreshTokenId");
      if (refId != null) {
        if (refreshCount[refId]) {
          refreshCount[refId] += 1;
        } else {
          refreshCount[refId] = 1;
        }
      }
    });

    // console.log(refreshCount);

    // set up pagination
    var numPagesRefresh = Math.ceil(this.model.refresh.length / 10);
    if (numPagesRefresh > 1) {
      $(".paginator-refresh", this.el).show();
      $(".paginator-refresh", this.el).bootpag({
        total: numPagesRefresh,
        page: 1,
      });
    } else {
      $(".paginator-refresh", this.el).hide();
    }

    _.each(this.model.refresh.models, function (token, index) {
      // look up client
      var client = _self.options.clientList.getByClientId(
        token.get("clientId"),
      );
      var view = new RefreshTokenView({
        model: token,
        client: client,
        systemScopeList: _self.options.systemScopeList,
        accessTokenCount: refreshCount[token.get("id")],
      });
      view.parentView = _self;
      var element = view.render().el;
      $("#refresh-token-table", _self.el).append(element);
      if (Math.ceil((index + 1) / 10) != 1) {
        $(element).hide();
      }
    });

    /*
     * _.each(this.model.models, function (scope) { $("#scope-table",
     * this.el).append(new SystemScopeView({model: scope}).render().el); },
     * this);
     */

    this.togglePlaceholder();
    $(this.el).i18n();
    return this;
  },
});

ui.routes.push({
  path: "user/tokens",
  name: "tokens",
  callback: function () {
    this.breadCrumbView.collection.reset();
    this.breadCrumbView.collection.add([
      {
        text: $.t("admin.home"),
        href: "",
      },
      {
        text: $.t("token.manage"),
        href: "manage/#user/tokens",
      },
    ]);

    this.updateSidebar("user/tokens");

    var view = new TokenListView({
      model: {
        access: this.accessTokensList,
        refresh: this.refreshTokensList,
      },
      clientList: this.clientList,
      systemScopeList: this.systemScopeList,
    });

    view.load(function (collection, response, options) {
      $("#content").html(view.render().el);
      setPageTitle($.t("token.manage"));
    });
  },
});

ui.templates.push("resources/template/token.html");

ui.init.push(function (app) {
  app.accessTokensList = new AccessTokenCollection();
  app.refreshTokensList = new RefreshTokenCollection();
});
