package menumanager

import (
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"github.com/wailsapp/wails/v2/pkg/menu"
	"sync"
)

var trayMenuID int
var trayMenuIDMutex sync.Mutex

func generateTrayID() string {
	trayMenuIDMutex.Lock()
	result := fmt.Sprintf("%d", trayMenuID)
	trayMenuID++
	trayMenuIDMutex.Unlock()
	return result
}

type TrayMenu struct {
	ID            string
	Label         string
	Icon          string
	menuItemMap   *MenuItemMap
	menu          *menu.Menu
	ProcessedMenu *WailsMenu
}

func (t *TrayMenu) AsJSON() (string, error) {
	data, err := json.Marshal(t)
	if err != nil {
		return "", err
	}
	return string(data), nil
}

func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {

	result := &TrayMenu{
		Label:       trayMenu.Label,
		Icon:        trayMenu.Icon,
		menu:        trayMenu.Menu,
		menuItemMap: NewMenuItemMap(),
	}

	result.menuItemMap.AddMenu(trayMenu.Menu)
	result.ProcessedMenu = NewWailsMenu(result.menuItemMap, result.menu)

	return result
}

func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
	newTrayMenu := NewTrayMenu(trayMenu)

	// Hook up a new ID
	trayID := generateTrayID()
	newTrayMenu.ID = trayID

	// Save the references
	m.trayMenus[trayID] = newTrayMenu
	m.trayMenuPointers[trayMenu] = trayID

	return newTrayMenu.AsJSON()
}

// SetTrayMenu updates or creates a menu
func (m *Manager) SetTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
	trayID, trayMenuKnown := m.trayMenuPointers[trayMenu]
	if !trayMenuKnown {
		return m.AddTrayMenu(trayMenu)
	}

	// Create the updated tray menu
	updatedTrayMenu := NewTrayMenu(trayMenu)
	updatedTrayMenu.ID = trayID

	// Save the reference
	m.trayMenus[trayID] = updatedTrayMenu

	return updatedTrayMenu.AsJSON()
}

func (m *Manager) GetTrayMenus() ([]string, error) {
	result := []string{}
	for _, trayMenu := range m.trayMenus {
		JSON, err := trayMenu.AsJSON()
		if err != nil {
			return nil, err
		}
		result = append(result, JSON)
	}

	return result, nil
}

func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) {
	trayID, trayMenuKnown := m.trayMenuPointers[trayMenu]
	if !trayMenuKnown {
		return "", fmt.Errorf("[UpdateTrayMenuLabel] unknown tray id for tray %s", trayMenu.Label)
	}

	type LabelUpdate struct {
		ID    string
		Label string
	}

	update := &LabelUpdate{
		ID:    trayID,
		Label: trayMenu.Label,
	}

	data, err := json.Marshal(update)
	if err != nil {
		return "", errors.Wrap(err, "[UpdateTrayMenuLabel] ")
	}

	return string(data), nil

}

func (m *Manager) GetContextMenus() ([]string, error) {
	result := []string{}
	for _, contextMenu := range m.contextMenus {
		JSON, err := contextMenu.AsJSON()
		if err != nil {
			return nil, err
		}
		result = append(result, JSON)
	}

	return result, nil
}
