package org.xcolab.view.errors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import org.xcolab.client.members.pojo.Member;
import org.xcolab.view.auth.MemberAuthUtil;
import org.xcolab.view.util.entity.email.EmailToAdminDispatcher;

import java.io.IOException;
import java.net.URLDecoder;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Collectors;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Controller
public class ErrorReportingController {

    private static final String MESSAGE_BODY_HEADER_FORMAT_STRING =
            "<p><strong>An exception occurred at:</strong><br>%s</p>"
                    + "<p><strong>Error:</strong><br/>%d - %s<br/>Message: %s</p>"
                    + "<p><strong>User Agent:</strong><br/>%s</p>"
                    + "<p><strong>Cookies:</strong><br/>%s</p>"
                    + "<p><strong>Session info:</strong><br/>%s</p>"
                    + "<p><strong>Referer:</strong><br/>%s</p>"
                    + "<p><strong>Message from user (%s):</strong><br/>" + "%s</p>";

    private static final String MESSAGE_BODY_EMAIL_FORMAT_STRING =
            "<p><strong>Please notify user once we have a fix for the bug:</strong><br/>%s</p>";

    private static final String EMAIL_SUBJECT = "Error Report from User";

    @PostMapping("/reportError")
    public void reportError(HttpServletRequest request, HttpServletResponse response,
            @RequestParam String url, @RequestParam String email, @RequestParam String description,
            @RequestParam Integer status, @RequestParam String error, @RequestParam String message,
            @RequestParam String stackTrace, @RequestParam String referer) throws IOException {

        final String userAgent = request.getHeader(HttpHeaders.USER_AGENT);
        final Cookie[] cookies = request.getCookies();
        final String cookieNames = Arrays.stream(cookies)
            .map(Cookie::getName)
            .collect(Collectors.joining(", "));

        final HttpSession session = request.getSession(false);
        String sessionInfo;
        if (session == null) {
            sessionInfo = "No session found";
        } else {
            final String sessionAttributeNames = Collections.list(session.getAttributeNames())
                    .stream().collect(Collectors.joining(", "));
            sessionInfo = String.format("ID: %s,<br/>"
                            + "Created: %s,<br/>"
                            + "Last accessed: %s,<br/>"
                            + "Attribute names: %s",
                    session.getId().substring(0, 7) + "...",
                    Instant.ofEpochMilli(session.getCreationTime()).toString(),
                    Instant.ofEpochMilli(session.getLastAccessedTime()).toString(),
                    sessionAttributeNames);
        }

        if (!stackTrace.equals("${exception.stackTrace}")) {
            String userScreenName = "no user was logged in";
            final Member member = MemberAuthUtil.getMemberOrNull(request);
            if (member != null) {
                userScreenName = member.getScreenName();

                if (email.isEmpty()) {
                    email = member.getEmailAddress();
                }
            }

            if (StringUtils.isNotEmpty(url)) {
                final String userAgentString = userAgent != null ? userAgent : "Unknown";
                StringBuilder messageBodyBuilder = new StringBuilder();

                String descriptionInHtmlFormat = description.replaceAll("(\r\n|\n)", "<br />");
                messageBodyBuilder.append(String.format(MESSAGE_BODY_HEADER_FORMAT_STRING, url,
                        status, error, message, userAgentString, cookieNames, sessionInfo, referer,
                        userScreenName, descriptionInHtmlFormat));

                if (StringUtils.isNotEmpty(email)) {
                    messageBodyBuilder.append(String.format(MESSAGE_BODY_EMAIL_FORMAT_STRING, email));
                }

                //TODO COLAB-2841: Remove debug logging
                //Add debug info for invalid CSRF token errors
                final String csrfCookieValue = Arrays.stream(cookies)
                        .filter(cookie -> cookie.getName().equals("XSRF-TOKEN"))
                        .map(cookie -> StringUtils.substring(cookie.getValue(), 0, 7))
                        .findAny().orElse("no cookie found");
                final String csrfParameterValue = StringUtils.substring(
                        request.getParameter("_csrf"), 0, 7);
                messageBodyBuilder.append("<p><strong>CSRF debug info:</strong><br/>")
                        .append("Cookie value: ").append(csrfCookieValue).append("<br/>")
                        .append("Parameter value: ").append(csrfParameterValue).append("</p>");

                messageBodyBuilder.append(URLDecoder.decode(stackTrace, "UTF-8"));

                new EmailToAdminDispatcher(EMAIL_SUBJECT, messageBodyBuilder.toString()).sendMessage();
            }
        }
        response.sendRedirect("/");
    }
}

@@ -286,16 +286,62 @@
 stParam;
+%0Aimport org.springframework.web.util.WebUtils;
 %0A%0Aimport
@@ -1164,32 +1164,98 @@
 ng%3E%3Cbr/%3E%25s%3C/p%3E%22%0A
+                    + %22%3Cp%3E%3Cstrong%3ETracking UUID:%3C/strong%3E %25s%3C/p%3E%22%0A
                 
@@ -2445,24 +2445,243 @@
 ng(%22, %22));%0A%0A
+        final Cookie trackingCookie = WebUtils.getCookie(request, %22userTrackingUuid%22);%0A        final String trackingCookieValue = trackingCookie != null ? trackingCookie.getValue()%0A                : %22no cookie found%22;%0A%0A
         fina
@@ -4393,41 +4393,40 @@
 ng, 
-cookieNames, sessionInfo, referer
+trackingCookieValue, cookieNames
 ,%0A  
@@ -4438,32 +4438,54 @@
                 
+ sessionInfo, referer,
  userScreenName,

Add tracking cookie to error reports