// TODO: Add EUPL banner everywhere
package main

import (
	"embed"
	"flag"
	"path"
	"time"

	"git.mstar.dev/mstar/goutils/other"
	"github.com/go-webauthn/webauthn/webauthn"
	"github.com/mstarongithub/passkey"
	"github.com/rs/zerolog/log"
	"gopkg.in/natefinch/lumberjack.v2"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"

	"git.mstar.dev/mstar/linstrom/config"
	"git.mstar.dev/mstar/linstrom/server"
	"git.mstar.dev/mstar/linstrom/shared"
	"git.mstar.dev/mstar/linstrom/storage"
	storagenew "git.mstar.dev/mstar/linstrom/storage-new"
	"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
	"git.mstar.dev/mstar/linstrom/storage/cache"
	webdebug "git.mstar.dev/mstar/linstrom/web/debug"
	webpublic "git.mstar.dev/mstar/linstrom/web/public"
)

// TODO: Add frontend overwrite
// Serve frontend files from a given directory instead of embedded data

//go:embed frontend-reactive/dist/* frontend-reactive/dist/assets
var reactiveFS embed.FS

//go:embed frontend-noscript
var nojsFS embed.FS

//go:embed duck.webp
var defaultDuck string

func main() {
	other.SetupFlags()
	flag.Parse()
	logfile := getLogFilePathOrNil()
	if logfile == nil {
		other.ConfigureLogging(nil)
		log.Warn().Msg("Can't write to target logfile, not creating one")
	} else {
		other.ConfigureLogging(&lumberjack.Logger{
			Filename:   *logfile,
			MaxSize:    500, // Megabytes
			MaxBackups: 3,
			MaxAge:     3, // Days
			Compress:   false,
		})
		log.Info().Str("logfile", *logfile).Msg("Logging to stderr and logfile")
	}
	if err := config.ReadAndWriteToGlobal(*shared.FlagConfigFile); err != nil {
		log.Fatal().
			Err(err).
			Str("config-file", *shared.FlagConfigFile).
			Msg("Failed to read config and couldn't write default")
	}

	// Request to only check config
	if *shared.FlagConfigOnly {
		return
	}
	if *shared.FlagStartNew {
		log.Info().Msg("Starting new system")
		newServer()
	} else {
		log.Info().Msg("Starting old system")
		oldServer()
	}
}

func getLogFilePathOrNil() *string {
	logfile := *shared.FlagLogFile
	logPath := path.Dir(logfile)
	if shared.IsWritable(logPath) {
		return &logfile
	} else {
		return nil
	}
}

func oldServer() {
	storageCache, err := cache.NewCache(
		config.GlobalConfig.Storage.MaxInMemoryCacheSize,
		config.GlobalConfig.Storage.RedisUrl,
	)
	if err != nil {
		log.Fatal().Err(err).Msg("Failed to start cache")
	}

	// var store *storage.Storage
	// if config.GlobalConfig.Storage.DbIsPostgres != nil && *config.GlobalConfig.Storage.DbIsPostgres {
	// 	store, err = storage.NewStoragePostgres(config.GlobalConfig.Storage.DatabaseUrl, storageCache)
	// } else {
	// 	store, err = storage.NewStorageSqlite(config.GlobalConfig.Storage.DatabaseUrl, storageCache)
	// }
	//
	store, err := storage.NewStorage(config.GlobalConfig.Storage.BuildPostgresDSN(), storageCache)

	if err != nil {
		log.Fatal().Err(err).Msg("Failed to setup storage")
	}

	pkey, err := passkey.New(passkey.Config{
		WebauthnConfig: &webauthn.Config{
			RPDisplayName: "Linstrom",
			RPID:          "localhost",
			RPOrigins:     []string{"http://localhost:8000"},
		},
		UserStore:     store,
		SessionStore:  store,
		SessionMaxAge: time.Hour * 24,
	}, passkey.WithLogger(&shared.ZerologWrapper{}))
	if err != nil {
		log.Fatal().Err(err).Msg("Failed to setup passkey support")
	}

	server := server.NewServer(
		store,
		pkey,
		shared.NewFSWrapper(reactiveFS, "frontend-reactive/dist/", false),
		shared.NewFSWrapper(nojsFS, "frontend-noscript/", false),
		&defaultDuck,
	)
	server.Start(":8000")
	// TODO: Set up media server
	// TODO: Set up queues
	// TODO: Set up plugins
}

func newServer() {
	db, err := gorm.Open(
		postgres.Open(config.GlobalConfig.Storage.BuildPostgresDSN()),
		&gorm.Config{
			Logger: shared.NewGormLogger(log.Logger),
		},
	)
	if err != nil {
		log.Fatal().Err(err).Msg("Failed to start db")
	}
	dbgen.SetDefault(db)
	if err = storagenew.Migrate(db); err != nil {
		log.Fatal().Err(err).Msg("Failed to automigrate structure")
	}
	if err = storagenew.InsertSelf(); err != nil {
		log.Fatal().Err(err).Msg("Failed to insert self properly")
	}
	if *shared.FlagStartDebugServer {
		go func() {
			log.Info().Msg("Starting debug server")
			if err = webdebug.New(*shared.FlagDebugPort).Start(); err != nil {
				log.Fatal().Err(err).Msg("Debug server failed")
			}
		}()
	}
	log.Info().Msg("Starting public server")
	public := webpublic.New(":8080", &defaultDuck)
	public.Start()
}
