package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"regexp"
	"strconv"
	"time"

	"imuslab.com/zoraxy/mod/acme"
	"imuslab.com/zoraxy/mod/dynamicproxy"
	"imuslab.com/zoraxy/mod/utils"
)

/*
	acme.go

	This script handle special routing required for acme auto cert renew functions
*/

// Helper function to generate a random port above a specified value
func getRandomPort(minPort int) int {
	return rand.Intn(65535-minPort) + minPort
}

// init the new ACME instance
func initACME() *acme.ACMEHandler {
	log.Println("Starting ACME handler")
	rand.Seed(time.Now().UnixNano())
	// Generate a random port above 30000
	port := getRandomPort(30000)

	// Check if the port is already in use
	for acme.IsPortInUse(port) {
		port = getRandomPort(30000)
	}

	return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port))
}

// create the special routing rule for ACME
func acmeRegisterSpecialRoutingRule() {
	log.Println("Assigned temporary port:" + acmeHandler.Getport())

	err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{
		ID: "acme-autorenew",
		MatchRule: func(r *http.Request) bool {
			found, _ := regexp.MatchString("/.well-known/acme-challenge/*", r.RequestURI)
			return found
		},
		RoutingHandler: func(w http.ResponseWriter, r *http.Request) {

			req, err := http.NewRequest(http.MethodGet, "http://localhost:"+acmeHandler.Getport()+r.RequestURI, nil)
			req.Host = r.Host
			if err != nil {
				fmt.Printf("client: could not create request: %s\n", err)
				return
			}
			res, err := http.DefaultClient.Do(req)
			if err != nil {
				fmt.Printf("client: error making http request: %s\n", err)
				return
			}

			resBody, err := ioutil.ReadAll(res.Body)
			if err != nil {
				fmt.Printf("error reading: %s\n", err)
				return
			}
			w.Write(resBody)
		},
		Enabled:                true,
		UseSystemAccessControl: false,
	})

	if err != nil {
		log.Println("[Err] " + err.Error())
	}
}

// This function check if the renew setup is satisfied. If not, toggle them automatically
func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
	isForceHttpsRedirectEnabledOriginally := false
	if dynamicProxyRouter.Option.Port == 443 {
		//Enable port 80 to 443 redirect
		if !dynamicProxyRouter.Option.ForceHttpsRedirect {
			log.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests")
			dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
		} else {
			//Set this to true, so after renew, do not turn it off
			isForceHttpsRedirectEnabledOriginally = true
		}

	} else if dynamicProxyRouter.Option.Port == 80 {
		//Go ahead

	} else {
		//This port do not support ACME
		utils.SendErrorResponse(w, "ACME renew only support web server listening on port 80 (http) or 443 (https)")
	}

	// Pass over to the acmeHandler to deal with the communication
	acmeHandler.HandleRenewCertificate(w, r)

	if dynamicProxyRouter.Option.Port == 443 {
		if !isForceHttpsRedirectEnabledOriginally {
			//Default is off. Turn the redirection off
			log.Println("Restoring HTTP to HTTPS redirect settings")
			dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
		}
	}
}
