package app

import (
	
	
	
	
	
	
	
	
	

	
)

// UI is the interface that describes a user interface element such as
// components and HTML elements.
type UI interface {
	// JSValue returns the javascript value linked to the element.
	JSValue() Value

	// Reports whether the element is mounted.
	Mounted() bool

	parent() UI
	setParent(UI) UI
}

// FilterUIElems processes and returns a filtered list of the provided UI
// elements.
//
// Specifically, it:
// - Interprets and removes selector elements such as Condition and RangeLoop.
// - Eliminates nil elements and nil pointers.
// - Flattens and includes the children of recognized selector elements.
//
// This function is primarily intended for components that accept ui elements as
// variadic arguments or slice, such as the Body method of HTML elements.
func ( ...UI) []UI {
	if len() == 0 {
		return nil
	}

	 := func( int) {
		copy([:], [+1:])
		[len()-1] = nil
		 = [:len()-1]
	}

	var  []UI
	 := func( int,  ...UI) {
		 = append(, [+1:]...)
		 = append([:], ...)
		 = append(, ...)
		 = [:0]
	}

	for  := len() - 1;  >= 0; -- {
		 := []
		if  == nil {
			()
		}
		if  := reflect.ValueOf(); .Kind() == reflect.Pointer && .IsNil() {
			()
		}

		switch elem := .(type) {
		case Condition:
			(, .body()...)

		case RangeLoop:
			(, .body()...)
		}
	}

	return 
}

// HTMLString returns a string that represents the HTML markup for the provided
// UI element.
func ( UI) string {
	return HTMLStringWithEvents(, nil)
}

// HTMLStringWithEvents is HTMLString with an EventBridge, allowing a server
// renderer to receive event handlers.
func ( UI,  EventBridge) string {
	 := NewTestEngine().(*engineX)
	.nodes.eventBridge = 
	var  bytes.Buffer
	.nodes.Encode(.baseContext(), &, )
	return .String()
}

// PrintHTML writes the HTML representation of the given UI element into the
// specified writer.
func ( io.Writer,  UI) {
	.Write([]byte(HTMLString()))
}

// Component events.
type nav struct{}
type appUpdate struct{}
type appInstallChange struct{}
type resize struct{}

// EventBridge receives event handlers during server-side HTML rendering.
// Returning true means the handler was consumed and should not be rendered.
type EventBridge func(element HTML, event string, handler EventHandler) bool

// nodeManager orchestrates the lifecycle of UI elements, providing specialized
// mechanisms for mounting, dismounting, and updating nodes.
type nodeManager struct {
	// eventBridge is optional and used by server-side renderers, eg livepage.
	eventBridge EventBridge
}

// Mount mounts a UI element based on its type and the specified depth. It
// returns the mounted UI element and any potential error during the process.
func ( nodeManager) ( Context,  uint,  UI) (UI, error) {
	 = .context(, )

	switch v := .(type) {
	case *text:
		return .mountText()

	case HTML:
		return .mountHTML(, , )

	case Composer:
		return .mountComponent(, , )

	case *raw:
		return .mountRawHTML(, )

	default:
		return nil, errors.New("unsupported element").
			WithTag("type", reflect.TypeOf()).
			WithTag("depth", )
	}
}

func ( nodeManager) ( *text) (UI, error) {
	if .Mounted() {
		return nil, errors.New("text is already mounted").
			WithTag("parent-type", reflect.TypeOf(.parent())).
			WithTag("preview-value", previewText(.value))
	}

	.jsvalue = Window().createTextNode(.value)
	return , nil
}

func ( nodeManager) ( Context,  uint,  HTML) (UI, error) {
	if .Mounted() {
		return nil, errors.New("html element is already mounted").
			WithTag("parent-type", reflect.TypeOf(.parent())).
			WithTag("type", reflect.TypeOf()).
			WithTag("tag", .Tag()).
			WithTag("depth", .depth())
	}

	,  := Window().createElement(.Tag(), .XMLNamespace())
	 = .setJSElement()
	.mountHTMLAttributes(, )
	.mountHTMLEventHandlers(, )

	 = .setDepth().(HTML)
	 := .body()
	for ,  := range  {
		var  error
		if ,  = .Mount(, +1, );  != nil {
			return nil, errors.New("mounting child failed").
				WithTag("type", reflect.TypeOf()).
				WithTag("tag", .Tag()).
				WithTag("depth", ).
				WithTag("index", ).
				Wrap()
		}
		 = .setParent()
		[] = 
		.JSValue().appendChild()
	}

	return , nil
}

func ( nodeManager) ( Context,  HTML) {
	for ,  := range .attrs() {
		setJSAttribute(.JSValue(), , resolveAttributeURLValue(
			,
			,
			.ResolveStaticResource,
		))
	}
}

func ( nodeManager) ( Context,  HTML) {
	 := .events()
	for ,  := range  {
		[] = .mountHTMLEventHandler(, , )
	}
}

func ( nodeManager) ( Context,  HTML,  eventHandler) eventHandler {
	 := .event

	 := FuncOf(func( Value,  []Value) any {
		if len() != 0 {
			.Dispatch(func( Context) {
				 := Event{Value: [0]}
				trackMousePosition()
				.goHandler(, )
			})
		}
		return nil
	})
	.JSValue().addEventListener(, , .options())

	return eventHandler{
		event:     ,
		scope:     .scope,
		goHandler: .goHandler,
		jsHandler: ,
		close: func() {
			.JSValue().removeEventListener(, )
			.Release()
		},
	}
}

func ( nodeManager) ( Context,  uint,  Composer) (UI, error) {
	if .Mounted() {
		return nil, errors.New("component is already mounted").
			WithTag("parent-type", reflect.TypeOf(.parent())).
			WithTag("type", reflect.TypeOf()).
			WithTag("depth", .depth())
	}

	 = .setRef()
	 = .setDepth()

	if ,  := .(Initializer);  {
		.OnInit()
	}

	if ,  := .(PreRenderer);  && IsServer {
		.Dispatch(.OnPreRender)
	}

	// if mounter, ok := v.(Mounter); ok && IsClient {
	,  := .(Mounter)
	if  {
		.Dispatch(.OnMount)
	}

	,  := .renderComponent()
	if  != nil {
		return nil, errors.New("rendering component failed").
			WithTag("type", reflect.TypeOf()).
			WithTag("depth", .depth()).
			Wrap()
	}
	if ,  = .Mount(, +1, );  != nil {
		return nil, errors.New("mounting component root failed").
			WithTag("type", reflect.TypeOf()).
			WithTag("depth", .depth()).
			Wrap()
	}
	 = .setParent()
	 = .setRoot()

	return , nil
}

func ( nodeManager) ( Composer) (UI, error) {
	 := FilterUIElems(.Render())
	if len() == 0 {
		return nil, errors.New("render method does not returns a text, html element, or component")
	}
	return [0], nil
}

func ( nodeManager) ( uint,  *raw) (UI, error) {
	if .Mounted() {
		return nil, errors.New("raw html is already mounted").
			WithTag("parent-type", reflect.TypeOf(.parent())).
			WithTag("type", reflect.TypeOf()).
			WithTag("depth", .depth()).
			WithTag("raw-preview", previewText(.value))
	}

	,  := Window().createElement("div", "")
	.setInnerHTML(.value)
	.jsElement = .firstChild()
	.removeChild(.jsElement)
	return , nil
}

// Dismount removes a UI element based on its type.
func ( nodeManager) ( UI) {
	switch v := .(type) {
	case *text:

	case HTML:
		.dismountHTML()

	case Composer:
		.dismountComponent()

	case *raw:
		.dismountRawHTML()
	}
}

func ( nodeManager) ( HTML) {
	for ,  := range .body() {
		.Dismount()
	}

	for ,  := range .events() {
		.dismountHTMLEventHandler()
	}

	.setJSElement(nil)
}

func ( nodeManager) ( eventHandler) {
	if .close != nil {
		.close()
	}
}

func ( nodeManager) ( Composer) {
	.Dismount(.root())
	.setRef(nil)

	if ,  := .(Dismounter);  {
		.OnDismount()
	}
}

func ( nodeManager) ( *raw) {
	.jsElement = nil
}

// CanUpdate determines whether a given UI element 'v' can be updated with a new
// UI element 'new'. It returns false if the types of the two elements are
// different.
//
// For HTML elements, it ensures that the tag names match. Otherwise, it returns
// true indicating that an update is feasible.
func ( nodeManager) (,  UI) bool {
	if ,  := reflect.TypeOf(), reflect.TypeOf();  !=  {
		return false
	}

	switch .(type) {
	case DismountEnforcer:
		return .(DismountEnforcer).CompoID() == .(DismountEnforcer).CompoID()

	case *htmlElem, *htmlElemSelfClosing:
		return .(HTML).Tag() == .(HTML).Tag()

	default:
		return true
	}
}

// Update updates the existing UI element 'v' with a new UI element 'new'. It
// returns the updated UI element and any error encountered during the update
// process.
func ( nodeManager) ( Context, ,  UI) (UI, error) {
	if !.Mounted() {
		return nil, errors.New("element not mounted").WithTag("type", reflect.TypeOf())
	}

	 = .context(, )
	switch v := .(type) {
	case *text:
		return .updateText(, .(*text))

	case HTML:
		return .updateHTML(, , .(HTML))

	case Composer:
		return .updateComponent(, , .(Composer))

	case *raw:
		return .updateRawHTML(, , .(*raw))

	default:
		return nil, errors.New("unsupported element").WithTag("type", reflect.TypeOf())
	}
}

func ( nodeManager) (,  *text) (UI, error) {
	if .value == .value {
		return , nil
	}

	.value = .value
	.JSValue().setNodeValue(.value)
	return , nil
}

func ( nodeManager) ( Context, ,  HTML) (UI, error) {
	 := .attrs()
	 := .attrs()
	if  == nil && len() != 0 {
		 = .setAttrs()
		.mountHTMLAttributes(, )
	} else if  != nil {
		.updateHTMLAttributes(, , )
	}

	 := .events()
	 := .events()
	if  == nil && len() != 0 {
		 = .setEvents()
		.mountHTMLEventHandlers(, )
	} else if  != nil {
		.updateHTMLEventHandlers(, , )
	}

	 := .body()
	 := .body()
	 := min(len(), len())
	for  := 0;  < min(len(), len()); ++ {
		 := []
		 := []
		if .CanUpdate(, ) {
			,  := .Update(, , )
			if  != nil {
				return nil, errors.New("updating child failed").
					WithTag("type", reflect.TypeOf()).
					WithTag("tag", .Tag()).
					WithTag("depth", .depth()).
					WithTag("index", ).
					Wrap()
			}
			[] = 
			continue
		}

		,  := .Mount(, .depth()+1, [])
		if  != nil {
			return nil, errors.New("mounting child failed").
				WithTag("type", reflect.TypeOf()).
				WithTag("tag", .Tag()).
				WithTag("depth", .depth()).
				WithTag("index", ).
				Wrap()
		}
		.JSValue().replaceChild(, )
		 = .setParent()
		[] = 
		.Dismount()
	}

	for  := ;  < len(); ++ {
		 := []
		.JSValue().removeChild()
		.Dismount()
		[] = nil
	}
	 = [:]

	for  := ;  < len(); ++ {
		,  := .Mount(, .depth()+1, [])
		if  != nil {
			return nil, errors.New("mounting child failed").
				WithTag("type", reflect.TypeOf()).
				WithTag("tag", .Tag()).
				WithTag("depth", .depth()).
				WithTag("index", ).
				Wrap()
		}
		.JSValue().appendChild()
		 = .setParent()
		 = append(, )
	}

	 = .setBody()
	return , nil
}

func ( nodeManager) ( Context,  HTML,  attributes) {
	 := .attrs()
	for  := range  {
		if ,  := []; ! {
			deleteJSAttribute(.JSValue(), )
			delete(, )
		}
	}

	for ,  := range  {
		if [] ==  {
			continue
		}

		[] = 
		setJSAttribute(.JSValue(), , resolveAttributeURLValue(
			,
			,
			.ResolveStaticResource,
		))
	}
}

func ( nodeManager) ( Context,  HTML,  eventHandlers) {
	 := .events()
	for ,  := range  {
		if ,  := []; ! {
			.dismountHTMLEventHandler()
			delete(, )
		}
	}

	for ,  := range  {
		,  := []
		if ! {
			[] = .mountHTMLEventHandler(, , )
			continue
		}

		if .Equal() {
			continue
		}

		.dismountHTMLEventHandler()
		[] = .mountHTMLEventHandler(, , )
	}
}

func ( nodeManager) ( Context, ,  Composer) (UI, error) {
	 := reflect.Indirect(reflect.ValueOf())
	 := reflect.Indirect(reflect.ValueOf())

	var  bool
	for  := 0;  < .NumField(); ++ {
		 := .Field()
		 := .Field()
		if !.CanSet() {
			continue
		}
		if ,  := .Interface().(Compo);  {
			continue
		}
		if !canUpdateValue(, ) {
			continue
		}
		.Set()
		 = true
	}
	if ! {
		return , nil
	}

	if ,  := .(Updater);  {
		.OnUpdate()
	}

	.removeComponentUpdate()
	return .UpdateComponentRoot(, )
}

// UpdateComponentRoot updates the root element of the given component.
func ( nodeManager) ( Context,  Composer) (UI, error) {
	 = .context(, )

	 := .root()
	,  := .renderComponent()
	if  != nil {
		return nil, errors.New("rendering component failed").
			WithTag("type", reflect.TypeOf()).
			WithTag("depth", .depth()).
			Wrap()
	}

	if .CanUpdate(, ) {
		if ,  = .Update(, , );  != nil {
			return nil, errors.New("updating component root failed").
				WithTag("type", reflect.TypeOf()).
				WithTag("depth", .depth()).
				Wrap()
		}
		.setRoot()
	} else {
		if ,  = .Mount(, .depth()+1, );  != nil {
			return nil, errors.New("mounting component root failed").
				WithTag("type", reflect.TypeOf()).
				WithTag("depth", .depth()).
				Wrap()
		}

		for  := .parent();  != nil;  = .parent() {
			if ,  := .(HTML);  {
				.JSValue().replaceChild(, )
				break
			}
		}
		.setParent()
		.setRoot()
		.Dismount()
	}

	return , nil
}

func ( nodeManager) ( Context, ,  *raw) (UI, error) {
	if .value == .value {
		return , nil
	}

	,  := .Mount(, .depth(), )
	if  != nil {
		return nil, errors.New("mounting updated raw html failed").
			WithTag("type", reflect.TypeOf()).
			WithTag("depth", .depth()).
			WithTag("raw-preview", previewText(.value)).
			Wrap()
	}

	for  := .parent();  != nil;  = .parent() {
		if ,  := .(HTML);  {
			.JSValue().replaceChild(, )
			.setParent()
			break
		}
	}
	.Dismount()
	return , nil
}

func ( nodeManager) ( Context,  UI) Context {
	.sourceElement = 
	.notifyComponentEvent = .NotifyComponentEvent
	return 
}

// NotifyComponentEvent traverses a UI element tree to propagate a component
// event, activating pertinent component handlers and potentially enqueuing
// component updates as needed.
func ( nodeManager) ( Context,  UI,  any) {
	 = .context(, )

	switch element := .(type) {
	case HTML:
		for ,  := range .body() {
			.(, , )
		}

	case Composer:
		switch .(type) {
		case nav:
			if ,  := .(Navigator);  {
				.Dispatch(.OnNav)
			}

		case appUpdate:
			if ,  := .(AppUpdater);  {
				.Dispatch(.OnAppUpdate)
			}

		case appInstallChange:
			if ,  := .(AppInstaller);  {
				.Dispatch(.OnAppInstallChange)
			}

		case resize:
			if ,  := .(Resizer);  {
				.Dispatch(.OnResize)
			}
		}
		.(, .root(), )
	}
}

// Encode transforms the provided UI element into its HTML byte slice
// representation. This allows for the conversion of in-memory UI structures
// into a format suitable for server rendering.
func ( nodeManager) ( Context,  *bytes.Buffer,  UI) {
	.encode(, , 0, )
}

func ( nodeManager) ( Context,  *bytes.Buffer,  int,  UI) {
	switch v := .(type) {
	case *text:
		.encodeText(, , )

	case HTML:
		.encodeHTML(, , , )

	case Composer:
		.encodeComponent(, , , )

	case *raw:
		.encodeRawHTML(, , )
	}
}

func ( nodeManager) ( *bytes.Buffer,  int,  *text) {
	if .value != "" {
		.encodeIndent(, )
		.WriteString(html.EscapeString(.value))
	}
}

func ( nodeManager) ( *bytes.Buffer,  int) {
	for  := 0;  < *2; ++ {
		.WriteByte(' ')
	}
}

func ( nodeManager) ( Context,  *bytes.Buffer,  int,  HTML) {
	if .eventBridge != nil {
		for ,  := range .events() {
			.eventBridge(, , .goHandler)
		}
	}

	.encodeIndent(, )
	.WriteByte('<')
	.WriteString(.Tag())
	 := slices.Collect(maps.Keys(.attrs()))
	sort.Strings()
	for ,  := range  {
		 := .attrs()[]
		.encodeHTMLAttribute(, , , )
	}
	.WriteByte('>')

	if .SelfClosing() {
		return
	}

	 := .body()
	switch {
	case len() > 1:
		.WriteByte('\n')
		for ,  := range  {
			.encode(, , +1, )
			.WriteByte('\n')
		}
		.encodeIndent(, )

	case len() == 1:
		 := [0]
		if ,  := .(*text);  {
			.encodeText(, 0, )
		} else {
			.WriteByte('\n')
			.encode(, , +1, )
			.WriteByte('\n')
			.encodeIndent(, )
		}
	}

	.WriteString("</")
	.WriteString(.Tag())
	.WriteByte('>')
}

func ( nodeManager) ( Context,  *bytes.Buffer, ,  string) {
	if  == "" {
		switch  {
		case "id", "class", "title":
			return
		}
	}

	.WriteString(" ")
	.WriteString()
	if  != "" &&  != "true" {
		.WriteString("=")
		.WriteString(strconv.Quote(resolveAttributeURLValue(, , .ResolveStaticResource)))
	}
}

func ( nodeManager) ( Context,  *bytes.Buffer,  int,  Composer) {
	 := .root()
	if  == nil {
		, _ = .renderComponent()
	}
	if  != nil {
		.encode(, , , )
	}
}

func ( nodeManager) ( *bytes.Buffer,  int,  *raw) {
	if .value != "" {
		.encodeIndent(, )
		.WriteString(.value)
	}
}

func canUpdateValue(,  reflect.Value) bool {
	switch .Kind() {
	case reflect.String,
		reflect.Bool,
		reflect.Int,
		reflect.Int64,
		reflect.Int32,
		reflect.Int16,
		reflect.Int8,
		reflect.Uint,
		reflect.Uint64,
		reflect.Uint32,
		reflect.Uint16,
		reflect.Uint8,
		reflect.Float64,
		reflect.Float32:
		return !.Equal()

	default:
		switch .Interface().(type) {
		case time.Time:
			return !.Equal()

		default:
			return !reflect.DeepEqual(.Interface(), .Interface())
		}
	}
}

func component( UI) (Composer, bool) {
	for  := ;  != nil;  = .parent() {
		if ,  := .(Composer);  {
			return , true
		}
	}
	return nil, false
}