package main

import (
	"encoding/json"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"

	"imuslab.com/arozos/ReverseProxy/mod/dynamicproxy"
	"imuslab.com/arozos/ReverseProxy/mod/utils"
)

var (
	dynamicProxyRouter *dynamicproxy.Router
)

//Add user customizable reverse proxy
func ReverseProxtInit() {
	inboundPort := 80
	if sysdb.KeyExists("settings", "inbound") {
		sysdb.Read("settings", "inbound", &inboundPort)
		log.Println("Serving inbound port ", inboundPort)
	} else {
		log.Println("Inbound port not set. Using default (80)")
	}

	useTls := false
	sysdb.Read("settings", "usetls", &useTls)
	if useTls {
		log.Println("TLS mode enabled. Serving proxxy request with TLS")
	} else {
		log.Println("TLS mode disabled. Serving proxy request with plain http")
	}

	forceHttpsRedirect := false
	sysdb.Read("settings", "redirect", &forceHttpsRedirect)
	if forceHttpsRedirect {
		log.Println("Force HTTPS mode enabled")
	} else {
		log.Println("Force HTTPS mode disabled")
	}

	dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
		Port:               inboundPort,
		UseTls:             useTls,
		ForceHttpsRedirect: forceHttpsRedirect,
		TlsManager:         tlsCertManager,
	})
	if err != nil {
		log.Println(err.Error())
		return
	}

	dynamicProxyRouter = dprouter

	//Load all conf from files
	confs, _ := filepath.Glob("./conf/*.config")
	for _, conf := range confs {
		record, err := LoadReverseProxyConfig(conf)
		if err != nil {
			log.Println("Failed to load "+filepath.Base(conf), err.Error())
			return
		}

		if record.ProxyType == "root" {
			dynamicProxyRouter.SetRootProxy(record.ProxyTarget, record.UseTLS)
		} else if record.ProxyType == "subd" {
			dynamicProxyRouter.AddSubdomainRoutingService(record.Rootname, record.ProxyTarget, record.UseTLS)
		} else if record.ProxyType == "vdir" {
			dynamicProxyRouter.AddVirtualDirectoryProxyService(record.Rootname, record.ProxyTarget, record.UseTLS)
		} else {
			log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf))
		}
	}

	/*
		dynamicProxyRouter.SetRootProxy("192.168.0.107:8080", false)
		dynamicProxyRouter.AddSubdomainRoutingService("aroz.localhost", "192.168.0.107:8080/private/AOB/", false)
		dynamicProxyRouter.AddSubdomainRoutingService("loopback.localhost", "localhost:8080", false)
		dynamicProxyRouter.AddSubdomainRoutingService("git.localhost", "mc.alanyeung.co:3000", false)
		dynamicProxyRouter.AddVirtualDirectoryProxyService("/git/server/", "mc.alanyeung.co:3000", false)
	*/

	//Start Service
	dynamicProxyRouter.StartProxyService()
	log.Println("Dynamic Reverse Proxy service started")

}

func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
	enable, _ := mv(r, "enable", true) //Support root, vdir and subd
	if enable == "true" {
		err := dynamicProxyRouter.StartProxyService()
		if err != nil {
			sendErrorResponse(w, err.Error())
			return
		}
	} else {
		err := dynamicProxyRouter.StopProxyService()
		if err != nil {
			sendErrorResponse(w, err.Error())
			return
		}
	}

	sendOK(w)
}

func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
	eptype, err := mv(r, "type", true) //Support root, vdir and subd
	if err != nil {
		sendErrorResponse(w, "type not defined")
		return
	}

	endpoint, err := mv(r, "ep", true)
	if err != nil {
		sendErrorResponse(w, "endpoint not defined")
		return
	}

	tls, _ := mv(r, "tls", true)
	if tls == "" {
		tls = "false"
	}

	useTLS := (tls == "true")
	rootname := ""
	if eptype == "vdir" {
		vdir, err := mv(r, "rootname", true)
		if err != nil {
			sendErrorResponse(w, "vdir not defined")
			return
		}

		if !strings.HasPrefix(vdir, "/") {
			vdir = "/" + vdir
		}
		rootname = vdir
		dynamicProxyRouter.AddVirtualDirectoryProxyService(vdir, endpoint, useTLS)

	} else if eptype == "subd" {
		subdomain, err := mv(r, "rootname", true)
		if err != nil {
			sendErrorResponse(w, "subdomain not defined")
			return
		}
		rootname = subdomain
		dynamicProxyRouter.AddSubdomainRoutingService(subdomain, endpoint, useTLS)
	} else if eptype == "root" {
		rootname = "root"
		dynamicProxyRouter.SetRootProxy(endpoint, useTLS)
	} else {
		//Invalid eptype
		sendErrorResponse(w, "Invalid endpoint type")
		return
	}

	//Save it
	SaveReverseProxyConfig(eptype, rootname, endpoint, useTLS)

	sendOK(w)

}

func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
	ep, err := mv(r, "ep", true)
	if err != nil {
		sendErrorResponse(w, "Invalid ep given")
	}

	ptype, err := mv(r, "ptype", true)
	if err != nil {
		sendErrorResponse(w, "Invalid ptype given")
	}

	err = dynamicProxyRouter.RemoveProxy(ptype, ep)
	if err != nil {
		sendErrorResponse(w, err.Error())
	}

	RemoveReverseProxyConfig(ep)
	sendOK(w)
}

func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
	js, _ := json.Marshal(dynamicProxyRouter)
	sendJSONResponse(w, string(js))
}

func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
	eptype, err := mv(r, "type", true) //Support root, vdir and subd
	if err != nil {
		sendErrorResponse(w, "type not defined")
		return
	}

	if eptype == "vdir" {
		results := []*dynamicproxy.ProxyEndpoint{}
		dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
			results = append(results, value.(*dynamicproxy.ProxyEndpoint))
			return true
		})

		js, _ := json.Marshal(results)
		sendJSONResponse(w, string(js))
	} else if eptype == "subd" {
		results := []*dynamicproxy.SubdomainEndpoint{}
		dynamicProxyRouter.SubdomainEndpoint.Range(func(key, value interface{}) bool {
			results = append(results, value.(*dynamicproxy.SubdomainEndpoint))
			return true
		})
		js, _ := json.Marshal(results)
		sendJSONResponse(w, string(js))
	} else if eptype == "root" {
		js, _ := json.Marshal(dynamicProxyRouter.Root)
		sendJSONResponse(w, string(js))
	} else {
		sendErrorResponse(w, "Invalid type given")
	}
}

//Handle https redirect
func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
	useRedirect, err := utils.GetPara(r, "set")
	if err != nil {
		currentRedirectToHttps := false
		//Load the current status
		err = sysdb.Read("settings", "redirect", &currentRedirectToHttps)
		if err != nil {
			sendErrorResponse(w, err.Error())
			return
		}
		js, _ := json.Marshal(currentRedirectToHttps)
		sendJSONResponse(w, string(js))
	} else {
		if useRedirect == "true" {
			sysdb.Write("settings", "redirect", true)
			log.Println("Updating force HTTPS redirection to true")
			dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
		} else if useRedirect == "false" {
			sysdb.Write("settings", "redirect", false)
			log.Println("Updating force HTTPS redirection to false")
			dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
		}

		utils.SendOK(w)
	}
}

//Handle incoming port set. Change the current proxy incoming port
func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
	newIncomingPort, err := mv(r, "incoming", true)
	if err != nil {
		sendErrorResponse(w, "invalid incoming port given")
		return
	}

	newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
	if err != nil {
		sendErrorResponse(w, "invalid incoming port given")
		return
	}

	//Stop and change the setting of the reverse proxy service
	if dynamicProxyRouter.Running {
		dynamicProxyRouter.StopProxyService()
		dynamicProxyRouter.ListenPort = newIncomingPortInt
		dynamicProxyRouter.StartProxyService()
	} else {
		//Only change setting but not starting the proxy service
		dynamicProxyRouter.ListenPort = newIncomingPortInt
	}

	sysdb.Write("settings", "inbound", newIncomingPortInt)

	sendOK((w))
}
