package cmd

import (
	"errors"
	"fmt"
	"image"
	"os"
	"time"
	config2 "tuxpa.in/t/erm/app/darktile/config"
	gui2 "tuxpa.in/t/erm/app/darktile/gui"
	"tuxpa.in/t/erm/app/darktile/hinters"
	termutil2 "tuxpa.in/t/erm/app/darktile/termutil"
	"tuxpa.in/t/erm/app/darktile/version"

	"github.com/spf13/cobra"
)

var rewriteConfig bool
var debugFile string
var initialCommand string
var shell string
var screenshotAfterMS int
var screenshotFilename string
var themePath string
var showVersion bool

var rootCmd = &cobra.Command{
	Use:          os.Args[0],
	SilenceUsage: true,
	RunE: func(c *cobra.Command, args []string) error {

		if showVersion {
			fmt.Println(version.Version)
			os.Exit(0)
		}

		var startupErrors []error
		var fileNotFound *config2.ErrorFileNotFound

		conf, err := config2.LoadConfig()
		if err != nil {
			if !errors.As(err, &fileNotFound) {
				startupErrors = append(startupErrors, err)
			}
			conf = config2.DefaultConfig()
		}

		if rewriteConfig {
			if _, err := conf.Save(); err != nil {
				return fmt.Errorf("failed to write config file: %w", err)
			}
			fmt.Println("Config written.")
			return nil
		}

		var theme *termutil2.Theme

		if themePath != "" {
			theme, err = config2.LoadThemeFromPath(conf, themePath)
			if err != nil {
				return fmt.Errorf("failed to load theme: %s", err)
			}
		} else {
			theme, err = config2.LoadTheme(conf)
			if err != nil {
				if !errors.As(err, &fileNotFound) {
					startupErrors = append(startupErrors, err)
				}
				theme, err = config2.DefaultTheme(conf)
				if err != nil {
					return fmt.Errorf("failed to load default theme: %w", err)
				}
			}
		}

		termOpts := []termutil2.Option{
			termutil2.WithTheme(theme),
		}

		if debugFile != "" {
			termOpts = append(termOpts, termutil2.WithLogFile(debugFile))
		}
		if shell != "" {
			termOpts = append(termOpts, termutil2.WithShell(shell))
		}
		if initialCommand != "" {
			termOpts = append(termOpts, termutil2.WithInitialCommand(initialCommand))
		}

		terminal := termutil2.New(termOpts...)

		options := []gui2.Option{
			gui2.WithFontDPI(conf.Font.DPI),
			gui2.WithFontSize(conf.Font.Size),
			gui2.WithFontFamily(conf.Font.Family),
			gui2.WithOpacity(conf.Opacity),
			gui2.WithLigatures(conf.Font.Ligatures),
		}

		if conf.Cursor.Image != "" {
			img, err := getImageFromFilePath(conf.Cursor.Image)
			if err != nil {
				startupErrors = append(startupErrors, err)
			} else {
				options = append(options, gui2.WithCursorImage(img))
			}
		}

		if screenshotAfterMS > 0 {
			options = append(options, gui2.WithStartupFunc(func(g *gui2.GUI) {
				<-time.After(time.Duration(screenshotAfterMS) * time.Millisecond)
				g.RequestScreenshot(screenshotFilename)
			}))
		}

		// load all hinters
		for _, hinter := range hinters.All() {
			options = append(options, gui2.WithHinter(hinter))
		}

		g, err := gui2.New(terminal, options...)
		if err != nil {
			return err
		}

		for _, err := range startupErrors {
			g.ShowError(err.Error())
		}

		return g.Run()
	},
}

func getImageFromFilePath(filePath string) (image.Image, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	image, _, err := image.Decode(f)
	return image, err
}

func Execute() error {
	rootCmd.Flags().BoolVar(&showVersion, "version", showVersion, "Show darktile version information and exit")
	rootCmd.Flags().BoolVar(&rewriteConfig, "rewrite-config", rewriteConfig, "Write the resultant config after parsing config files and merging with defauls back to the config file")
	rootCmd.Flags().StringVar(&debugFile, "log-file", debugFile, "Debug log file")
	rootCmd.Flags().StringVarP(&shell, "shell", "s", shell, "Shell to launch terminal with - defaults to configured user shell")
	rootCmd.Flags().StringVarP(&initialCommand, "command", "c", initialCommand, "Command to run when shell starts - use this with caution")
	rootCmd.Flags().IntVar(&screenshotAfterMS, "screenshot-after-ms", screenshotAfterMS, "Take a screenshot after this many milliseconds")
	rootCmd.Flags().StringVar(&screenshotFilename, "screenshot-filename", screenshotFilename, "Filename to store screenshot taken by --screenshot-after-ms")
	rootCmd.Flags().StringVar(&themePath, "theme-path", themePath, "Path to a theme file to use instead of the default")
	return rootCmd.Execute()
}
