package dms

import (
	"strings"

	"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
	tea "github.com/charmbracelet/bubbletea"
)

func (m Model) updatePluginsMenu(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
	switch msg.String() {
	case "ctrl+c", "q":
		return m, tea.Quit
	case "esc":
		m.state = StateMainMenu
	case "up", "k":
		if m.selectedPluginsMenuItem > 0 {
			m.selectedPluginsMenuItem--
		}
	case "down", "j":
		if m.selectedPluginsMenuItem < len(m.pluginsMenuItems)-1 {
			m.selectedPluginsMenuItem++
		}
	case "enter", " ":
		if m.selectedPluginsMenuItem < len(m.pluginsMenuItems) {
			selectedAction := m.pluginsMenuItems[m.selectedPluginsMenuItem].Action
			switch selectedAction {
			case StatePluginsBrowse:
				m.state = StatePluginsBrowse
				m.pluginsLoading = true
				m.pluginsError = ""
				m.pluginsList = nil
				return m, loadPlugins
			case StatePluginsInstalled:
				m.state = StatePluginsInstalled
				m.installedPluginsLoading = true
				m.installedPluginsError = ""
				m.installedPluginsList = nil
				return m, loadInstalledPlugins
			}
		}
	}
	return m, nil
}

func (m Model) updatePluginsBrowse(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
	switch msg.String() {
	case "ctrl+c", "q":
		return m, tea.Quit
	case "esc":
		m.state = StatePluginsMenu
		m.pluginSearchQuery = ""
		m.filteredPluginsList = m.pluginsList
		m.selectedPluginIndex = 0
	case "up", "k":
		if m.selectedPluginIndex > 0 {
			m.selectedPluginIndex--
		}
	case "down", "j":
		if m.selectedPluginIndex < len(m.filteredPluginsList)-1 {
			m.selectedPluginIndex++
		}
	case "enter", " ":
		if m.selectedPluginIndex < len(m.filteredPluginsList) {
			m.state = StatePluginDetail
		}
	case "/":
		m.state = StatePluginSearch
		m.pluginSearchQuery = ""
	}
	return m, nil
}

func (m Model) updatePluginDetail(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
	switch msg.String() {
	case "ctrl+c", "q":
		return m, tea.Quit
	case "esc":
		m.state = StatePluginsBrowse
	case "i":
		if m.selectedPluginIndex < len(m.filteredPluginsList) {
			plugin := m.filteredPluginsList[m.selectedPluginIndex]
			installed := m.pluginInstallStatus[plugin.Name]
			if !installed {
				return m, installPlugin(plugin)
			}
		}
	}
	return m, nil
}

func (m Model) updatePluginSearch(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
	switch msg.String() {
	case "ctrl+c":
		return m, tea.Quit
	case "esc":
		m.state = StatePluginsBrowse
		m.pluginSearchQuery = ""
		m.filteredPluginsList = m.pluginsList
		m.selectedPluginIndex = 0
	case "enter":
		m.state = StatePluginsBrowse
		m.filterPlugins()
	case "backspace":
		if len(m.pluginSearchQuery) > 0 {
			m.pluginSearchQuery = m.pluginSearchQuery[:len(m.pluginSearchQuery)-1]
		}
	default:
		if len(msg.String()) == 1 {
			m.pluginSearchQuery += msg.String()
		}
	}
	return m, nil
}

func (m *Model) filterPlugins() {
	if m.pluginSearchQuery == "" {
		m.filteredPluginsList = m.pluginsList
		m.selectedPluginIndex = 0
		return
	}

	rawPlugins := make([]plugins.Plugin, len(m.pluginsList))
	for i, p := range m.pluginsList {
		rawPlugins[i] = plugins.Plugin{
			ID:           p.ID,
			Name:         p.Name,
			Category:     p.Category,
			Author:       p.Author,
			Description:  p.Description,
			Repo:         p.Repo,
			Path:         p.Path,
			Capabilities: p.Capabilities,
			Compositors:  p.Compositors,
			Dependencies: p.Dependencies,
		}
	}

	searchResults := plugins.FuzzySearch(m.pluginSearchQuery, rawPlugins)
	searchResults = plugins.SortByFirstParty(searchResults)

	filtered := make([]pluginInfo, len(searchResults))
	for i, p := range searchResults {
		filtered[i] = pluginInfo{
			ID:           p.ID,
			Name:         p.Name,
			Category:     p.Category,
			Author:       p.Author,
			Description:  p.Description,
			Repo:         p.Repo,
			Path:         p.Path,
			Capabilities: p.Capabilities,
			Compositors:  p.Compositors,
			Dependencies: p.Dependencies,
			FirstParty:   strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"),
		}
	}

	m.filteredPluginsList = filtered
	m.selectedPluginIndex = 0
}

type pluginsLoadedMsg struct {
	plugins []plugins.Plugin
	err     error
}

func loadPlugins() tea.Msg {
	registry, err := plugins.NewRegistry()
	if err != nil {
		return pluginsLoadedMsg{err: err}
	}

	pluginList, err := registry.List()
	if err != nil {
		return pluginsLoadedMsg{err: err}
	}

	return pluginsLoadedMsg{plugins: pluginList}
}

func (m *Model) updatePluginInstallStatus() {
	manager, err := plugins.NewManager()
	if err != nil {
		return
	}

	for _, plugin := range m.pluginsList {
		p := plugins.Plugin{ID: plugin.ID}
		installed, err := manager.IsInstalled(p)
		if err == nil {
			m.pluginInstallStatus[plugin.Name] = installed
		}
	}
}

func (m Model) updatePluginsInstalled(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
	switch msg.String() {
	case "ctrl+c", "q":
		return m, tea.Quit
	case "esc":
		m.state = StatePluginsMenu
	case "up", "k":
		if m.selectedInstalledIndex > 0 {
			m.selectedInstalledIndex--
		}
	case "down", "j":
		if m.selectedInstalledIndex < len(m.installedPluginsList)-1 {
			m.selectedInstalledIndex++
		}
	case "enter", " ":
		if m.selectedInstalledIndex < len(m.installedPluginsList) {
			m.state = StatePluginInstalledDetail
		}
	}
	return m, nil
}

func (m Model) updatePluginInstalledDetail(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
	switch msg.String() {
	case "ctrl+c", "q":
		return m, tea.Quit
	case "esc":
		m.state = StatePluginsInstalled
	case "u":
		if m.selectedInstalledIndex < len(m.installedPluginsList) {
			plugin := m.installedPluginsList[m.selectedInstalledIndex]
			return m, uninstallPlugin(plugin)
		}
	case "p":
		if m.selectedInstalledIndex < len(m.installedPluginsList) {
			plugin := m.installedPluginsList[m.selectedInstalledIndex]
			return m, updatePlugin(plugin)
		}
	}
	return m, nil
}

type installedPluginsLoadedMsg struct {
	plugins []plugins.Plugin
	err     error
}

type pluginUninstalledMsg struct {
	pluginName string
	err        error
}

type pluginInstalledMsg struct {
	pluginName string
	err        error
}

type pluginUpdatedMsg struct {
	pluginName string
	err        error
}

func loadInstalledPlugins() tea.Msg {
	manager, err := plugins.NewManager()
	if err != nil {
		return installedPluginsLoadedMsg{err: err}
	}

	registry, err := plugins.NewRegistry()
	if err != nil {
		return installedPluginsLoadedMsg{err: err}
	}

	installedNames, err := manager.ListInstalled()
	if err != nil {
		return installedPluginsLoadedMsg{err: err}
	}

	allPlugins, err := registry.List()
	if err != nil {
		return installedPluginsLoadedMsg{err: err}
	}

	var installed []plugins.Plugin
	for _, id := range installedNames {
		for _, p := range allPlugins {
			if p.ID == id {
				installed = append(installed, p)
				break
			}
		}
	}

	installed = plugins.SortByFirstParty(installed)

	return installedPluginsLoadedMsg{plugins: installed}
}

func installPlugin(plugin pluginInfo) tea.Cmd {
	return func() tea.Msg {
		manager, err := plugins.NewManager()
		if err != nil {
			return pluginInstalledMsg{pluginName: plugin.Name, err: err}
		}

		p := plugins.Plugin{
			ID:           plugin.ID,
			Name:         plugin.Name,
			Category:     plugin.Category,
			Author:       plugin.Author,
			Description:  plugin.Description,
			Repo:         plugin.Repo,
			Path:         plugin.Path,
			Capabilities: plugin.Capabilities,
			Compositors:  plugin.Compositors,
			Dependencies: plugin.Dependencies,
		}

		if err := manager.Install(p); err != nil {
			return pluginInstalledMsg{pluginName: plugin.Name, err: err}
		}

		return pluginInstalledMsg{pluginName: plugin.Name}
	}
}

func uninstallPlugin(plugin pluginInfo) tea.Cmd {
	return func() tea.Msg {
		manager, err := plugins.NewManager()
		if err != nil {
			return pluginUninstalledMsg{pluginName: plugin.Name, err: err}
		}

		p := plugins.Plugin{
			ID:           plugin.ID,
			Name:         plugin.Name,
			Category:     plugin.Category,
			Author:       plugin.Author,
			Description:  plugin.Description,
			Repo:         plugin.Repo,
			Path:         plugin.Path,
			Capabilities: plugin.Capabilities,
			Compositors:  plugin.Compositors,
			Dependencies: plugin.Dependencies,
		}

		if err := manager.Uninstall(p); err != nil {
			return pluginUninstalledMsg{pluginName: plugin.Name, err: err}
		}

		return pluginUninstalledMsg{pluginName: plugin.Name}
	}
}

func updatePlugin(plugin pluginInfo) tea.Cmd {
	return func() tea.Msg {
		manager, err := plugins.NewManager()
		if err != nil {
			return pluginUpdatedMsg{pluginName: plugin.Name, err: err}
		}

		p := plugins.Plugin{
			ID:           plugin.ID,
			Name:         plugin.Name,
			Category:     plugin.Category,
			Author:       plugin.Author,
			Description:  plugin.Description,
			Repo:         plugin.Repo,
			Path:         plugin.Path,
			Capabilities: plugin.Capabilities,
			Compositors:  plugin.Compositors,
			Dependencies: plugin.Dependencies,
		}

		if err := manager.Update(p); err != nil {
			return pluginUpdatedMsg{pluginName: plugin.Name, err: err}
		}

		return pluginUpdatedMsg{pluginName: plugin.Name}
	}
}
