/*
Copyright © 2023 Evan Fiordeliso <evan.fiordeliso@gmail.com>
*/
package cmd

import (
	"os"

	"github.com/dgraph-io/badger/v2"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"go.fifitido.net/ytdl-web/app/controllers"
	"go.fifitido.net/ytdl-web/config"
	"go.fifitido.net/ytdl-web/pkg/server"
	"go.fifitido.net/ytdl-web/pkg/utils"
	"go.fifitido.net/ytdl-web/pkg/ytdl"
	"go.fifitido.net/ytdl-web/pkg/ytdl/cache"
	"golang.org/x/exp/slog"
)

var (
	cfgFile string
	cfg     *config.Config

	rootCmd = &cobra.Command{
		Use:   "ytdl-web",
		Short: "A web frontend for yt-dlp",
		Long: `YTDL Web

A web application that grabs the links to videos from over a
thousand websites using the yt-dlp project under the hood.`,
		RunE: func(cmd *cobra.Command, args []string) error {
			logger := slog.Default()

			db, err := badger.Open(
				badger.
					DefaultOptions(cfg.Cache.DirPath).
					WithLogger(utils.NewBadgerLogger(logger.With("module", "badger"))),
			)
			if err != nil {
				return err
			}
			defer db.Close()

			cache := cache.NewDefaultMetadataCache(db)
			ytdl := ytdl.NewYtdl(cfg, slog.Default(), cache)

			s := server.New(
				server.
					DefaultOptions().
					WithListenAddr(viper.GetString("http.listen")).
					WithListenPort(viper.GetInt("http.port")).
					WithLogger(logger.With("module", "server")),
			)

			s.MountController("/", controllers.NewHomeController(ytdl))
			s.MountController("/download", controllers.NewDownloadController(ytdl))

			return s.ListenAndServe()
		},
	}
)

func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $XDG_CONFIG_HOME/ytdl-web/config.yml)")

	rootCmd.PersistentFlags().IntP("port", "p", 8080, "port to listen on")
	rootCmd.PersistentFlags().StringP("listen", "l", "", "address to listen on")
	rootCmd.PersistentFlags().StringP("base-path", "b", "", "the base path, used when behind reverse proxy")
	rootCmd.PersistentFlags().StringP("ytdlp-path", "y", "", "the path to the yt-dlp binary, used when it is not in $PATH")
	rootCmd.PersistentFlags().BoolP("cookies-enabled", "C", false, "whether cookies are enabled")
	rootCmd.PersistentFlags().StringP("cookies", "c", "", "the path to the cookies file")

	// trunk-ignore-begin(golangci-lint/errcheck): Ignoring errors
	viper.BindPFlag("http.port", rootCmd.PersistentFlags().Lookup("port"))
	viper.BindPFlag("http.listen", rootCmd.PersistentFlags().Lookup("listen"))
	viper.BindPFlag("http.basePath", rootCmd.PersistentFlags().Lookup("base-path"))
	viper.BindPFlag("ytdlp.binaryPath", rootCmd.PersistentFlags().Lookup("ytdlp-path"))
	viper.BindPFlag("cookies.enabled", rootCmd.PersistentFlags().Lookup("cookies-enabled"))
	viper.BindPFlag("cookies.filePath", rootCmd.PersistentFlags().Lookup("cookies"))
	// trunk-ignore-end(golangci-lint/errcheck)
}

func initConfig() {
	var err error
	if cfgFile != "" {
		cfg, err = config.LoadConfig(cfgFile)
	} else {
		cfg, err = config.LoadConfig()
	}

	if err != nil {
		slog.Error("Error loading configuration", slog.String("error", err.Error()))
		os.Exit(1)
	}

	initLogging()

	slog.Info("Configuration loaded")
}

func initLogging() {
	var handler slog.Handler

	if cfg.IsProduction() {
		handler = slog.HandlerOptions{
			AddSource: true,
			Level:     slog.LevelInfo,
		}.NewJSONHandler(os.Stdout)
	} else {
		handler = slog.HandlerOptions{
			AddSource: true,
			Level:     slog.LevelDebug,
		}.NewTextHandler(os.Stdout)
	}

	slog.SetDefault(slog.New(handler))
}
