package server

import (
	"html/template"
	"os"
	"path"
	"strings"

	"github.com/goccy/go-json"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/template/html/v2"

	"pluja.dev/kycnot.me/pow"
)

type Server struct {
	ListenAddr    string
	Router        *fiber.App
	PowChallenger *pow.PowChallenger
}

func NewServer(listenAddr string) *Server {
	// Create a new template engine
	engine := html.New(path.Join(os.Getenv("ROOT_DIR"), "web/templates"), ".html")
	if os.Getenv("DEV_MODE") == "true" {
		engine.Reload(true)
	}
	engine.AddFuncMap(
		map[string]interface{}{
			"attr": func(s string) template.HTMLAttr {
				return template.HTMLAttr(s)
			},
			"safe": func(s string) template.HTML {
				return template.HTML(s)
			},
			"shortText": func(s string) string {
				if len(s) > 50 {
					return strings.TrimSpace(s[:50]) + "..."
				}
				return s
			},
		},
	)
	return &Server{
		ListenAddr: listenAddr,
		Router: fiber.New(fiber.Config{
			JSONEncoder:  json.Marshal,
			JSONDecoder:  json.Unmarshal,
			BodyLimit:    2 * 1024 * 1024, // Increase body limit to 2MB
			ServerHeader: "None",          // Optional, for easier debugging
			Views:        engine,
		}),
		PowChallenger: &pow.PowChallenger{},
	}
}

func (s *Server) Run() {
	s.SetupMiddleware()
	s.RegisterRoutes()
	s.PowChallenger.Init()
	s.Router.Listen(s.ListenAddr)
}

func (s *Server) SetupMiddleware() {
	s.Router.Use(cors.New())

	if os.Getenv("DEV_MODE") == "true" {
		s.Router.Use(logger.New(logger.Config{
			Format: "${time} | ${method} ${status} ${path} ${latency}\n",
		}))
	}
}

func (s *Server) RegisterRoutes() {
	// Static routes
	s.Router.Static("/static", path.Join(os.Getenv("ROOT_DIR"), "/web/static"), fiber.Static{
		Compress:  true,
		ByteRange: true,
	})

	// Register HTTP route for getting initial state.
	s.Router.Get("/", s.handleIndex)
	s.Router.Get("/service/:name", s.handleService)
	s.Router.Get("/point/:id", s.handleAttribute)
	s.Router.Get("/attr/:id", s.handleAttribute)
	s.Router.Get("/request/service", s.handleRequestServiceForm)
	s.Router.Post("/request/service", s.handleRequestServicePostForm)

	// Register API routes
	s.Router.Get("/api/pow/verify/:id/:nonce", s.handleVerifyPow)
}
