package server

import (
	"crypto/tls"
	"log"
	"net"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/gofiber/fiber/v2"
	"github.com/jackc/pgx/v5/pgxpool"

	"git.pbiernat.io/egommerce/api-entities/http"
	"git.pbiernat.io/egommerce/go-api-pkg/consul"
	"git.pbiernat.io/egommerce/go-api-pkg/fluentd"
)

type (
	Server struct {
		*fiber.App

		ID       string
		addr     string // e.g. "127.0.0.1:443"
		handlers map[string]any
	}
	HeaderRequestID struct {
		RequestID string `reqHeader:"x-request-id"`
	}
)

func New(c *Config) *Server {
	return &Server{
		ID: c.ID,
		App: fiber.New(fiber.Config{
			AppName:      c.ID,
			ServerHeader: c.Name + ":" + c.ID,
			ReadTimeout:  c.ReadTimeout * time.Millisecond,
			WriteTimeout: c.WriteTimeout * time.Millisecond,
			IdleTimeout:  c.IdleTimeout * time.Millisecond,
		}),
		addr:     c.NetAddr,
		handlers: make(map[string]any),
	}
}

func (s *Server) Start() error {
	SetupMiddleware(s)
	SetupRouter(s)

	// fmt.Printf("Starting server at: %s...\n", s.addr)
	cer, err := tls.LoadX509KeyPair("certs/client.crt", "certs/client.key")
	if err != nil {
		log.Fatal(err)
	}
	tlsCnf := &tls.Config{Certificates: []tls.Certificate{cer}}

	ln, _ := net.Listen("tcp", s.addr)
	ln = tls.NewListener(ln, tlsCnf)

	return s.Listener(ln)
}

func (s *Server) RegisterHandler(name string, fn func() any) {
	// fmt.Printf("Registering plugin( with handler): %s... OK\n", name)
	s.handlers[name] = fn()
}

func (s *Server) OnShutdown() {
	// s.GetLogger().Log("Server %s is going down...", s.ID)

	// s.GetRegistry().Unregister()
	// a.clearMetadataCache()
	// s.GetEventBus().Close()
	s.GetDatabase().Close()
	s.GetLogger().Log("Gone.")
	s.GetLogger().Close()

	s.Shutdown()
}

func (s *Server) GetRequestID(c *fiber.Ctx) (string, error) {
	var hdr = new(HeaderRequestID)
	if err := c.ReqHeaderParser(hdr); err != nil {
		return "", err
	}

	return hdr.RequestID, nil
}

func (s *Server) Error(c *fiber.Ctx, code int, msg string) error {
	return c.Status(code).JSON(http.ErrorResponse{Error: msg})
}

// Plugin helper funcitons
func (s *Server) GetCache() *redis.Client {
	return (s.handlers["cache"]).(*redis.Client)
}

func (s *Server) GetDatabase() *pgxpool.Pool { // FIXME hardcoded index issue
	return (s.handlers["database"]).(*pgxpool.Pool)
}

// func (s *Server) GetEventBus() *amqp.Channel {
// 	return (s.handlers["eventbus"]).(*amqp.Channel)
// }

func (s *Server) GetLogger() *fluentd.Logger {
	return (s.handlers["logger"]).(*fluentd.Logger)
}

func (s *Server) GetRegistry() *consul.Service {
	return (s.handlers["registry"]).(*consul.Service)
}

// @CHECK: merge s.Config and s.Base.Config to display all config as one array/map
// func (s *Server) registerKVUpdater() { // @FIXME: merge duplication in server.go and worker.go
// 	go func() {
// 		ticker := time.NewTicker(time.Second * 10)
// 		for range ticker.C {
// 			config, _, err := s.Registry.KV().Get(s.cnf.KVNamespace, nil)
// 			if err != nil || config == nil {
// 				return
// 			}

// 			kvCnf := bytes.NewBuffer(config.Value)
// 			decoder := json.NewDecoder(kvCnf)
// 			if err := decoder.Decode(&s.cnf); err != nil {
// 				return
// 			}
// 		}
// 	}()
// }

// // func (s *Server) clearMetadataCache() {
// // 	ctx := context.Background()
// // 	key, address := s.getMetadataIPsKey(), s.cnf.Base.AppID

// // 	s.Cache.LRem(ctx, key, 0, address)
// // }

// // func (s *Server) getMetadataIPsKey() string {
// // 	return "internal__" + s.cnf.Name + "__ips"
// // }
