package ui

import (
	"context"
	"fmt"
	"path/filepath"

	"github.com/derailed/k9s/internal/config"
	"github.com/derailed/k9s/internal/render"
	"github.com/derailed/tview"
	"github.com/fsnotify/fsnotify"
	"github.com/rs/zerolog/log"
)

// Synchronizer manages ui event queue.
type synchronizer interface {
	QueueUpdateDraw(func()) *tview.Application
	QueueUpdate(func()) *tview.Application
}

// Configurator represents an application configurationa.
type Configurator struct {
	skinFile string
	Config   *config.Config
	Styles   *config.Styles
	Bench    *config.Bench
}

// HasSkins returns true if a skin file was located.
func (c *Configurator) HasSkins() bool {
	return c.skinFile != ""
}

// StylesUpdater watches for skin file changes.
func (c *Configurator) StylesUpdater(ctx context.Context, s synchronizer) error {
	if !c.HasSkins() {
		return nil
	}

	w, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	go func() {
		for {
			select {
			case evt := <-w.Events:
				_ = evt
				s.QueueUpdateDraw(func() {
					c.RefreshStyles(c.Config.K9s.CurrentCluster)
				})
			case err := <-w.Errors:
				log.Info().Err(err).Msg("Skin watcher failed")
				return
			case <-ctx.Done():
				log.Debug().Msgf("SkinWatcher Done `%s!!", c.skinFile)
				if err := w.Close(); err != nil {
					log.Error().Err(err).Msg("Closing watcher")
				}
				return
			}
		}
	}()

	log.Debug().Msgf("SkinWatcher watching `%s", c.skinFile)
	return w.Add(c.skinFile)
}

// InitBench load benchmark configuration if any.
func (c *Configurator) InitBench(cluster string) {
	var err error
	if c.Bench, err = config.NewBench(BenchConfig(cluster)); err != nil {
		log.Info().Err(err).Msg("No benchmark config file found, using defaults.")
	}
}

// BenchConfig location of the benchmarks configuration file.
func BenchConfig(cluster string) string {
	return filepath.Join(config.K9sHome, config.K9sBench+"-"+cluster+".yml")
}

// RefreshStyles load for skin configuration changes.
func (c *Configurator) RefreshStyles(cluster string) {
	clusterSkins := filepath.Join(config.K9sHome, fmt.Sprintf("%s_skin.yml", cluster))
	if c.Styles == nil {
		c.Styles = config.NewStyles()
	}
	if err := c.Styles.Load(clusterSkins); err != nil {
		log.Info().Msgf("No cluster specific skin file found -- %s", clusterSkins)
	} else {
		log.Debug().Msgf("Found cluster skins %s", clusterSkins)
		c.updateStyles(clusterSkins)
		return
	}

	if err := c.Styles.Load(config.K9sStylesFile); err != nil {
		log.Info().Msgf("No skin file found -- %s. Loading stock skins.", config.K9sStylesFile)
		c.updateStyles("")
		return
	}
	c.updateStyles(config.K9sStylesFile)
}

func (c *Configurator) updateStyles(f string) {
	c.skinFile = f
	c.Styles.Update()

	render.StdColor = config.AsColor(c.Styles.Frame().Status.NewColor)
	render.AddColor = config.AsColor(c.Styles.Frame().Status.AddColor)
	render.ModColor = config.AsColor(c.Styles.Frame().Status.ModifyColor)
	render.ErrColor = config.AsColor(c.Styles.Frame().Status.ErrorColor)
	render.HighlightColor = config.AsColor(c.Styles.Frame().Status.HighlightColor)
	render.CompletedColor = config.AsColor(c.Styles.Frame().Status.CompletedColor)
}
