Project, Task and Time management
package main

import (
	"context"
	"fmt"
	"strings"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	"github.com/charmbracelet/bubbles/list"
)

type listItem struct {
	title string
	desc string
	id int64
	started bool
}

func (item listItem) Title() string {
	return item.title
}

func (item listItem) Description() string {
	return item.desc
}

func (item listItem) FilterValue() string {
	return item.title
}

type selectorItemsMsg = []list.Item

type selectedItemMsg = int64

func (model selector) changeItem(item list.Item) tea.Cmd {
	if i, ok := item.(listItem); ok {
		return func() tea.Msg {
			return selectedItemMsg(i.id)
		}
	}

	return nil
}


func (model selector) fetchScheduledTasks() tea.Msg {
	row, err := model.config.queries.ScheduledTasks(context.Background())

	if err != nil {
		return errMsg{err}
	}

	items := make([]list.Item, len(row))
	for i, e := range row {
		var desc strings.Builder

		desc.WriteString(fmt.Sprintf("Project: %s", e.ProjectName))

		if e.DueAt.Valid {
			desc.WriteString(fmt.Sprintf("\nDue: %s", e.DueAt.Time.Format("2006/02/01")))
		}

		items[i] = listItem{
			title: e.Name,
			desc: desc.String(),
			id: e.ID,
			started: e.StartAt.Valid,
		}
	}

	return selectorItemsMsg(items)
}

type selector struct {
	config *config
	list   list.Model
	fetchFunc tea.Cmd
	err    error
	width  int
	height int
}

func (model selector) Init() tea.Cmd {
	return tea.Batch(model.fetchFunc, model.list.StartSpinner())
}

func (model selector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.WindowSizeMsg:
		model.width = msg.Width
		model.height = msg.Height
		model.list.SetSize(model.width, model.height)
		return model, nil

	case selectorItemsMsg:
		model.list.SetItems(msg)
		model.list.StopSpinner()
		if len(msg) > 0 {
			if i, ok := msg[0].(listItem); ok {
				return model, func () tea.Msg {
					return selectedItemMsg(i.id)
				}
			}
		}
		return model, nil

	case errMsg:
		model.err = msg.err
		model.list.StopSpinner()
		return model, nil

	case startedTaskMsg:
		return model, tea.Batch(model.fetchFunc, model.list.StartSpinner())
	}

	var cmds []tea.Cmd
	prevItem := model.list.SelectedItem()

	newList, cmd := model.list.Update(msg)
	cmds = append(cmds, cmd)
	model.list = newList

	if model.list.SelectedItem() != nil && model.list.FilterState() != list.Filtering && prevItem != model.list.SelectedItem() {
		cmds = append(cmds, model.changeItem(model.list.SelectedItem()))
	}

	return model, tea.Batch(cmds...)
}

func (model selector) View() string {
	if model.err != nil {
		return lipgloss.PlaceHorizontal(model.width, lipgloss.Center, model.err.Error())
	}

	if len(model.list.Items()) < 1 {
		model.list.SetShowStatusBar(false)
	}

	return lipgloss.NewStyle().Height(model.height).Width(model.width).Render(model.list.View())
}