/*
Example:

	rp1 := reverseproxy.New(ctx, "http://localhost:8080")
	rp2 := reverseproxy.New(ctx, "http://localhost:3001")
	rp3 := reverseproxy.New(ctx, "http://localhost:3002")
	router := router.NewRouter()
	pr1 := router.NewHostRouter("keycloak.z.com")
	pr1.Handler(rp1)

	go func() {
		s := server.New().Name("proxy1").Port(443).Router(router)
		s.CertKey(CERTS_PATH, "z.com.cert.pem", "z.com.key.pem")
		err := s.ListenAndServeTLS()
		if err != nil {
			log.Println(err.Error())
		}
	}()
*/
package main

import (
	"context"
	"strconv"
	"strings"

	"github.com/zeevdiukman/go-helper"
	"github.com/zeevdiukman/go-reverseproxy"
	"github.com/zeevdiukman/go-router"
	"github.com/zeevdiukman/go-server"
	"github.com/zeevdiukman/zprox/internal/config"
)

// type ReverseProxy struct {
// 	Proxy  *http.Handler
// 	Domain string
// 	Router *router.HostRouter
// }

const CERTS_PATH string = "./assets/certs/"

type App struct {
	// Routers struct {
	// 	Maps map[string]*router.HostRouter
	// }
	// map[string]*router.HostRouter
	// Rules   map[string]map[string]string
	// Services map[string]string
	DomainRouters DomainRouters
	EntryPoints   EntryPoints
}

type DomainRouters map[string]*router.DomainRouter
type EntryPoints map[string]*EntryPoint
type EntryPoint struct {
	*server.Server
	*router.Router
	HostRouters HostRouters
}
type HostRouters map[string]*router.DomainRouter

func New() *App {
	return &App{}
}

func (ep EntryPoints) ForEach(f func(string, *EntryPoint)) {
	forEach(ep, f)
}

func forEach[K comparable, V any](mp map[K]V, f func(K, V)) {
	for k, v := range mp {
		f(k, v)
	}
}

// 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.AppRunner(true, func() {
		helper.Clear()
		ctx := context.Background()
		app := New()
		conf := config.NewConfig()
		activeEntryPoints := make(map[string]bool)
		conf.EntryPoints.ForEach(func(entryPointName string, entryPointConfig *config.EntryPoint) {
			conf.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) {
				if rConfig.EntryPoint == entryPointName {
					activeEntryPoints[entryPointName] = true

				}
			})
		})
		conf.EntryPoints.ForEach(func(entryPointName string, entryPointConfig *config.EntryPoint) {
			if isUsed, ok := activeEntryPoints[entryPointName]; ok && isUsed {
				if _, ok := app.EntryPoints[entryPointName]; !ok {
					app.EntryPoints = make(map[string]*EntryPoint)
				}
				r := router.NewRouter()
				_, portStr, _ := strings.Cut(entryPointConfig.Address, ":")
				port, _ := strconv.Atoi(portStr)
				s := server.New().Name(entryPointName).Port(port)
				s.Router(r)
				app.EntryPoints[entryPointName] = &EntryPoint{
					Server:      s,
					Router:      r,
					HostRouters: make(map[string]*router.DomainRouter),
				}
			}
		})
		conf.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) {
			if isUsed, ok := activeEntryPoints[rConfig.EntryPoint]; ok && isUsed {
				domain := rConfig.Rules.Map["Domain"].Value
				serviceURL := conf.HTTP.Services[rConfig.Service].URL
				if _, ok := app.EntryPoints[rConfig.EntryPoint]; ok {
					entryPoint := app.EntryPoints[rConfig.EntryPoint]
					if _, ok := entryPoint.HostRouters[domain]; !ok {
						entryPoint.HostRouters = make(map[string]*router.DomainRouter)
					}
					rpHandler := reverseproxy.New(ctx, serviceURL)
					r := app.EntryPoints[rConfig.EntryPoint].Router
					hostRouter := r.NewDomainRouter(domain)
					hostRouter.Handler(rpHandler)
					app.EntryPoints[rConfig.EntryPoint].HostRouters[domain] = hostRouter
				}
			}
		})
		app.EntryPoints.ForEach(func(s string, ep *EntryPoint) {
			conf.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) {
				if s == rConfig.EntryPoint {

				}
			})
			ep.CertKey(CERTS_PATH, "z.com.cert.pem", "z.com.key.pem")
			go ep.ListenAndServeTLS()
		})
		helper.StartTestHTTPServer(3001, "app1")
		helper.StartTestHTTPServer(3002, "app2")
	})

}

//	func main() {
//		Test()
//	}
func Test() {
	ctx := context.Background()
	conf := config.NewConfig()
	rp1 := reverseproxy.New(ctx, conf.HTTP.Services["keycloak"].URL)
	rp2 := reverseproxy.New(ctx, conf.HTTP.Services["app1"].URL)
	rp3 := reverseproxy.New(ctx, conf.HTTP.Services["app2"].URL)

	r := router.NewRouter()
	r.NewDomainRouter(conf.HTTP.Routers["keycloak"].Rules.Map["Domain"].Value).Handler(rp1)
	r.NewDomainRouter(conf.HTTP.Routers["app1"].Rules.Map["Domain"].Value).Handler(rp2)
	r.NewDomainRouter(conf.HTTP.Routers["app2"].Rules.Map["Domain"].Value).Handler(rp3)

	s := server.New().Name("proxy1").Port(443).Router(r)
	s.CertKey(CERTS_PATH, "z.com.cert.pem", "z.com.key.pem")
	go s.ListenAndServeTLS()
	helper.StartTestHTTPServer(3001, "app1")
	helper.StartTestHTTPServer(3002, "app2")
	select {}
}
