package plugin_ui

import (
	"errors"

	"github.com/dop251/goja"
	"github.com/goccy/go-json"
)

const (
	MAX_FIELD_REFS = 100
)

// ComponentManager is used to register components.
// Any higher-order UI system must use this to register components. (Tray)
type ComponentManager struct {
	ctx *Context

	// Last rendered components
	lastRenderedComponents interface{}
}

// jsDiv
//
//	Example:
//	const div = tray.div({
//		items: [
//			tray.text("Some text"),
//		]
//	})
func (c *ComponentManager) jsDiv(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "div", []ComponentProp{
		{Name: "items", Type: "array", Required: false, OptionalFirstArg: true},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

// jsFlex
//
//	Example:
//	const flex = tray.flex({
//		items: [
//			tray.button({ label: "A button", onClick: "my-action" }),
//			true ? tray.text("Some text") : null,
//		]
//	})
//	tray.render(() => flex)
func (c *ComponentManager) jsFlex(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "flex", []ComponentProp{
		{Name: "items", Type: "array", Required: false, OptionalFirstArg: true},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "gap", Type: "number", Required: false, Default: 2, Validate: validateType("number")},
		{Name: "direction", Type: "string", Required: false, Default: "row", Validate: validateType("string")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

// jsStack
//
//	Example:
//	const stack = tray.stack({
//		items: [
//			tray.text("Some text"),
//		]
//	})
func (c *ComponentManager) jsStack(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "stack", []ComponentProp{
		{Name: "items", Type: "array", Required: false, OptionalFirstArg: true},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "gap", Type: "number", Required: false, Default: 2, Validate: validateType("number")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

// jsText
//
//	Example:
//	const text = tray.text("Some text")
//	// or
//	const text = tray.text({ text: "Some text" })
func (c *ComponentManager) jsText(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "text", []ComponentProp{
		{Name: "text", Type: "string", Required: true, OptionalFirstArg: true, Validate: validateType("string")},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

// jsButton
//
//	Example:
//	const button = tray.button("Click me")
//	// or
//	const button = tray.button({ label: "Click me", onClick: "my-action" })
func (c *ComponentManager) jsButton(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "button", []ComponentProp{
		{Name: "label", Type: "string", Required: true, OptionalFirstArg: true, Validate: validateType("string")},
		{Name: "onClick", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "intent", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "disabled", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "loading", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "size", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

// jsAnchor
//
//	Example:
//	const anchor = tray.anchor("Click here", { href: "https://example.com" })
//	// or
//	const anchor = tray.anchor({ text: "Click here", href: "https://example.com" })
func (c *ComponentManager) jsAnchor(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "anchor", []ComponentProp{
		{Name: "text", Type: "string", Required: true, OptionalFirstArg: true, Validate: validateType("string")},
		{Name: "href", Type: "string", Required: true, Validate: validateType("string")},
		{Name: "target", Type: "string", Required: false, Default: "_blank", Validate: validateType("string")},
		{Name: "onClick", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

////////////////////////////////////////////
// Fields
////////////////////////////////////////////

// jsInput
//
//	Example:
//	const input = tray.input("Enter your name") // placeholder as shorthand
//	// or
//	const input = tray.input({
//		placeholder: "Enter your name",
//		value: "John",
//		onChange: "input-changed"
//	})
func (c *ComponentManager) jsInput(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "input", []ComponentProp{
		{Name: "label", Type: "string", Required: false, OptionalFirstArg: true, Validate: validateType("string")},
		{Name: "placeholder", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "value", Type: "string", Required: false, Default: "", Validate: validateType("string")},
		{Name: "onChange", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "onSelect", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "fieldRef", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "disabled", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "textarea", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "size", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

func validateOptions(v interface{}) error {
	if v == nil {
		return errors.New("options must be an array of objects")
	}
	marshaled, err := json.Marshal(v)
	if err != nil {
		return err
	}
	var arr []map[string]interface{}
	if err := json.Unmarshal(marshaled, &arr); err != nil {
		return err
	}
	if len(arr) == 0 {
		return nil
	}
	for _, option := range arr {
		if _, ok := option["label"]; !ok {
			return errors.New("options must be an array of objects with a label property")
		}
		if _, ok := option["value"]; !ok {
			return errors.New("options must be an array of objects with a value property")
		}
	}
	return nil
}

// jsSelect
//
//	Example:
//	const select = tray.select("Select an item", {
//		options: [{ label: "Item 1", value: "item1" }, { label: "Item 2", value: "item2" }],
//		onChange: "select-changed"
//	})
//	// or
//	const select = tray.select({
//		placeholder: "Select an item",
//		options: [{ label: "Item 1", value: "item1" }, { label: "Item 2", value: "item2" }],
//		value: "Item 1",
//		onChange: "select-changed"
//	})
func (c *ComponentManager) jsSelect(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "select", []ComponentProp{
		{Name: "label", Type: "string", Required: true, OptionalFirstArg: true, Validate: validateType("string")},
		{Name: "placeholder", Type: "string", Required: false, Validate: validateType("string")},
		{
			Name:     "options",
			Type:     "array",
			Required: true,
			Validate: validateOptions,
		},
		{Name: "value", Type: "string", Required: false, Default: "", Validate: validateType("string")},
		{Name: "onChange", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "fieldRef", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "disabled", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "size", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

// jsCheckbox
//
//	Example:
//	const checkbox = tray.checkbox("I agree to the terms and conditions")
//	// or
//	const checkbox = tray.checkbox({ label: "I agree to the terms and conditions", value: true })
func (c *ComponentManager) jsCheckbox(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "checkbox", []ComponentProp{
		{Name: "label", Type: "string", Required: true, OptionalFirstArg: true, Validate: validateType("string")},
		{Name: "value", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "onChange", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "fieldRef", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "disabled", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "size", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

// jsRadioGroup
//
//	Example:
//	const radioGroup = tray.radioGroup({
//		options: [{ label: "Item 1", value: "item1" }, { label: "Item 2", value: "item2" }],
//		onChange: "radio-group-changed"
//	})
func (c *ComponentManager) jsRadioGroup(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "radio-group", []ComponentProp{
		{Name: "label", Type: "string", Required: true, OptionalFirstArg: true, Validate: validateType("string")},
		{Name: "value", Type: "string", Required: false, Default: "", Validate: validateType("string")},
		{
			Name:     "options",
			Type:     "array",
			Required: true,
			Validate: validateOptions,
		},
		{Name: "onChange", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "fieldRef", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "disabled", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "size", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}

// jsSwitch
//
//	Example:
//	const switch = tray.switch({
//		label: "Toggle me",
//		value: true
//	})
func (c *ComponentManager) jsSwitch(call goja.FunctionCall) goja.Value {
	return defineComponent(c.ctx.vm, call, "switch", []ComponentProp{
		{Name: "label", Type: "string", Required: true, OptionalFirstArg: true, Validate: validateType("string")},
		{Name: "value", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "onChange", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "fieldRef", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "style", Type: "object", Required: false, Validate: validateType("object")},
		{Name: "disabled", Type: "boolean", Required: false, Default: false, Validate: validateType("boolean")},
		{Name: "size", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "side", Type: "string", Required: false, Validate: validateType("string")},
		{Name: "className", Type: "string", Required: false, Validate: validateType("string")},
	})
}
