// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s

package config

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"strings"

	"github.com/derailed/k9s/internal/config/data"
	"github.com/derailed/k9s/internal/config/json"

	"github.com/adrg/xdg"
	"gopkg.in/yaml.v2"
)

const k9sPluginsDir = "k9s/plugins"

// Plugins represents a collection of plugins.
type Plugins struct {
	Plugins map[string]Plugin `yaml:"plugins"`
}

// Plugin describes a K9s plugin.
type Plugin struct {
	Scopes      []string `yaml:"scopes"`
	Args        []string `yaml:"args"`
	ShortCut    string   `yaml:"shortCut"`
	Override    bool     `yaml:"override"`
	Pipes       []string `yaml:"pipes"`
	Description string   `yaml:"description"`
	Command     string   `yaml:"command"`
	Confirm     bool     `yaml:"confirm"`
	Background  bool     `yaml:"background"`
	Dangerous   bool     `yaml:"dangerous"`
}

func (p Plugin) String() string {
	return fmt.Sprintf("[%s] %s(%s)", p.ShortCut, p.Command, strings.Join(p.Args, " "))
}

// NewPlugins returns a new plugin.
func NewPlugins() Plugins {
	return Plugins{
		Plugins: make(map[string]Plugin),
	}
}

// Load K9s plugins.
func (p Plugins) Load(path string) error {
	var errs error

	if err := p.load(AppPluginsFile); err != nil {
		errs = errors.Join(errs, err)
	}
	if err := p.load(path); err != nil {
		errs = errors.Join(errs, err)
	}

	for _, dataDir := range xdg.DataDirs {
		if err := p.loadPluginDir(filepath.Join(dataDir, k9sPluginsDir)); err != nil {
			errs = errors.Join(errs, err)
		}
	}

	return errs
}

func (p Plugins) loadPluginDir(dir string) error {
	pluginFiles, err := os.ReadDir(dir)
	if err != nil {
		return nil
	}

	var errs error
	for _, file := range pluginFiles {
		if file.IsDir() || !isYamlFile(file.Name()) {
			continue
		}
		bb, err := os.ReadFile(filepath.Join(dir, file.Name()))
		if err != nil {
			errs = errors.Join(errs, err)
		}
		var plugin Plugin
		if err = yaml.Unmarshal(bb, &plugin); err != nil {
			return err
		}
		p.Plugins[strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))] = plugin
	}

	return errs
}

func (p *Plugins) load(path string) error {
	if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
		return nil
	}
	bb, err := os.ReadFile(path)
	if err != nil {
		return err
	}
	if err := data.JSONValidator.Validate(json.PluginsSchema, bb); err != nil {
		return fmt.Errorf("validation failed for %q: %w", path, err)
	}
	var pp Plugins
	if err := yaml.Unmarshal(bb, &pp); err != nil {
		return err
	}
	for k, v := range pp.Plugins {
		p.Plugins[k] = v
	}

	return nil
}
