/*
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
:                                                                               :
:   █▀ █ █▀▀   ·   Blazing-fast pentesting suite                                :
:   ▄█ █ █▀    ·   BSD 3-Clause License                                         :
:                                                                               :
:   (c) 2022-2026 vmfunc, xyzeva,                                               :
:                 lunchcat alumni & contributors                                :
:                                                                               :
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
*/

/*
	What we are doing is abusing a internal file in Next.js pages router called
	_buildManifest.js which lists all routes and script files ever referenced in
	the application within next.js, this allows us to optimise and not bruteforce
	directories for routes and instead get all of them at once.

	We are currently parsing this js file with regexes but that should ideally be
	replaced soon.
*/

package frameworks

import (
	"bufio"
	"context"
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/dropalldatabases/sif/internal/httpx"
	urlutil "github.com/projectdiscovery/utils/url"
)

// nextPagesRegex matches JavaScript file references in Next.js build manifest.
var nextPagesRegex = regexp.MustCompile(`\[("([^"]+\.js)"(,?))`)

func GetPagesRouterScripts(scriptUrl string) ([]string, error) {
	baseUrl, err := urlutil.Parse(scriptUrl)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, scriptUrl, http.NoBody)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	// no timeout in scope here; 0 matches the previous DefaultClient behavior
	// while still routing through the shared transport (proxy/headers/rate-limit).
	resp, err := httpx.Client(0).Do(req)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	defer resp.Body.Close()

	var sb strings.Builder
	scanner := bufio.NewScanner(resp.Body)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		sb.WriteString(scanner.Text())
	}
	manifestText := sb.String()

	list := nextPagesRegex.FindAllStringSubmatch(manifestText, -1)

	var scripts []string

	for _, el := range list {
		var script = strings.ReplaceAll(el[2], "\\u002F", "/")
		url, err := urlutil.Parse(script)
		if err != nil {
			continue
		}

		if url.IsRelative {
			url.Host = baseUrl.Host
			url.Scheme = baseUrl.Scheme
			url.Path = "/_next/" + url.Path
		}
		scripts = append(scripts, url.String())
	}

	return scripts, nil
}
