package leigh.esp.kensho;

import jakarta.servlet.http.Cookie;
import leigh.esp.keikai.sdk.KeikaiClient;
import leigh.mecha.fabric.HandlerStatus;
import leigh.mecha.http.server.PrefixedHandler;
import leigh.mecha.http.server.WebTransaction;
import leigh.mecha.http.velocity.VelocityHttpProcessor;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.VelocityContext;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;

/**
 * Kensho is an authentication & user portal microservice for ESP. It is a re-imagining of the previous
 * ESP authentication service which was built into the Engine. The primary purpose of the
 * service is to facilitate management of the ESP STK and the ESP Username parameters.
 * <p>
 * Login path: /kensho/v1/identifier
 *
 * @author Alex Leigh
 */
public class KenshoService extends PrefixedHandler {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(KenshoService.class);
    public static String PROP_DB_HOSTNAME = "kensho.auth.db.hostname";
    public static String PROP_DB_USERNAME = "kensho.auth.db.username";
    public static String PROP_DB_PASSWORD = "kensho.auth.db.password";
    public static String PROP_DB_DATABASE = "kensho.auth.db.database";
    public static String VC_PRIOR_USERNAME = "priorIdentifier";
    public static String COOKIE_STK = "__esp_stk";
    public static String COOKIER_USERNAME = "__esp_identity";
    public static String KEY_IDENTIFIER = "identifier";
    private final VelocityHttpProcessor velocity = new VelocityHttpProcessor();

    public KenshoService(final Set<String> pathPrefixes, Properties properties) {
        super(pathPrefixes);
        String hostname = properties.getProperty(PROP_DB_HOSTNAME);
        String username = properties.getProperty(PROP_DB_USERNAME);
        String password = properties.getProperty(PROP_DB_PASSWORD);
        String database = properties.getProperty(PROP_DB_DATABASE);

        logger.info("Database: [host: {}] [username: {}] [schema: {}]", hostname, username, database);

        KeikaiClient keikaiClient = new KeikaiClient("http://localhost:8123",
                "/kensho/v1/",
                "http://localhost:8124/kensho/");

        new Thread(keikaiClient).start();
    }

    public HandlerStatus velocityTest(WebTransaction request) throws IOException {
        VelocityContext vc = new VelocityContext();
        vc.put("now", Instant.now());
        velocity.process("leigh/esp/kensho/test.vm", vc, request);

        return HandlerStatus.BREAK;
    }

    @Override
    public HandlerStatus handlePrefixedWebRequest(WebTransaction request) throws IOException {
        logger.info("Kensho service called. [pathInfo: {}] [cookies: {}]", request.getPathInfo());

        try {
            String path = request.getPathInfo();
            switch (path) {
                case "/test":
                    return velocityTest(request);
                case "/identity":
                    return identityPage(request);
                case "/success":
                    request.httpServletResponse.setContentType("text/html");
                    request.httpServletResponse.getOutputStream().write("OK".getBytes(StandardCharsets.UTF_8));
                    return HandlerStatus.BREAK;
            }

            // Fall through and try to load resource-based HTML content

            try (InputStream is = KenshoService.class.getResourceAsStream("/leigh/esp/kensho/html" + path)) {
                if (is != null) {
                    // TODO Set the content type correctly
                    IOUtils.copy(is, request.httpServletResponse.getOutputStream());
                    return HandlerStatus.BREAK;
                }
            }

            request.httpServletResponse.setStatus(404);
            request.httpServletResponse.getOutputStream().write("404".getBytes(StandardCharsets.UTF_8));
        } catch (Throwable t) {
            t.printStackTrace();
            throw t;
        }
        return HandlerStatus.BREAK;
    }

    public HandlerStatus identityPage(WebTransaction request) throws IOException {
        String filename = FilenameUtils.getName(request.getPathInfo());
        logger.info("Filename: {}", filename);

        String identifier = request.httpServletRequest.getParameter(KEY_IDENTIFIER);
        String password = request.httpServletRequest.getParameter("password");

        logger.info("Got login details. [id: {}] [pw: {}]", identifier, password);

        if (identifier != null && password != null) {
            if (identifier.equals("demo") && password.equals("demo")) {
                Cookie stk = new Cookie(COOKIE_STK, createSessionToken().toString());
                Cookie user = new Cookie(COOKIER_USERNAME, identifier);
                stk.setPath("/");
                stk.setMaxAge(356 * 86400);

                user.setPath("/");
                user.setMaxAge(356 * 86400);

                request.httpServletResponse.addCookie(stk);
                request.httpServletResponse.addCookie(user);

                // MASS-62 We can't use sendRedirect() because it will build a FQDN request
                // request.httpServletResponse.sendRedirect("v1/success");
                request.httpServletResponse.setStatus(302);
                request.httpServletResponse.setHeader("location", "success");

                return HandlerStatus.BREAK;
                // TODO: Create session ID, and write it back as cookie
            }
            logger.info("Processing login. [un: {}] [pw: {}]", identifier, password);
        }

        VelocityContext vc = new VelocityContext();
        if (identifier != null) {
            vc.put(VC_PRIOR_USERNAME, identifier);
        } else {
            Cookie user = request.getCookie(COOKIER_USERNAME);
            if (user != null) vc.put(VC_PRIOR_USERNAME, user.getValue());
        }
        request.httpServletResponse.setContentType("text/html");
        velocity.process("leigh/esp/kensho/identity.vm", vc, request);

        return HandlerStatus.BREAK;
    }

    public UUID createSessionToken() {
        return UUID.randomUUID();
    }
}
