package server

import (
	"fmt"
	"time"

	"github.com/gin-contrib/gzip"
	"github.com/gin-gonic/gin"

	"warpbox/lib/boxstore"
	"warpbox/lib/config"
	"warpbox/lib/metastore"
	"warpbox/lib/routing"
)

type App struct {
	config            *config.Config
	store             *metastore.Store
	adminLoginEnabled bool
}

func Run(addr string) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := cfg.EnsureDirectories(); err != nil {
		return err
	}

	boxstore.SetUploadRoot(cfg.UploadsDir)

	store, err := metastore.Open(cfg.DBDir)
	if err != nil {
		return fmt.Errorf("open metadata database: %w", err)
	}
	defer store.Close()

	overrides, err := store.ListSettings()
	if err != nil {
		return fmt.Errorf("load settings overrides: %w", err)
	}
	if err := cfg.ApplyOverrides(overrides); err != nil {
		return fmt.Errorf("apply settings overrides: %w", err)
	}

	bootstrap, err := metastore.BootstrapAdmin(cfg, store)
	if err != nil {
		return fmt.Errorf("bootstrap admin metadata: %w", err)
	}

	app := &App{
		config:            cfg,
		store:             store,
		adminLoginEnabled: bootstrap.AdminLoginEnabled,
	}

	router := gin.Default()
	router.LoadHTMLGlob("templates/*.html")

	routing.Register(router, routing.Handlers{
		Index:              app.handleIndex,
		ShowBox:            app.handleShowBox,
		BoxLogin:           handleBoxLogin,
		BoxLoginPost:       handleBoxLoginPost,
		BoxStatus:          app.handleBoxStatus,
		DownloadBox:        app.handleDownloadBox,
		DownloadFile:       app.handleDownloadFile,
		DownloadThumbnail:  app.handleDownloadThumbnail,
		CreateBox:          app.handleCreateBox,
		ManifestFileUpload: app.handleManifestFileUpload,
		FileStatusUpdate:   app.handleFileStatusUpdate,
		DirectBoxUpload:    app.handleDirectBoxUpload,
		LegacyUpload:       app.handleLegacyUpload,
	})
	app.registerAdminRoutes(router)

	compressed := router.Group("/", gzip.Gzip(gzip.DefaultCompression))
	compressed.Static("/static", "./static")

	boxstore.StartThumbnailWorker(cfg.ThumbnailBatchSize, time.Duration(cfg.ThumbnailIntervalSeconds)*time.Second)

	return router.Run(addr)
}
