// Copyright 2017 Google Inc.
//
// 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.

package com.google.webauthn.gaedemo.servlets;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.DatatypeConverter;

import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.common.io.BaseEncoding;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.webauthn.gaedemo.exceptions.ResponseException;
import com.google.webauthn.gaedemo.objects.AuthenticatorAssertionResponse;
import com.google.webauthn.gaedemo.objects.PublicKeyCredential;
import com.google.webauthn.gaedemo.server.PublicKeyCredentialResponse;
import com.google.webauthn.gaedemo.server.Server;
import com.google.webauthn.gaedemo.storage.Credential;


public class FinishGetAssertion extends HttpServlet {
  private static final int FINGERPRINT = 2;
  private static final int SCREEN_LOCK = 134;
  private static final long serialVersionUID = 1L;
  private final UserService userService = UserServiceFactory.getUserService();


  public FinishGetAssertion() {

  }

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    doPost(request, response);
  }

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    String currentUser = userService.getCurrentUser().getEmail();
    String data = request.getParameter("data");
    if (data == null) {
      data = "";
    }
    String session = request.getParameter("session");
    if (session == null) {
      session = "";
    }

    String credentialId = null;
    String type = null;
    String uvm = null;
    JsonElement assertionJson = null;

    try {
      JsonObject json = new JsonParser().parse(data).getAsJsonObject();
      JsonElement idJson = json.get("id");
      if (idJson != null) {
        credentialId = idJson.getAsString();
      }
      JsonElement typeJson = json.get("type");
      if (typeJson != null) {
        type = typeJson.getAsString();
      }
      JsonElement uvmJson = json.get("uvm");
      if (uvmJson != null && uvmJson.isJsonArray()) {
        JsonArray uvmArray = uvmJson.getAsJsonArray();
        if (uvmJson.isJsonArray()) {
          JsonElement uvmElement = uvmArray.get(0);
          if(uvmElement != null) {
            switch (uvmElement.getAsJsonObject().get("userVerificationMethod").getAsInt()){
              case FINGERPRINT:
                uvm = "Fingerprint";
                break;
              case SCREEN_LOCK:
                uvm = "Screen Lock";
                break;
              default:
                uvm = "Others";
                break;
            }
          }
        }
      }
      assertionJson = json.get("response");
      if (assertionJson == null) {
        throw new ServletException("Missing element 'response'");
      }
    } catch (IllegalStateException e) {
      throw new ServletException("Passed data not a json object");
    } catch (ClassCastException e) {
      throw new ServletException("Invalid input");
    } catch (JsonParseException e) {
      throw new ServletException("Input not valid json");
    }

    AuthenticatorAssertionResponse assertion = null;
    try {
      assertion = new AuthenticatorAssertionResponse(assertionJson);
    } catch (ResponseException e) {
      throw new ServletException(e.toString());
    }

    // Recoding of credential ID is needed, because the ID from HTTP servlet request doesn't support
    // padding.
    String credentialIdRecoded = BaseEncoding.base64Url().encode(
        BaseEncoding.base64Url().decode(credentialId));
    PublicKeyCredential cred = new PublicKeyCredential(credentialIdRecoded, type,
        BaseEncoding.base64Url().decode(credentialId), assertion);

    Credential savedCredential;
    try {
      savedCredential = Server.validateAndFindCredential(cred, currentUser, session);
    } catch (ResponseException e) {
      throw new ServletException("Unable to validate assertion", e);
    }

    Server.verifyAssertion(cred, currentUser, session, savedCredential);
    savedCredential.setUserVerificationMethod(uvm);
    savedCredential.save(currentUser);

    response.setContentType("application/json");
    String handle = DatatypeConverter.printHexBinary(savedCredential.getCredential().rawId);
    PublicKeyCredentialResponse rsp =
        new PublicKeyCredentialResponse(true, "Successful assertion", handle);
    response.getWriter().println(rsp.toJson());
  }
}