// package kserv_http -- встроенный HTTP-сервер
package kserv_http

import (
	"embed"
	"fmt"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/compress"
	"github.com/gofiber/fiber/v2/middleware/filesystem"
	"github.com/gofiber/fiber/v2/middleware/monitor"

	. "gitp78su.ipnodns.ru/svi/kern/v2/kc/helpers"
	"gitp78su.ipnodns.ru/svi/kern/v2/kc/local_ctx"
	"gitp78su.ipnodns.ru/svi/kern/v2/kc/safe_bool"
	"gitp78su.ipnodns.ru/svi/kern/v2/krn/kctx"
	. "gitp78su.ipnodns.ru/svi/kern/v2/krn/ktypes"
)

const (
	streamName = "kernel_server_http" // Контрольная строка для ожидателя потока
)

// kServHttp -- встроенный HTTP-сервер
type kServHttp struct {
	kCtx     IKernelCtx
	ctx      ILocalCtx
	log      ILogBuf
	strUrl   string // URL, на котором слушает HTTP-сервер
	fiberApp *fiber.App
	isWork   ISafeBool
	isEnd    ISafeBool
}

//go:embed static/*
var embedDirStatic embed.FS

var (
	kernServHttp *kServHttp
	block        sync.Mutex
)

// GetKernelServHttp -- возвращает  встроенный HTTP-сервер
func GetKernelServHttp() IKernelServerHttp {
	block.Lock()
	defer block.Unlock()
	if kernServHttp != nil {
		kernServHttp.log.Debug("GetKernelServHttp()")
		return kernServHttp
	}
	ctx := kctx.GetKernelCtx()

	strUrl := os.Getenv("LOCAL_HTTP_URL")
	Hassert(strUrl != "", "GetKernelServHttp(): env LOCAL_HTTP_URL not set")
	confFiber := fiber.Config{
		ServerHeader:      ctx.Get("monolitName").Val().(string),
		UnescapePath:      true,
		ReadTimeout:       time.Second * 15,
		WriteTimeout:      time.Second * 15,
		AppName:           ctx.Get("monolitName").Val().(string),
		Network:           "tcp4",
		EnablePrintRoutes: true,
	}
	sf := &kServHttp{
		kCtx:     ctx,
		ctx:      local_ctx.NewLocalCtx(ctx.Ctx()),
		strUrl:   strUrl,
		fiberApp: fiber.New(confFiber),
		isWork:   safe_bool.NewSafeBool(),
		isEnd:    safe_bool.NewSafeBool(),
	}
	sf.log = sf.ctx.Log()
	sf.log.Debug("GetKernelServHttp(): first run")
	sf.fiberApp.Use(compress.New(compress.Config{
		Level: compress.LevelBestCompression, // 2
	}))
	sf.fiberApp.Use("/static", filesystem.New(filesystem.Config{
		Root:       http.FS(embedDirStatic),
		PathPrefix: "static",
		Browse:     true,
		MaxAge:     3600 * 24,
	}))
	sf.fiberApp.Get("/monitor", monitor.New(monitor.Config{Title: ctx.Get("monolitName").Val().(string)}))
	res := sf.kCtx.Wg().Add(streamName)
	res.Hassert("NewKernelServHttp(): in add stream %v", streamName)
	ctx.Set("fiberApp", sf.fiberApp, "kServHttp: internal fiber app")
	kernServHttp = sf
	ctx.Set("kServHttp", kernServHttp, "kServHttp")
	return kernServHttp
}

// IsWork -- возвращает признак работы
func (sf *kServHttp) IsWork() bool {
	return sf.isWork.Get()
}

// Log -- возвращает локальный лог
func (sf *kServHttp) Log() ILogBuf {
	return sf.log
}

// Fiber -- возвращает объект веб-приложения fiber
func (sf *kServHttp) Fiber() *fiber.App {
	return sf.fiberApp
}

// Run -- запускает сервер в работу (блокирующий вызов)
func (sf *kServHttp) Run() {
	if sf.isEnd.Get() {
		return
	}
	if sf.isWork.Get() {
		return
	}
	go sf.close()
	sf.isWork.Set()
	sf.log.Debug("kServHttp.Run(): url='%v'", sf.strUrl)
	lstPort := strings.Split(sf.strUrl, ":")
	strPort := lstPort[len(lstPort)-1]
	strPort = strings.ReplaceAll(strPort, "/", "")
	strPort = strings.ReplaceAll(strPort, `"`, "")
	err := sf.fiberApp.Listen(":" + strPort)
	strOut := fmt.Sprintf("kServHttp.Run(): in listen, err=\n\t%v", err)
	sf.log.Err(strOut)
	sf.kCtx.Cancel()
	sf.isWork.Reset()
	sf.isEnd.Set()
}

// Ожидает окончания работы
func (sf *kServHttp) close() {
	sf.kCtx.Done()
	if !sf.isWork.Get() {
		return
	}
	sf.isWork.Reset()
	sf.isEnd.Set()
	err := sf.fiberApp.Server().Shutdown()
	Assert(err == nil, "kServHttp.close(): in close server, err=\n\t%v", err)
	sf.kCtx.Wg().Done(streamName)
	sf.log.Debug("kServHttp.close(): end")
}
