package server

import (
	"encoding/json"
	"html/template"
	"path/filepath"
	"time"

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

	"warpbox/lib/activity"
	"warpbox/lib/alerts"
	"warpbox/lib/boxstore"
	"warpbox/lib/config"
	"warpbox/lib/routing"
	"warpbox/lib/security"
)

type App struct {
	config                *config.Config
	settingsOverridesPath string
	activityStore         *activity.Store
	alertStore            *alerts.Store
	securityGuard         *security.Guard
}

func Run(addr string) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := cfg.EnsureDirectories(); err != nil {
		return err
	}
	overridesPath := filepath.Join(cfg.DBDir, config.AdminSettingsOverrideFilename)
	overrides, err := config.ReadAdminSettingsOverrides(overridesPath)
	if err != nil {
		return err
	}
	if err := cfg.ApplyOverrides(overrides); err != nil {
		return err
	}

	applyBoxstoreRuntimeConfig(cfg)

	app := &App{
		config:                cfg,
		settingsOverridesPath: overridesPath,
		activityStore:         activity.NewStore(filepath.Join(cfg.DBDir, "activity_log.json")),
		alertStore:            alerts.NewStore(filepath.Join(cfg.DBDir, "alerts.json")),
		securityGuard:         security.NewGuard(),
	}
	app.reloadSecurityConfig()

	router := gin.Default()
	router.Use(app.securityMiddleware())
	router.NoRoute(app.handleNoRoute)
	htmlTemplates, err := loadHTMLTemplates()
	if err != nil {
		return err
	}
	router.SetHTMLTemplate(htmlTemplates)

	routing.Register(router, routing.Handlers{
		Health:             app.handleHealth,
		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,

		AdminLogin:          app.handleAdminLogin,
		AdminLoginPost:      app.handleAdminLoginPost,
		AdminLogout:         app.handleAdminLogout,
		AdminDashboard:      app.handleAdminDashboard,
		AdminAlerts:         app.handleAdminAlerts,
		AdminBoxes:          app.handleAdminBoxes,
		AdminBoxesAction:    app.handleAdminBoxesAction,
		AdminUsers:          app.handleAdminUsers,
		AdminActivity:       app.handleAdminActivity,
		AdminSecurity:       app.handleAdminSecurity,
		AdminAlertsAction:   app.handleAdminAlertsAction,
		AdminSecurityAction: app.handleAdminSecurityAction,
		AdminSettings:       app.handleAdminSettings,
		AdminSettingsExport: app.handleAdminSettingsExport,
		AdminSettingsSave:   app.handleAdminSettingsSave,
		AdminSettingsImport: app.handleAdminSettingsImport,
		AdminSettingsReset:  app.handleAdminSettingsReset,
		AdminAuth:           app.adminAuthMiddleware,
	})

	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)
}

func loadHTMLTemplates() (*template.Template, error) {
	tmpl := template.New("").Funcs(template.FuncMap{
		"toJSON": func(value any) template.JS {
			data, err := json.Marshal(value)
			if err != nil {
				return template.JS("null")
			}
			return template.JS(data)
		},
	})
	for _, pattern := range []string{
		"templates/*.html",
		"templates/admin/*.html",
		"templates/admin/partials/*.html",
	} {
		var err error
		tmpl, err = tmpl.ParseGlob(pattern)
		if err != nil {
			return nil, err
		}
	}
	return tmpl, nil
}

func applyBoxstoreRuntimeConfig(cfg *config.Config) {
	boxstore.SetUploadRoot(cfg.UploadsDir)
	boxstore.SetOneTimeDownloadExpiry(cfg.OneTimeDownloadExpirySeconds)
}

func (app *App) handleHealth(c *gin.Context) {
	c.JSON(200, gin.H{
		"status": "healthy",
	})
}
