package de.tum.in.www1.exerciseapp.service;

import de.tum.in.www1.exerciseapp.domain.Exercise;
import de.tum.in.www1.exerciseapp.domain.User;
import de.tum.in.www1.exerciseapp.exception.JiraException;
import de.tum.in.www1.exerciseapp.repository.UserRepository;
import de.tum.in.www1.exerciseapp.security.JiraAuthenticationProvider;
import de.tum.in.www1.exerciseapp.security.SecurityUtils;
import de.tum.in.www1.exerciseapp.web.rest.dto.LtiLaunchRequestDTO;
import org.apache.commons.lang.RandomStringUtils;
import org.imsglobal.lti.launch.LtiOauthVerifier;
import org.imsglobal.lti.launch.LtiVerificationException;
import org.imsglobal.lti.launch.LtiVerificationResult;
import org.imsglobal.lti.launch.LtiVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;


@Service
@Transactional
public class LtiService {

    private final Logger log = LoggerFactory.getLogger(LtiService.class);


    @Value("${exerciseapp.lti.oauth-key}")
    private String OAUTH_KEY;

    @Value("${exerciseapp.lti.oauth-secret}")
    private String OAUTH_SECRET;

    @Value("${exerciseapp.lti.create-user-prefix}")
    private String CREATE_USER_PREFIX = "";

    @Inject
    private JiraService jiraService;


    @Inject
    private UserService userService;

    @Inject
    private UserRepository userRepository;


    @Inject
    private JiraAuthenticationProvider jiraAuthenticationProvider;

    @Autowired
    AuthenticationSuccessHandler successHandler;

    /**
     * Handles LTI launch requests. Therefore it creates an user, and signs in if necessary.
     *
     * @param launchRequest The launch request, sent by LTI consumer
     * @param exercise      Exercise to launch
     * @throws JiraException
     * @throws AuthenticationException
     */
    public void handleLaunchRequest(LtiLaunchRequestDTO launchRequest, Exercise exercise) throws JiraException, AuthenticationException {

        String username = (this.CREATE_USER_PREFIX + launchRequest.getLis_person_sourcedid());
        String password;

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (auth instanceof AnonymousAuthenticationToken) {
            // currently not logged in

            // check if user already exists
            Optional<String> existingPassword = userService.decryptPasswordByLogin(username);
            if (existingPassword.isPresent()) {

                password = existingPassword.get();

                log.debug("User {} already exists, signing in", username);

            } else {
                // user needs to be created

                password = RandomStringUtils.randomAlphanumeric(10);

                jiraService.createUser(username,
                    password,
                    launchRequest.getLis_person_contact_email_primary(),
                    launchRequest.getLis_person_sourcedid());

                log.debug("Created user {} on JIRA, signing in", username);

            }

            // Authenticate, which will create the local user
            Authentication token = jiraAuthenticationProvider.authenticate(new UsernamePasswordAuthenticationToken(username, password));
            SecurityContextHolder.getContext().setAuthentication(token);

            // Save password if the user was newly created
            if (!existingPassword.isPresent()) {
                userService.changePasswordByLogin(username, password);
            }
        } else {
            log.debug("User already signed in");
        }


        // Make sure user is added to group for this exercise
        User user  =userRepository.findOneByLogin(username).get();

        if(user.getLogin().equals(username)) {
            String courseGroup = exercise.getCourse().getStudentGroupName();

            if(!user.getGroups().contains(courseGroup)) {
                jiraService.addUserToGroup(username, exercise.getCourse().getStudentGroupName());
                List<String> groups = user.getGroups();
                groups.add(courseGroup);
                user.setGroups(groups);
                userRepository.save(user);
            }
        }


    }

    /**
     * Checks if a LTI request is correctly signed via OAuth with the secret
     *
     * @param request The request to check
     * @return True if the request is valid, otherwise false
     */
    public Boolean verifyRequest(HttpServletRequest request) {

        LtiVerifier ltiVerifier = new LtiOauthVerifier();
        Boolean success = false;
        try {
            LtiVerificationResult ltiResult = ltiVerifier.verify(request, this.OAUTH_SECRET);
            success = ltiResult.getSuccess();
            if (!success) {
                log.error("Lti signature verification failed: " + ltiResult.getMessage());
            }
        } catch (LtiVerificationException e) {
            log.error("Lti signature verification failed. ", e);
        }
        return success;

    }


}

@@ -2048,15 +2048,8 @@
 lti.
-create-
 user
@@ -2078,23 +2078,16 @@
  String 
-CREATE_
 USER_PRE
@@ -2097,16 +2097,110 @@
  = %22%22;%0A%0A
+    @Value(%22$%7Bexerciseapp.lti.user-group-name%7D%22)%0A    private String USER_GROUP_NAME = %22lti%22;%0A%0A
     @Inj
@@ -2969,15 +2969,8 @@
 his.
-CREATE_
 USER
@@ -3896,24 +3896,96 @@
 rcedid());%0A%0A
+                jiraService.addUserToGroup(username, USER_GROUP_NAME);%0A%0A
             
@@ -4978,50 +4978,19 @@
 me, 
-exercise.getCourse().getStudentGroupName()
+courseGroup
 );%0A 

add all created LTI users to one common group