package extension_repo

import (
	"fmt"
	"seanime/internal/extension"
	"seanime/internal/plugin"
	"seanime/internal/util"
	"seanime/internal/util/filecache"
	"strings"
)

func getExtensionUserConfigBucketKey(extId string) string {
	return fmt.Sprintf("ext_user_config_%s", extId)
}

var (
	ErrMissingUserConfig      = fmt.Errorf("extension: user config is missing")
	ErrIncompatibleUserConfig = fmt.Errorf("extension: user config is incompatible")
)

// loadUserConfig loads the user config for the given extension by getting it from the cache and modifying the payload.
// This should be called before loading the extension.
// If the user config is absent OR the current user config is outdated, it will return an error.
// When an error is returned, the extension will not be loaded and the user will be prompted to update the extension on the frontend.
func (r *Repository) loadUserConfig(ext *extension.Extension) (err error) {
	defer util.HandlePanicInModuleThen("extension_repo/loadUserConfig", func() {
		err = nil
	})

	// If the extension doesn't define a user config, skip this step
	if ext.UserConfig == nil {
		return nil
	}

	bucket := filecache.NewPermanentBucket(getExtensionUserConfigBucketKey(ext.ID))

	// Get the user config from the cache
	var savedConfig extension.SavedUserConfig
	found, _ := r.fileCacher.GetPerm(bucket, ext.ID, &savedConfig)

	// No user config found but the extension requires it
	if !found && ext.UserConfig.RequiresConfig {
		return ErrMissingUserConfig
	}

	// If the user config is outdated, return an error
	if found && savedConfig.Version != ext.UserConfig.Version {
		return ErrIncompatibleUserConfig
	}

	// Store the user config so it's accessible inside the VMs
	ext.SavedUserConfig = &savedConfig
	if ext.SavedUserConfig.Values == nil {
		ext.SavedUserConfig.Values = make(map[string]string)
	}
	ext.SavedUserConfig.Version = ext.UserConfig.Version

	if found {
		// Replace the placeholders in the payload with the saved values
		for _, field := range ext.UserConfig.Fields {
			savedValue, found := savedConfig.Values[field.Name]
			if !found {
				ext.Payload = strings.ReplaceAll(ext.Payload, fmt.Sprintf("{{%s}}", field.Name), field.Default)
				ext.SavedUserConfig.Values[field.Name] = field.Default // Update saved config
			} else {
				ext.Payload = strings.ReplaceAll(ext.Payload, fmt.Sprintf("{{%s}}", field.Name), savedValue)
				ext.SavedUserConfig.Values[field.Name] = savedValue // Update saved config
			}
		}
		return nil
	} else {
		// If the user config is missing but isn't required, replace the placeholders with the default values
		for _, field := range ext.UserConfig.Fields {
			ext.Payload = strings.ReplaceAll(ext.Payload, fmt.Sprintf("{{%s}}", field.Name), field.Default)
			ext.SavedUserConfig.Values[field.Name] = field.Default // Update saved config
		}
	}

	return nil
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

type ExtensionUserConfig struct {
	UserConfig      *extension.UserConfig      `json:"userConfig"`
	SavedUserConfig *extension.SavedUserConfig `json:"savedUserConfig"`
}

func (r *Repository) GetExtensionUserConfig(id string) (ret *ExtensionUserConfig) {
	ret = &ExtensionUserConfig{
		UserConfig:      nil,
		SavedUserConfig: nil,
	}

	defer util.HandlePanicInModuleThen("extension_repo/GetExtensionUserConfig", func() {})

	ext, found := r.extensionBank.Get(id)
	if !found {
		return
	}

	ret.UserConfig = ext.GetUserConfig()

	bucket := filecache.NewPermanentBucket(getExtensionUserConfigBucketKey(id))

	var savedConfig extension.SavedUserConfig
	found, _ = r.fileCacher.GetPerm(bucket, id, &savedConfig)

	if found {
		ret.SavedUserConfig = &savedConfig
	}

	return
}

func (r *Repository) SaveExtensionUserConfig(id string, savedConfig *extension.SavedUserConfig) (err error) {
	defer util.HandlePanicInModuleWithError("extension_repo/SaveExtensionUserConfig", &err)

	// Save the config
	bucket := filecache.NewPermanentBucket(getExtensionUserConfigBucketKey(id))
	err = r.fileCacher.SetPerm(bucket, id, savedConfig)
	if err != nil {
		return err
	}

	// If the extension is built-in, reload it
	builtinExt, isBuiltIn := r.builtinExtensions.Get(id)
	if isBuiltIn {
		r.reloadBuiltInExtension(builtinExt.Extension, builtinExt.provider)
		return nil
	}

	// Reload the extension
	r.reloadExtension(id)

	return nil
}

// This should be called when the extension is uninstalled
func (r *Repository) deleteExtensionUserConfig(id string) (err error) {
	defer util.HandlePanicInModuleWithError("extension_repo/deleteExtensionUserConfig", &err)

	// Delete the config
	bucket := filecache.NewPermanentBucket(getExtensionUserConfigBucketKey(id))
	err = r.fileCacher.RemovePerm(bucket.Name())
	if err != nil {
		return err
	}

	return nil
}

// This should be called when the extension is uninstalled
func (r *Repository) deletePluginData(id string) {
	defer util.HandlePanicInModuleThen("extension_repo/deletePluginData", func() {
	})

	plugin.GlobalAppContext.DropPluginData(id)
}
