package config

import (
	"regexp"
	"strings"

	"github.com/zeevdiukman/go-config"
)

const VIPER_NAME string = "config"
const VIPER_TYPE string = "yaml"
const VIPER_PATH string = "./assets/config/."
const SERVICES_KEY string = "http.services"
const ROUTERS_KEY string = "http.routers"
const ENTRYPOINTS_KEY string = "entrypoints"
const TLS_KEY string = "tls.certproviders"

type Config struct {
	HTTP        *HTTP
	TLS         *TLS
	EntryPoints EntryPoints
}
type HTTP struct {
	Routers  Routers
	Services Services
}
type TLS struct {
	CertProviders CertProviders
}
type CertProviders map[string]*CertProvider
type CertProvider struct {
	Key  string
	Cert string
}

type Services map[string]*Service
type Service struct {
	URL string
}
type EntryPoints map[string]*EntryPoint
type EntryPoint struct {
	Address string
	TLS     *EntryPointTLS
}
type EntryPointTLS struct {
	Enabled bool
}
type Routers map[string]*Router
type Router struct {
	EntryPoint string
	Service    string
	Rules      *Rules
	TLS        *RouterTLS
}
type RouterTLS struct {
	CertProvider string
}
type Rules struct {
	Raw string
	Map map[string]*Rule
}

type Rule struct {
	Name  string
	Value string
}

func NewConfig() *Config {

	c := config.NewConfig(VIPER_NAME, VIPER_TYPE, VIPER_PATH)
	c.Load()

	appConfig := &Config{
		HTTP: &HTTP{
			Routers:  Routers{},
			Services: Services{},
		},
		TLS: &TLS{
			CertProviders: CertProviders{},
		},
		EntryPoints: EntryPoints{},
	}

	appConfig.InitRouters(c)
	appConfig.InitServices(c)
	appConfig.InitEntryPoints(c)
	appConfig.InitTLS(c)

	return appConfig
}
func (r Routers) ForEach(fn func(routerName string, routerConfig *Router)) {
	for k, v := range r {
		fn(k, v)
	}
}
func (ep EntryPoints) ForEach(fn func(entryPointName string, entryPointConfig *EntryPoint)) {
	forEach(ep, fn)
}
func (s Services) ForEach(fn func(serviceName string, serviceConfig *Service)) {
	forEach(s, fn)
}
func (c *Config) InitRouters(vConfig *config.Config) {
	c.HTTP.Routers = make(Routers)
	for k, v := range vConfig.Sub(ROUTERS_KEY).AllSettings() {
		val := v.(map[string]any)

		var tls string = ""
		var services string = ""
		var rawRules string = ""
		var entryPoint string = ""

		if value, ok := val["service"]; ok {
			services = value.(string)
		}
		if value, ok := val["rules"]; ok {
			rawRules = value.(string)
		}
		if value, ok := val["entrypoint"]; ok {
			entryPoint = value.(string)
		}
		if value, ok := val["tls"]; ok {
			tls = value.(map[string]any)["certprovider"].(string)
		}
		rls := &Rules{
			Raw: rawRules,
			Map: make(map[string]*Rule),
		}

		r := &Router{
			Service:    services,
			Rules:      rls,
			EntryPoint: entryPoint,
			TLS:        &RouterTLS{CertProvider: tls},
		}
		r.ForRuleFunc("Domain", func(domain string) {
			r.Rules.Map["Domain"] = &Rule{
				Name:  "Domain",
				Value: domain,
			}
		})
		r.ForRuleFunc("Path", func(path string) {
			r.Rules.Map["Path"] = &Rule{
				Name:  "Path",
				Value: path,
			}
		})

		c.HTTP.Routers[k] = r
	}
}
func (c *Config) InitServices(vConfig *config.Config) {
	c.HTTP.Services = make(map[string]*Service)
	for k, v := range vConfig.Sub(SERVICES_KEY).AllSettings() {
		c.HTTP.Services[k] = &Service{
			URL: v.(string),
		}
	}
}
func (c *Config) InitEntryPoints(vConfig *config.Config) {
	c.EntryPoints = make(EntryPoints)
	for k, v := range vConfig.Sub(ENTRYPOINTS_KEY).AllSettings() {
		v := v.(map[string]any)
		address := ""
		enabled := false
		if v, ok := v["tls"]; ok {
			if v, ok := v.(map[string]any)["enabled"]; ok {
				enabled = v.(bool)
			}
		}

		if value, ok := v["address"]; ok {
			address = value.(string)
		}
		c.EntryPoints[k] = &EntryPoint{
			Address: address,
			TLS: &EntryPointTLS{
				Enabled: enabled,
			},
		}
	}
}

func (c *Config) InitTLS(vConfig *config.Config) {
	c.TLS.CertProviders = make(map[string]*CertProvider)

	for k, v := range vConfig.Sub(TLS_KEY).AllSettings() {
		cert := v.(map[string]any)["cert"].(string)
		key := v.(map[string]any)["key"].(string)
		c.TLS.CertProviders[k] = &CertProvider{
			Cert: cert,
			Key:  key,
		}
	}
}
func forEach[K comparable, V comparable](mp map[K]V, fn func(key K, value V)) {
	for k, v := range mp {
		fn(k, v)
	}
}

func (r *Router) ForRuleFunc(ruleName string, fn func(string)) {
	rules := r.Rules.Raw
	rulesSlice := splitStatementByRegex(rules, "\\(`(.*?)`\\)")
	for idx, rule := range rulesSlice {
		rule, _ = strings.CutPrefix(rule, ".")
		if rule == ruleName {
			fn(rulesSlice[idx+1])
		}
	}
}

func splitStatementByRegex(str string, regx string) []string {

	input := str
	re := regexp.MustCompile(regx)
	matches := re.FindAllStringSubmatch(input, -1)
	if len(matches) == 0 {
		return []string{str} // Return original if no backticks found
	}

	result := []string{}
	lastIndex := 0

	for _, match := range matches {

		startIndex := strings.Index(input, match[0])

		if startIndex > lastIndex {
			result = append(result, input[lastIndex:startIndex])
		}

		result = append(result, match[1]) // Append the content within backticks
		lastIndex = startIndex + len(match[0])
	}

	if lastIndex < len(input) {
		result = append(result, input[lastIndex:])
	}

	return result
}
