package main

/*
	Vdir.go

	This script handle virtual directory functions
	in global scopes

	Author: tobychui
*/

import (
	"encoding/json"
	"net/http"
	"strings"

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

// List the Virtual directory under given proxy rule
func ReverseProxyListVdir(w http.ResponseWriter, r *http.Request) {
	eptype, err := utils.PostPara(r, "type") //Support root and host
	if err != nil {
		utils.SendErrorResponse(w, "type not defined")
		return
	}

	var targetEndpoint *dynamicproxy.ProxyEndpoint
	if eptype == "host" {
		endpoint, err := utils.PostPara(r, "ep")
		if err != nil {
			utils.SendErrorResponse(w, "endpoint not defined")
			return
		}

		targetEndpoint, err = dynamicProxyRouter.LoadProxy(endpoint)
		if err != nil {
			utils.SendErrorResponse(w, "target endpoint not found")
			return
		}
	} else if eptype == "root" {
		targetEndpoint = dynamicProxyRouter.Root
	} else {
		utils.SendErrorResponse(w, "invalid type given")
		return
	}

	//Parse result to json
	vdirs := targetEndpoint.VirtualDirectories
	if targetEndpoint.VirtualDirectories == nil {
		//Avoid returning null to front-end
		vdirs = []*dynamicproxy.VirtualDirectoryEndpoint{}
	}
	js, _ := json.Marshal(vdirs)
	utils.SendJSONResponse(w, string(js))
}

// Add Virtual Directory to a host
func ReverseProxyAddVdir(w http.ResponseWriter, r *http.Request) {
	eptype, err := utils.PostPara(r, "type") //Support root and host
	if err != nil {
		utils.SendErrorResponse(w, "type not defined")
		return
	}

	matchingPath, err := utils.PostPara(r, "path")
	if err != nil {
		utils.SendErrorResponse(w, "matching path not defined")
		return
	}

	//Must start with /
	if !strings.HasPrefix(matchingPath, "/") {
		matchingPath = "/" + matchingPath
	}

	domain, err := utils.PostPara(r, "domain")
	if err != nil {
		utils.SendErrorResponse(w, "target domain not defined")
		return
	}

	reqTLSStr, err := utils.PostPara(r, "reqTLS")
	if err != nil {
		//Assume false
		reqTLSStr = "false"
	}
	reqTLS := (reqTLSStr == "true")

	skipValidStr, err := utils.PostPara(r, "skipValid")
	if err != nil {
		//Assume false
		skipValidStr = "false"
	}

	skipValid := (skipValidStr == "true")

	//Load the target proxy endpoint from runtime
	var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
	if eptype == "root" {
		//Check if root is running at reverse proxy mode
		if dynamicProxyRouter.Root.DefaultSiteOption != dynamicproxy.DefaultSite_ReverseProxy {
			utils.SendErrorResponse(w, "virtual directory can only be added to root router under proxy mode")
			return
		}
		targetProxyEndpoint = dynamicProxyRouter.Root
	} else if eptype == "host" {
		endpointID, err := utils.PostPara(r, "endpoint")
		if err != nil {
			utils.SendErrorResponse(w, "endpoint not defined")
			return
		}

		loadedEndpoint, err := dynamicProxyRouter.LoadProxy(endpointID)
		if err != nil {
			utils.SendErrorResponse(w, "selected proxy host not exists")
			return
		}

		targetProxyEndpoint = loadedEndpoint
	} else {
		utils.SendErrorResponse(w, "invalid proxy type given")
		return
	}

	// Create a virtual directory entry base on the above info
	newVirtualDirectoryRouter := dynamicproxy.VirtualDirectoryEndpoint{
		MatchingPath:        matchingPath,
		Domain:              domain,
		RequireTLS:          reqTLS,
		SkipCertValidations: skipValid,
	}

	//Add Virtual Directory Rule to this Proxy Endpoint
	activatedProxyEndpoint, err := targetProxyEndpoint.AddVirtualDirectoryRule(&newVirtualDirectoryRouter)
	if err != nil {
		utils.SendErrorResponse(w, err.Error())
		return
	}

	//Save it to file
	SaveReverseProxyConfig(activatedProxyEndpoint)

	// Update uptime monitor
	UpdateUptimeMonitorTargets()
	utils.SendOK(w)
}

func ReverseProxyDeleteVdir(w http.ResponseWriter, r *http.Request) {
	eptype, err := utils.PostPara(r, "type") //Support root and host
	if err != nil {
		utils.SendErrorResponse(w, "type not defined")
		return
	}

	vdir, err := utils.PostPara(r, "vdir")
	if err != nil {
		utils.SendErrorResponse(w, "vdir matching key not defined")
		return
	}

	var targetEndpoint *dynamicproxy.ProxyEndpoint
	if eptype == "root" {
		targetEndpoint = dynamicProxyRouter.Root
	} else if eptype == "host" {
		//Proxy rule
		matchingPath, err := utils.PostPara(r, "path")
		if err != nil {
			utils.SendErrorResponse(w, "matching path not defined")
			return
		}

		ept, err := dynamicProxyRouter.LoadProxy(matchingPath)
		if err != nil {
			utils.SendErrorResponse(w, "target proxy rule not found")
			return
		}

		targetEndpoint = ept
	} else {
		utils.SendErrorResponse(w, "invalid endpoint type")
		return
	}

	//Delete the Vdir from endpoint
	err = targetEndpoint.RemoveVirtualDirectoryRuleByMatchingPath(vdir)
	if err != nil {
		utils.SendErrorResponse(w, err.Error())
		return
	}

	err = SaveReverseProxyConfig(targetEndpoint)
	if err != nil {
		SystemWideLogger.PrintAndLog("Config", "Fail to write vdir rules update to config file", err)
		utils.SendErrorResponse(w, "unable to write changes to file")
		return
	}

	UpdateUptimeMonitorTargets()

	utils.SendOK(w)
}

// Handle update of reverse proxy vdir rules
func ReverseProxyEditVdir(w http.ResponseWriter, r *http.Request) {
	eptype, err := utils.PostPara(r, "type") //Support root and host
	if err != nil {
		utils.SendErrorResponse(w, "type not defined")
		return
	}

	vdir, err := utils.PostPara(r, "vdir")
	if err != nil {
		utils.SendErrorResponse(w, "vdir matching key not defined")
		return
	}

	domain, err := utils.PostPara(r, "domain")
	if err != nil {
		utils.SendErrorResponse(w, "target domain not defined")
		return
	}

	reqTLSStr, err := utils.PostPara(r, "reqTLS")
	if err != nil {
		//Assume false
		reqTLSStr = "false"
	}
	reqTLS := (reqTLSStr == "true")

	skipValidStr, err := utils.PostPara(r, "skipValid")
	if err != nil {
		//Assume false
		skipValidStr = "false"
	}

	skipValid := (skipValidStr == "true")

	var targetEndpoint *dynamicproxy.ProxyEndpoint
	if eptype == "root" {
		targetEndpoint = dynamicProxyRouter.Root

	} else if eptype == "host" {
		//Proxy rule
		matchingPath, err := utils.PostPara(r, "path")
		if err != nil {
			utils.SendErrorResponse(w, "matching path not defined")
			return
		}

		ept, err := dynamicProxyRouter.LoadProxy(matchingPath)
		if err != nil {
			utils.SendErrorResponse(w, "target proxy rule not found")
			return
		}

		targetEndpoint = ept
	} else {
		utils.SendErrorResponse(w, "invalid endpoint type given")
		return
	}

	//Check if the target vdir exists
	if targetEndpoint.GetVirtualDirectoryRuleByMatchingPath(vdir) == nil {
		utils.SendErrorResponse(w, "target virtual directory rule not exists")
		return
	}

	//Overwrite the target endpoint
	newVdirRule := dynamicproxy.VirtualDirectoryEndpoint{
		MatchingPath:        vdir,
		Domain:              domain,
		RequireTLS:          reqTLS,
		SkipCertValidations: skipValid,
		Disabled:            false,
	}

	targetEndpoint.RemoveVirtualDirectoryRuleByMatchingPath(vdir)
	activatedProxyEndpoint, err := targetEndpoint.AddVirtualDirectoryRule(&newVdirRule)
	if err != nil {
		utils.SendErrorResponse(w, err.Error())
		return
	}

	//Save changes to file
	SaveReverseProxyConfig(activatedProxyEndpoint)

	UpdateUptimeMonitorTargets()

	utils.SendOK(w)
}
