package main

import (
	"net/http"
	"net/url"

	"github.com/zeevdiukman/go-helper"
	"github.com/zeevdiukman/go-reverseproxy"
	"github.com/zeevdiukman/go-router"
	"github.com/zeevdiukman/go-zgate"
	"github.com/zeevdiukman/go-zgate/pkg/config"
)

// main is the entry point of the z application.
// It initializes and configures the application, sets up entry points,
// configures routers, and starts the servers. It also starts test HTTP servers
// for demonstration purposes.
func main() {
	helper.Clear()

	helper.AppRunner(true, func() {

		zGate := zgate.New()

		//Enetry points building
		zGate.Config.EntryPoints.ForEach(func(entryPointName string, entryPointConfig *config.EntryPoint) {
			v, ok := isOKv(zGate.ActiveEntryPoints, entryPointName)
			if ok && v {
				port := zgate.StrAddressPortToInt(entryPointConfig.Address)
				newEntryPoint := zGate.EntryPoints.NewEntryPoint(entryPointName)
				newEntryPoint.Server.Port(port).Name(entryPointName)
				newEntryPoint.Server.Router(newEntryPoint.Router)
				if ok && entryPointConfig.TLS.Enabled {
					zGate.EntryPoints[entryPointName].IsTLS = true
				}
			}
		})

		//Routers building
		zGate.Config.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) {
			epName := rConfig.EntryPoint
			serviceName := rConfig.Service
			isActive, _ := isOKv(zGate.ActiveEntryPoints, epName)
			isEpExist := isOK(zGate.EntryPoints, epName)
			isServiceExist := isOK(zGate.Config.HTTP.Services, serviceName)
			ok := isActive && isEpExist && isServiceExist
			if ok {

				entryPoint := zGate.EntryPoints[rConfig.EntryPoint]
				if _, ok := entryPoint.HostRouters[rName]; !ok {
					entryPoint.HostRouters = make(map[string]*router.DomainRouter)
				}
				applyMap := make(map[string]string)
				rulesAvailable := []string{
					"Domain",
					"Path",
					"PathPrefix",
				}
				for _, ruleName := range rulesAvailable {
					applyMap[ruleName] = rConfig.Rules.Get(ruleName)
				}

				revereProxyHandler := zGate.NewReverseProxy(rConfig.Service)
				revereProxyHandler.Director = func(r *http.Request) {
					r = reverseproxy.StripPrefix(r, applyMap["PathPrefix"])
					r = r.WithContext(zGate.Context)
					host := zGate.Config.HTTP.Services[rConfig.Service].URL
					target, _ := url.Parse(host)
					targetQuery := target.RawQuery
					r.URL.Scheme = target.Scheme
					r.URL.Host = target.Host
					r.URL.Path, r.URL.RawPath = reverseproxy.JoinURLPath(target, r.URL)
					if targetQuery == "" || r.URL.RawQuery == "" {
						r.URL.RawQuery = targetQuery + r.URL.RawQuery
					} else {
						r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery
					}
				}
				route1 := entryPoint.Router.NewRoute()
				subRouter := route1.Host(applyMap["Domain"]).Subrouter()
				subRouter.PathPrefix(applyMap["PathPrefix"]).Handler(revereProxyHandler)
				// subRouter.NewRoute().Handler(revereProxyHandler)

				// readyRouter.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
				// 	return r.URL.Path == applyMap["PathPrefix"]
				// }).Handler(revereProxyHandler)
				// }

				// readyRouter.PathPrefix(applyMap["Path"]).Handler(revereProxyHandler)
				zGate.EntryPoints[rConfig.EntryPoint].Router = entryPoint.Router

				// }
				// if applyMap["Domain"] && applyMap["Path"] {
				// 	serviceURL := zGate.Config.HTTP.Services[rConfig.Service].URL
				// 	reverseProxy := reverseproxy.New(zGate.Context, serviceURL)

				// 	domain := rConfig.Rules.Map["Domain"].Value
				// 	hostRouter = entryPoint.Router.NewDomainRouter(domain,"Path")

				// 	pathPrefix := rConfig.Rules.Map["Path"].Value
				// 	hostRouter.Path(pathPrefix).Handler(reverseProxy)
				// 	zGate.EntryPoints[rConfig.EntryPoint].HostRouters[rName] = hostRouter
				// }

			}
		})

		zGate.EntryPoints.ForEach(func(epNAme string, zGateEntryPoint *zgate.EntryPoint) {
			if zGateEntryPoint.IsTLS {
				zGateEntryPoint.Server.CertKey(zgate.CERTS_PATH, "z.com.cert.pem", "z.com.key.pem")
				go zGateEntryPoint.Server.ListenAndServeTLS()
			} else {
				go zGateEntryPoint.Server.ListenAndServe()
			}

			/*
				//TODO: TLS per domain
				// isTLS := false

				// zGate.Config.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) {
				// 	rEntryPoint := rConfig.EntryPoint
				// 	ep, isEpConfOK := isOK(zGate.Config.EntryPoints, rEntryPoint)
				// 	activeEp, isActiveEpOK := isOK(zGate.ActiveEntryPoints, rEntryPoint)
				// 	if isEpConfOK && isActiveEpOK && ep.TLS.Enabled && epNAme == rEntryPoint && activeEp {
				// 		isTLS = true
				// 	}
				// })
				// zGate.Config.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) {
				// 	rEntryPoint := rConfig.EntryPoint
				// 	ep, ok := isOK(zGate.Config.EntryPoints, rEntryPoint)
				// 	if ok && ep.TLS.Enabled && epNAme == rEntryPoint {
				// 		isTLS = true
				// 	}
				// })

				// zGateEntryPoint.Server.ConnState = func(c net.Conn, cs http.ConnState) {
				// 	msg1 := c.RemoteAddr().String()
				// 	msg2 := "Connection state: " + strconv.Itoa(int(cs))

				// 	fmt.Println(msg1 + "\n" + msg2)
				// 	fmt.Println("=======================")
				// 	log.Println()

				// }
			*/
		})
		helper.StartTestHTTPServer(3001, "app1")
		helper.StartTestHTTPServer(3002, "app2")
	})
}
func isOKv[K comparable, V comparable](mp map[K]V, key K) (V, bool) {
	if v, ok := mp[key]; ok {
		return v, true
	} else {
		return v, false
	}
}

func isOK[K comparable, V comparable](mp map[K]V, key K) bool {
	if _, ok := mp[key]; ok {
		return true
	} else {
		return false
	}
}
