package extension

import (
	"strings"
)

type Consumer interface {
	InitExtensionBank(bank *UnifiedBank)
}

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

type Type string

type Language string

type PluginPermissionScope string

const (
	TypeAnimeTorrentProvider Type = "anime-torrent-provider"
	TypeMangaProvider        Type = "manga-provider"
	TypeOnlinestreamProvider Type = "onlinestream-provider"
	TypePlugin               Type = "plugin"
)

const (
	LanguageJavascript Language = "javascript"
	LanguageTypescript Language = "typescript"
	LanguageGo         Language = "go"
)

type Extension struct {
	// ID is the unique identifier of the extension
	// It must be unique across all extensions
	// It must start with a letter and contain only alphanumeric characters
	ID               string `json:"id"`      // e.g. "extension-example"
	Name             string `json:"name"`    // e.g. "Extension"
	Version          string `json:"version"` // e.g. "1.0.0"
	SemverConstraint string `json:"semverConstraint,omitempty"`
	// The URI to the extension manifest file.
	// This is "builtin" if the extension is built-in and "" if the extension is local.
	ManifestURI string `json:"manifestURI"` // e.g. "http://cdn.something.app/extensions/extension-example/manifest.json"
	// The programming language of the extension
	// It is used to determine how to interpret the extension
	Language Language `json:"language"` // e.g. "go"
	// Type is the area of the application the extension is targeting
	Type        Type   `json:"type"`        // e.g. "anime-torrent-provider"
	Description string `json:"description"` // e.g. "This extension provides torrents"
	Author      string `json:"author"`      // e.g. "Seanime"
	// Icon is the URL to the extension icon
	Icon string `json:"icon"`
	// Website is the URL to the extension website
	Website string `json:"website"`
	// ISO 639-1 language code.
	// Set this to "multi" if the extension supports multiple languages.
	// Defaults to "en".
	Lang string `json:"lang"`
	// List of permissions asked by the extension.
	// The user must grant these permissions before the extension can be loaded.
	Permissions []string    `json:"permissions,omitempty"` // NOT IMPLEMENTED
	UserConfig  *UserConfig `json:"userConfig,omitempty"`
	// Payload is the content of the extension.
	Payload string `json:"payload"`
	// PayloadURI is the URI to the extension payload.
	// It can be used as an alternative to the Payload field to load the payload from a remote source.
	// If the extension is in debug mode, this can be a file path to the local payload.
	PayloadURI string `json:"payloadURI,omitempty"`
	// Plugin is the manifest of the extension if it is a plugin.
	Plugin *PluginManifest `json:"plugin,omitempty"`

	// IsDevelopment is true if the extension is in development mode.
	// If true, the extension code will be loaded from PayloadURI and allow you to edit the code from an editor and reload the extension without restarting the application.
	IsDevelopment bool `json:"isDevelopment,omitempty"`

	SavedUserConfig *SavedUserConfig `json:"-"` // Contains the saved user config for the extension
}

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

// BaseExtension is the base interface for all extensions
// An extension is a JS file that is loaded by HTTP request
type BaseExtension interface {
	GetID() string
	GetName() string
	GetVersion() string
	GetManifestURI() string
	GetLanguage() Language
	GetType() Type
	GetDescription() string
	GetAuthor() string
	GetPayload() string
	GetPayloadURI() string
	GetLang() string
	GetIcon() string
	GetWebsite() string
	GetPermissions() []string
	GetUserConfig() *UserConfig
	GetSavedUserConfig() *SavedUserConfig
	GetIsDevelopment() bool
}

type Configurable interface {
	SetSavedUserConfig(config SavedUserConfig)
}

func ToExtensionData(ext BaseExtension) *Extension {
	return &Extension{
		ID:              ext.GetID(),
		Name:            ext.GetName(),
		Version:         ext.GetVersion(),
		ManifestURI:     ext.GetManifestURI(),
		Language:        ext.GetLanguage(),
		Lang:            GetExtensionLang(ext.GetLang()),
		Type:            ext.GetType(),
		Description:     ext.GetDescription(),
		Author:          ext.GetAuthor(),
		Permissions:     ext.GetPermissions(),
		UserConfig:      ext.GetUserConfig(),
		Icon:            ext.GetIcon(),
		Website:         ext.GetWebsite(),
		Payload:         ext.GetPayload(),
		PayloadURI:      ext.GetPayloadURI(),
		IsDevelopment:   ext.GetIsDevelopment(),
		SavedUserConfig: ext.GetSavedUserConfig(),
	}
}

func GetExtensionLang(lang string) string {
	if lang == "" {
		return "en"
	}
	if lang == "all" {
		return "multi"
	}
	return lang
}

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

type InvalidExtensionErrorCode string

const (
	// InvalidExtensionManifestError is returned when the extension manifest is invalid
	InvalidExtensionManifestError InvalidExtensionErrorCode = "invalid_manifest"
	// InvalidExtensionPayloadError is returned when the extension code is invalid / obsolete
	InvalidExtensionPayloadError    InvalidExtensionErrorCode = "invalid_payload"
	InvalidExtensionUserConfigError InvalidExtensionErrorCode = "user_config_error"
	// InvalidExtensionAuthorizationError is returned when some authorization scopes have not been granted
	InvalidExtensionAuthorizationError InvalidExtensionErrorCode = "invalid_authorization"
	// InvalidExtensionPluginPermissionsNotGranted is returned when the plugin permissions have not been granted
	InvalidExtensionPluginPermissionsNotGranted InvalidExtensionErrorCode = "plugin_permissions_not_granted"
	// InvalidExtensionSemverConstraintError is returned when the semver constraint is invalid
	InvalidExtensionSemverConstraintError InvalidExtensionErrorCode = "invalid_semver_constraint"
)

type InvalidExtension struct {
	// Auto-generated ID
	ID                          string                    `json:"id"`
	Path                        string                    `json:"path"`
	Extension                   Extension                 `json:"extension"`
	Reason                      string                    `json:"reason"`
	Code                        InvalidExtensionErrorCode `json:"code"`
	PluginPermissionDescription string                    `json:"pluginPermissionDescription,omitempty"`
}

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

type UserConfig struct {
	// The version of the extension configuration.
	// This is used to determine if the configuration has changed.
	Version int `json:"version"`
	// Whether the extension requires user configuration.
	RequiresConfig bool `json:"requiresConfig"`
	// This will be used to generate the user configuration form, and the values will be passed to the extension.
	Fields []ConfigField `json:"fields"`
}

type Preferences struct {
	// This will be used to generate the preference form, and the values will be passed to the extension.
	Fields []ConfigField `json:"fields"`
}

type SavedUserConfig struct {
	// The version of the extension configuration.
	Version int `json:"version"`
	// The values of the user configuration fields.
	Values map[string]string `json:"values"`
}

const (
	ConfigFieldTypeText   ConfigFieldType = "text"
	ConfigFieldTypeSwitch ConfigFieldType = "switch"
	ConfigFieldTypeSelect ConfigFieldType = "select"
)

type (

	// ConfigField represents a field in an extension's configuration.
	// The fields are defined in the manifest file.
	ConfigField struct {
		Type    ConfigFieldType           `json:"type"`
		Name    string                    `json:"name"`
		Label   string                    `json:"label"`
		Options []ConfigFieldSelectOption `json:"options,omitempty"`
		Default string                    `json:"default,omitempty"`
	}

	ConfigFieldType string

	ConfigFieldSelectOption struct {
		Value string `json:"value"`
		Label string `json:"label"`
	}

	ConfigFieldValueValidator func(value string) error
)

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

func (p *PluginPermissionScope) String() string {
	return string(*p)
}

func (p *PluginPermissionScope) Is(str string) bool {
	return strings.EqualFold(string(*p), str)
}
