package npmproxy

import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	"github.com/kataras/golog"
)

func (p *Proxy) externalHTTPServer(ctx context.Context) {
	mux := http.NewServeMux()

	srv := &http.Server{
		Addr:    p.externalProxyHost,
		Handler: mux,
	}

	go func() {
		<-ctx.Done()
		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		if err := srv.Shutdown(shutdownCtx); err != nil {
			fmt.Printf("Failed to shutdown proxy server: %v\n", err)
		}
	}()

	mux.HandleFunc("/", p.incomingNpmRequestHandler)

	if err := srv.ListenAndServe(); err != nil {
		if err != http.ErrServerClosed {
			panic(err)
		}
	}
}

// Receives incoming requests from the npm cli and decides based on override rules what server it should forwarded to.
func (p *Proxy) incomingNpmRequestHandler(res http.ResponseWriter, req *http.Request) {
	golog.Infof("Incoming NPM request for %s", req.URL.Path)
	pkgPath := strings.TrimLeft(req.URL.Path, "/")

	// If no matching override is found, we forward the request to the default registry.
	_, ok := p.matchingOverride(pkgPath)
	if !ok {
		serveReverseProxy(p.DefaultRegistry, res, req)
		return
	}

	// Process the override by forwarding the request to the internal proxy.

	serveReverseProxy(p.internalProxyUrl, res, req)

	// golog.Infof("Received request for url: %v", proxyUrl)
}

type ResponseWriterWrapper struct {
	http.ResponseWriter
	Body *bytes.Buffer
}

func (rw *ResponseWriterWrapper) Write(b []byte) (int, error) {
	rw.Body.Write(b)                  // Capture the response body
	return rw.ResponseWriter.Write(b) // Send the response to the original writer
}

func serveReverseProxy(target string, res http.ResponseWriter, req *http.Request) {
	// parse the OriginalUrl
	OriginalUrl, _ := url.Parse(target)

	// create the reverse proxy
	proxy := httputil.NewSingleHostReverseProxy(OriginalUrl)

	// Update the headers to allow for SSL redirection
	req.URL.Host = OriginalUrl.Host
	req.URL.Scheme = OriginalUrl.Scheme
	req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
	req.Host = OriginalUrl.Host

	wrappedRes := &ResponseWriterWrapper{ResponseWriter: res, Body: new(bytes.Buffer)}

	// Note that ServeHttp is non blocking and uses a go routine under the hood
	proxy.ServeHTTP(wrappedRes, req)

	// Print the captured response body
	fmt.Println("Response body:", wrappedRes.Body.String())
}