package cmd

import (
	"context"
	"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"
)

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
}

type Context struct {
	context.Context
}

type Root struct {
	Term      Term      `cmd:"" default:"1" help:"launch term"`
	ListFonts ListFonts `cmd:"" name:"list-fonts" help:"list fonts"`
}

type Term struct {
	ShowVersion        bool   `cmd:"version" help:"Show darktile version information and exit"`
	RewriteConfig      bool   `cmd:"rewrite-config" help:"Write the resultant config after parsing config files and merging with defauls back to the config file"`
	LogFile            string `cmd:"log-file" help:"Debug log file"`
	Shell              string `cmd:"shell" short:"s" help:"Shell to launch terminal with - defaults to configured user shell"`
	Command            string `cmd:"command" short:"c" help:"Command to run when shell starts - use this with caution"`
	ScreenshotAfterMs  int    `cmd:"screenshot-after-ms" help:"Take a screenshot after this many milliseconds"`
	ScreenshotFilename string `cmd:"screenshot-filename" help:"Filename to store screenshot taken by --screenshot-after-ms"`
	ThemePath          string `cmd:"theme-path" help:"Path to a theme file to use instead of the default"`
}

func (r *Term) Run(ctx *Context) error {
	if r.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 r.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 r.ThemePath != "" {
		theme, err = config2.LoadThemeFromPath(conf, r.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 r.LogFile != "" {
		termOpts = append(termOpts, termutil2.WithLogFile(r.LogFile))
	}
	if r.Shell != "" {
		termOpts = append(termOpts, termutil2.WithShell(r.Shell))
	}
	if r.Command != "" {
		termOpts = append(termOpts, termutil2.WithInitialCommand(r.Command))
	}

	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 r.ScreenshotAfterMs > 0 {
		options = append(options, gui2.WithStartupFunc(func(g *gui2.GUI) {
			<-time.After(time.Duration(r.ScreenshotAfterMs) * time.Millisecond)
			g.RequestScreenshot(r.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()
}
