package login

import (
	"bytes"
	"fmt"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	loginScriptName = "login.js"
	jwtTokenPrefix  = "JWT Token:"
	redactedToken   = "[REDACTED]"
)

func Login(loginInCI *bool, url, user, password string) (string, error) {
	cmd, message, err := loginCmd(loginInCI, url, user, password)
	fmt.Println(message)
	if err != nil {
		return "", fmt.Errorf("failed to prepare docker login helper: %w", err)
	}
	return runLoginCmd(cmd)
}

func loginCmd(loginInCI *bool, url, user, password string) (*exec.Cmd, string, error) {
	message := "Login by running a Docker container in your local machine..."
	if loginInCI != nil && *loginInCI {
		message = "Login by running a Docker container in the remote CI machine..."
	}

	cmd, err := dockerCmd(url, user, password)
	return cmd, message, err
}

func dockerCmd(url, user, password string) (*exec.Cmd, error) {
	loginScriptPath, err := localLoginScriptPath()
	if err != nil {
		return nil, err
	}

	loginScriptMount := fmt.Sprintf("%s:/home/pptruser/%s:ro", loginScriptPath, loginScriptName)
	return exec.Command(
		"docker", "run",
		"--init",
		"--cap-add=SYS_ADMIN",
		"--rm",
		"--entrypoint", "node",
		"--volume", loginScriptMount,
		"ghcr.io/puppeteer/puppeteer:16.1.0",
		"/home/pptruser/"+loginScriptName, url, user, password, "./",
	), nil
}

func localLoginScriptPath() (string, error) {
	_, sourcePath, _, ok := runtime.Caller(0)
	if !ok {
		return "", fmt.Errorf("unable to locate login helper")
	}
	return filepath.Abs(filepath.Join(filepath.Dir(sourcePath), loginScriptName))
}

func runLoginCmd(cmd *exec.Cmd) (string, error) {
	// Capture the output, token will be in stdout, debug & error message should be in stderr
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	// Execute the command
	err := cmd.Run()
	fmt.Println("Standard Out of login.js:")
	fmt.Println(redactTokenOutput(stdout.String()))
	fmt.Println("Standard Error of login.js:")
	fmt.Println(stderr.String())

	if err != nil {
		return "", fmt.Errorf("failed to execute login helper: %w", err)
	}

	if token := extractToken(stdout.String()); token != "" {
		return token, nil
	}

	return "", fmt.Errorf("login helper returned no JWT token")
}

func extractToken(output string) string {
	for _, line := range strings.Split(output, "\n") {
		line = strings.TrimSpace(line)
		if token, ok := strings.CutPrefix(line, jwtTokenPrefix); ok {
			if token = strings.TrimSpace(token); token != "" {
				return token
			}
		}
	}
	return ""
}

func redactTokenOutput(output string) string {
	lines := strings.Split(output, "\n")
	for i, line := range lines {
		trimmedLine := strings.TrimSpace(line)
		if _, ok := strings.CutPrefix(trimmedLine, jwtTokenPrefix); !ok {
			continue
		}

		leadingWhitespace := line[:len(line)-len(strings.TrimLeft(line, " \t"))]
		lines[i] = leadingWhitespace + jwtTokenPrefix + " " + redactedToken
	}
	return strings.Join(lines, "\n")
}
