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

import (
	"fmt"
	"log"
	"os"
	"sync"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cache"
	"github.com/gofiber/fiber/v2/middleware/compress"
	"github.com/gofiber/fiber/v2/utils"
	"github.com/gofiber/template/html/v2"

	"git.p78su.freemyip.com/svi/gostore/pkg/serv_http/router_anonym"
	"git.p78su.freemyip.com/svi/gostore/pkg/types"
)

// ServHttp -- встроенный HTTP-сервер
type ServHttp struct {
	serv     types.IService
	fiberApp *fiber.App
	port     string
	store    types.IStore
	anonym   *router_anonym.RouterAnonym
	block    sync.RWMutex
	isWork   bool // Признак работы
}

// NewServHttp -- возвращает новый HTTP-сервер
func NewServHttp(serv types.IService) (types.IServHttp, error) {
	if serv == nil {
		return nil, fmt.Errorf("NewServHttp(): IService==nil")
	}
	port := os.Getenv("STORE_HTTP_PORT")
	if port == "" {
		return nil, fmt.Errorf("NewServHttp(): env STORE_HTTP_PORT not set")
	}
	engineTmpl := html.New("./static/tmpl", ".tmpl.html")
	appConf := fiber.Config{
		Prefork:              false,
		ServerHeader:         "GoStore",
		StrictRouting:        false,
		CaseSensitive:        false,
		Immutable:            false,
		UnescapePath:         false,
		ETag:                 false,
		BodyLimit:            10_000_000,
		Concurrency:          0,
		Views:                engineTmpl,
		ViewsLayout:          "",
		PassLocalsToViews:    false,
		ReadTimeout:          time.Second * 60,
		WriteTimeout:         time.Second * 60,
		IdleTimeout:          time.Second * 10,
		ReadBufferSize:       0,
		WriteBufferSize:      0,
		CompressedFileSuffix: ".fiber.gz",
		ProxyHeader:          "",
		GETOnly:              false,
		// ErrorHandler:                 func(*fiber.Ctx, error) error { panic("not implemented") },
		DisableKeepalive:             false,
		DisableDefaultDate:           false,
		DisableDefaultContentType:    false,
		DisableHeaderNormalizing:     false,
		DisableStartupMessage:        false,
		AppName:                      "GoStore",
		StreamRequestBody:            false,
		DisablePreParseMultipartForm: false,
		ReduceMemoryUsage:            false,
		// JSONEncoder:                  func(interface{}) ([]byte, error) { panic("not implemented") },
		// JSONDecoder:                  func([]byte, interface{}) error { panic("not implemented") },
		// XMLEncoder:                   func(interface{}) ([]byte, error) { panic("not implemented") },
		Network:                 "tcp4",
		EnableTrustedProxyCheck: false,
		TrustedProxies:          []string{},
		EnableIPValidation:      false,
		EnablePrintRoutes:       true,
		// ColorScheme: fiber.Colors{
		// 	Black:   "",
		// 	Red:     "",
		// 	Green:   "",
		// 	Yellow:  "",
		// 	Blue:    "",
		// 	Magenta: "",
		// 	Cyan:    "",
		// 	White:   "",
		// 	Reset:   "",
		// },
		// RequestMethods:           []string{},
		EnableSplittingOnParsers: false,
	}

	app := fiber.New(appConf)
	app.Static("/static", "./static")
	compressConf := compress.Config{
		Level: compress.LevelBestCompression,
	}
	app.Use(compress.New(compressConf))
	cacheConf := cache.Config{
		Next:         nil,
		Expiration:   30 * time.Minute,
		CacheHeader:  "X-Cache",
		CacheControl: true,
		KeyGenerator: func(c *fiber.Ctx) string {
			return utils.CopyString(c.Path())
		},
		ExpirationGenerator:  nil,
		StoreResponseHeaders: false,
		Storage:              nil,
		MaxBytes:             0,
		Methods:              []string{fiber.MethodGet, fiber.MethodHead},
	}
	app.Use(cache.New(cacheConf))
	sf := &ServHttp{
		serv:     serv,
		fiberApp: app,
		port:     port,
		store:    serv.Store(),
	}

	var err error
	sf.anonym, err = router_anonym.NewRouterAnonym(sf)
	if err != nil {
		return nil, err
	}
	sf.fiberApp.Get("/", sf.get)
	sf.serv.Wg().Add(1)
	sf.isWork = true
	go sf.close()
	return sf, nil
}

// Возвращает индексную страницу хранилища
func (sf *ServHttp) get(ctx *fiber.Ctx) error {
	return ctx.Render("index", fiber.Map{})
}

// Service -- возвращает сервис
func (sf *ServHttp) Service() types.IService {
	return sf.serv
}

// FiberApp -- возвращает fiber-приложение
func (sf *ServHttp) FiberApp() *fiber.App {
	return sf.fiberApp
}

// Run -- запускает веб-сервер в работу
func (sf *ServHttp) Run() error {
	defer sf.serv.CancelApp()
	err := sf.fiberApp.Listen(":" + sf.port)
	if err != nil {
		return fmt.Errorf("ServHttp.Run(): in listen port(%q), err=\n\t%w", sf.port, err)
	}
	return nil
}

func (sf *ServHttp) close() {
	<-sf.serv.Ctx().Done()
	sf.block.Lock()
	defer sf.block.Unlock()
	if !sf.isWork {
		return
	}
	sf.isWork = false
	err := sf.fiberApp.Shutdown()
	if err != nil {
		log.Printf("ServHttp.close(): in shutdown fiberApp, err=\n\t%v\n", err)
	}
	sf.serv.Wg().Done()
}
