package commands

import (
	"os"

	"git.sr.ht/~rjarry/aerc/app"
	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib/log"
	"git.sr.ht/~rjarry/aerc/lib/ui"
)

type Reload struct {
	Binds bool   `opt:"-B" desc:"Reload binds.conf."`
	Conf  bool   `opt:"-C" desc:"Reload aerc.conf."`
	Style string `opt:"-s" complete:"CompleteStyle" desc:"Reload the specified styleset."`
}

func init() {
	Register(Reload{})
}

func (Reload) Description() string {
	return "Hot-reload configuration files."
}

func (r *Reload) CompleteStyle(s string) []string {
	var files []string
	for _, dir := range config.Ui.StyleSetDirs {
		entries, err := os.ReadDir(dir)
		if err != nil {
			log.Debugf("could not read directory '%s': %v", dir,
				err)
			continue
		}
		for _, e := range entries {
			if e.IsDir() {
				continue
			}
			files = append(files, e.Name())
		}
	}
	return FilterList(files, s, nil)
}

func (Reload) Context() CommandContext {
	return GLOBAL
}

func (Reload) Aliases() []string {
	return []string{"reload"}
}

func (r Reload) Execute(args []string) error {
	if !r.Binds && !r.Conf && r.Style == "" {
		r.Binds = true
		r.Conf = true
		r.Style = config.Ui.StyleSetName
	}

	reconfigure := false

	if r.Binds {
		f, err := config.ReloadBinds()
		if err != nil {
			return err
		}
		app.PushSuccess("Binds reloaded: " + f)
	}

	if r.Conf {
		f, err := config.ReloadConf()
		if err != nil {
			return err
		}
		app.PushSuccess("Conf reloaded: " + f)
		reconfigure = true
	}

	if r.Style != "" {
		config.Ui.ClearCache()
		config.Ui.StyleSetName = r.Style
		err := config.Ui.LoadStyle()
		if err != nil {
			return err
		}
		app.PushSuccess("Styleset: " + r.Style)
		reconfigure = true
	}

	if !reconfigure {
		return nil
	}

	// reload account views and message stores
	for _, name := range app.AccountNames() {

		// rebuild account view
		view, err := app.Account(name)
		if err != nil {
			continue
		}

		dirlist := view.Directories()
		if dirlist == nil {
			continue
		}

		wantTree := config.Ui.ForAccount(name).DirListTree
		dirlist = adjustDirlist(dirlist, wantTree)
		view.SetDirectories(dirlist)

		// now rebuild grid with correct dirlist
		view.Configure()

		// reconfigure the message stores
		for _, dir := range dirlist.List() {
			store, ok := dirlist.MsgStore(dir)
			if !ok {
				continue
			}
			uiConf := dirlist.UiConfig(dir)
			store.Configure(view.SortCriteria(uiConf))
		}
		ui.Invalidate()
	}

	// reload message viewers
	doTabs(func(tab *ui.Tab) {
		mv, ok := tab.Content.(*app.MessageViewer)
		if !ok {
			return
		}
		reloaded, err := app.NewMessageViewer(
			mv.SelectedAccount(),
			mv.MessageView(),
		)
		if err != nil {
			app.PushError(err.Error())
			return
		}
		app.ReplaceTab(mv, reloaded, tab.Name, false)
	})

	// reload composers
	doTabs(func(tab *ui.Tab) {
		c, ok := tab.Content.(*app.Composer)
		if !ok {
			return
		}
		_ = c.SwitchAccount(c.Account())
	})

	return nil
}

func adjustDirlist(d app.DirectoryLister, wantTree bool) app.DirectoryLister {
	switch d := d.(type) {
	case *app.DirectoryList:
		if wantTree {
			log.Tracef("dirlist: build tree")
			tree := app.NewDirectoryTree(d)
			tree.SelectedMsgStore()
			return tree
		}
		log.Tracef("dirlist: already dirlist")
		return d
	case *app.DirectoryTree:
		if !wantTree {
			log.Tracef("dirtree: get dirlist")
			return d.DirectoryList
		}
		log.Tracef("dirtree: already tree")
		return d
	default:
		return d
	}
}

func doTabs(do func(*ui.Tab)) {
	var tabname string
	if t := app.SelectedTab(); t != nil {
		tabname = t.Name
	}
	for i := range app.TabNames() {
		tab := app.GetTab(i)
		if tab == nil {
			continue
		}
		do(tab)
	}
	if tabname != "" {
		app.SelectTab(tabname)
	}
}
