// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package goal

import (
	"encoding/json"
	"fmt"

	"git.secluded.site/lune/internal/completion"
	"git.secluded.site/lune/internal/config"
	"git.secluded.site/lune/internal/ui"
	"github.com/charmbracelet/lipgloss"
	"github.com/charmbracelet/lipgloss/table"
	"github.com/spf13/cobra"
)

// ListCmd lists configured goals.
var ListCmd = &cobra.Command{
	Use:   "list",
	Short: "List configured goals",
	Long: `List goals configured in lune.

By default, shows goals from the default area (if configured).
Use --area to specify a different area, or --all to show all goals.`,
	RunE: runList,
}

func init() {
	ListCmd.Flags().StringP("area", "a", "", "Filter by area key")
	ListCmd.Flags().Bool("all", false, "Show goals from all areas")
	ListCmd.Flags().Bool("json", false, "Output as JSON")

	_ = ListCmd.RegisterFlagCompletionFunc("area", completion.Areas)
}

func runList(cmd *cobra.Command, _ []string) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}

	areaKey, _ := cmd.Flags().GetString("area")
	showAll, _ := cmd.Flags().GetBool("all")
	usingDefault := areaKey == "" && !showAll && cfg.Defaults.Area != ""

	areas := filterAreas(cfg, areaKey, showAll)
	if len(areas) == 0 {
		fmt.Fprintln(cmd.OutOrStdout(), "No areas found")

		return nil
	}

	goals := collectGoals(areas)
	if len(goals) == 0 {
		if usingDefault {
			fmt.Fprintf(cmd.OutOrStdout(), "No goals configured in %s area\n", cfg.Defaults.Area)
		} else {
			fmt.Fprintln(cmd.OutOrStdout(), "No goals configured")
		}

		return nil
	}

	jsonFlag, _ := cmd.Flags().GetBool("json")
	if jsonFlag {
		return outputJSON(cmd, goals)
	}

	return outputTable(cmd, goals)
}

type goalWithArea struct {
	Key     string `json:"key"`
	Name    string `json:"name"`
	ID      string `json:"id"`
	AreaKey string `json:"area_key"`
}

func filterAreas(cfg *config.Config, areaKey string, showAll bool) []config.Area {
	if showAll {
		return cfg.Areas
	}

	if areaKey == "" {
		areaKey = cfg.Defaults.Area
	}

	if areaKey == "" {
		return cfg.Areas
	}

	area := cfg.AreaByKey(areaKey)
	if area == nil {
		return nil
	}

	return []config.Area{*area}
}

func collectGoals(areas []config.Area) []goalWithArea {
	var goals []goalWithArea

	for _, area := range areas {
		for _, goal := range area.Goals {
			goals = append(goals, goalWithArea{
				Key:     goal.Key,
				Name:    goal.Name,
				ID:      goal.ID,
				AreaKey: area.Key,
			})
		}
	}

	return goals
}

func outputJSON(cmd *cobra.Command, goals []goalWithArea) error {
	enc := json.NewEncoder(cmd.OutOrStdout())
	enc.SetIndent("", "  ")

	if err := enc.Encode(goals); err != nil {
		return fmt.Errorf("encoding JSON: %w", err)
	}

	return nil
}

func outputTable(cmd *cobra.Command, goals []goalWithArea) error {
	rows := make([][]string, 0, len(goals))

	for _, goal := range goals {
		rows = append(rows, []string{goal.Key, goal.Name, goal.AreaKey})
	}

	tbl := table.New().
		Headers("KEY", "NAME", "AREA").
		Rows(rows...).
		StyleFunc(func(row, col int) lipgloss.Style {
			if row == table.HeaderRow {
				return ui.TableHeaderStyle()
			}

			return lipgloss.NewStyle()
		}).
		Border(ui.TableBorder())

	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())

	return nil
}
