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

package init

import (
	"errors"
	"fmt"

	"github.com/charmbracelet/huh"

	"git.secluded.site/lune/internal/config"
)

func manageAreas(cfg *config.Config) error {
	nav := manageAreasAsStep(cfg)
	if nav == navQuit {
		return errQuit
	}

	return nil
}

// manageAreasAsStep runs areas management as a wizard step with Back/Next navigation.
func manageAreasAsStep(cfg *config.Config) wizardNav {
	for {
		options := buildAreaStepOptions(cfg.Areas)

		choice, err := runListSelect(
			"Areas & Goals",
			"Areas organize your life (Work, Health, etc). Goals belong to areas.",
			options,
		)
		if err != nil {
			return navQuit
		}

		switch choice {
		case choiceBack:
			return navBack
		case choiceNext:
			return navNext
		case choiceAdd:
			if err := addArea(cfg); errors.Is(err, errQuit) {
				return navQuit
			}
		default:
			idx, ok := parseEditIndex(choice)
			if !ok {
				continue
			}

			if err := manageAreaActions(cfg, idx); errors.Is(err, errQuit) {
				return navQuit
			}
		}
	}
}

func buildAreaStepOptions(areas []config.Area) []huh.Option[string] {
	options := []huh.Option[string]{
		huh.NewOption("Add new area", choiceAdd),
	}

	for idx, area := range areas {
		goalCount := len(area.Goals)
		label := fmt.Sprintf("%s (%s) - %d goal(s)", area.Name, area.Key, goalCount)
		options = append(options, huh.NewOption(label, fmt.Sprintf("edit:%d", idx)))
	}

	options = append(options,
		huh.NewOption("← Back", choiceBack),
		huh.NewOption("Next →", choiceNext),
	)

	return options
}

func addArea(cfg *config.Config) error {
	area, err := editArea(nil, cfg)
	if err != nil {
		if errors.Is(err, errBack) {
			return nil
		}

		return err
	}

	cfg.Areas = append(cfg.Areas, *area)

	return maybeManageGoals(cfg, len(cfg.Areas)-1)
}

func manageAreaActions(cfg *config.Config, idx int) error {
	if idx < 0 || idx >= len(cfg.Areas) {
		return fmt.Errorf("%w: area %d", errIndexOutRange, idx)
	}

	area := &cfg.Areas[idx]

	action, err := runActionSelect(fmt.Sprintf("Area: %s (%s)", area.Name, area.Key), true)
	if err != nil {
		return err
	}

	return handleAreaAction(cfg, idx, area, action)
}

func handleAreaAction(cfg *config.Config, idx int, area *config.Area, action itemAction) error {
	switch action {
	case itemActionEdit:
		updated, err := editArea(area, cfg)
		if err != nil {
			if errors.Is(err, errBack) {
				return nil
			}

			return err
		}

		if updated != nil {
			cfg.Areas[idx] = *updated
		}
	case itemActionGoals:
		return manageGoals(cfg, idx)
	case itemActionDelete:
		return deleteArea(cfg, idx)
	case itemActionNone:
		// User cancelled or went back
	}

	return nil
}

func editArea(existing *config.Area, cfg *config.Config) (*config.Area, error) {
	area := config.Area{}
	if existing != nil {
		area = *existing
	}

	err := runItemForm(&area.Name, &area.Key, &area.ID, itemFormConfig{
		itemType:         "area",
		namePlaceholder:  "Personal",
		keyPlaceholder:   "personal",
		keyValidator:     validateAreaKey(cfg, existing),
		supportsDeepLink: true,
	})
	if err != nil {
		return nil, err
	}

	return &area, nil
}

func validateAreaKey(cfg *config.Config, existing *config.Area) func(string) error {
	return func(input string) error {
		if err := validateKeyFormat(input); err != nil {
			return err
		}

		for idx := range cfg.Areas {
			if existing != nil && &cfg.Areas[idx] == existing {
				continue
			}

			if cfg.Areas[idx].Key == input {
				return errKeyDuplicate
			}
		}

		return nil
	}
}

func maybeManageGoals(cfg *config.Config, areaIdx int) error {
	var manage bool

	err := huh.NewConfirm().
		Title("Add goals for this area?").
		Affirmative("Yes").
		Negative("Not now").
		Value(&manage).
		Run()
	if err != nil {
		if errors.Is(err, huh.ErrUserAborted) {
			return errQuit
		}

		return err
	}

	if manage {
		return manageGoals(cfg, areaIdx)
	}

	return nil
}

func manageGoals(cfg *config.Config, areaIdx int) error {
	if areaIdx < 0 || areaIdx >= len(cfg.Areas) {
		return fmt.Errorf("%w: area %d", errIndexOutRange, areaIdx)
	}

	area := &cfg.Areas[areaIdx]

	for {
		options := buildGoalOptions(area.Goals)

		choice, err := runListSelect(
			fmt.Sprintf("Goals for: %s (%s)", area.Name, area.Key),
			"",
			options,
		)
		if err != nil {
			return err
		}

		if choice == choiceDone {
			return nil
		}

		if choice == choiceAdd {
			if err := addGoal(area); err != nil {
				return err
			}

			continue
		}

		idx, ok := parseEditIndex(choice)
		if !ok {
			continue
		}

		if err := manageGoalActions(area, idx); err != nil {
			return err
		}
	}
}

func buildGoalOptions(goals []config.Goal) []huh.Option[string] {
	options := []huh.Option[string]{
		huh.NewOption("Add new goal", choiceAdd),
	}

	for idx, goal := range goals {
		label := fmt.Sprintf("%s (%s)", goal.Name, goal.Key)
		options = append(options, huh.NewOption(label, fmt.Sprintf("edit:%d", idx)))
	}

	options = append(options, huh.NewOption("Done", choiceDone))

	return options
}

func addGoal(area *config.Area) error {
	goal, err := editGoal(nil, area)
	if err != nil {
		if errors.Is(err, errBack) {
			return nil
		}

		return err
	}

	area.Goals = append(area.Goals, *goal)

	return nil
}

func manageGoalActions(area *config.Area, idx int) error {
	if idx < 0 || idx >= len(area.Goals) {
		return fmt.Errorf("%w: goal %d", errIndexOutRange, idx)
	}

	goal := &area.Goals[idx]

	action, err := runActionSelect(fmt.Sprintf("Goal: %s (%s)", goal.Name, goal.Key), false)
	if err != nil {
		return err
	}

	switch action {
	case itemActionEdit:
		updated, err := editGoal(goal, area)
		if err != nil {
			if errors.Is(err, errBack) {
				return nil
			}

			return err
		}

		if updated != nil {
			area.Goals[idx] = *updated
		}
	case itemActionDelete:
		return deleteGoal(area, idx)
	case itemActionNone, itemActionGoals:
		// User cancelled or went back; goals not applicable here
	}

	return nil
}

func editGoal(existing *config.Goal, area *config.Area) (*config.Goal, error) {
	goal := config.Goal{}
	if existing != nil {
		goal = *existing
	}

	err := runItemForm(&goal.Name, &goal.Key, &goal.ID, itemFormConfig{
		itemType:         "goal",
		namePlaceholder:  "Learn Gaelic",
		keyPlaceholder:   "gaelic",
		keyValidator:     validateGoalKey(area, existing),
		supportsDeepLink: true,
	})
	if err != nil {
		return nil, err
	}

	return &goal, nil
}

func validateGoalKey(area *config.Area, existing *config.Goal) func(string) error {
	return func(input string) error {
		if err := validateKeyFormat(input); err != nil {
			return err
		}

		for idx := range area.Goals {
			if existing != nil && &area.Goals[idx] == existing {
				continue
			}

			if area.Goals[idx].Key == input {
				return errKeyDuplicate
			}
		}

		return nil
	}
}

func deleteArea(cfg *config.Config, idx int) error {
	if idx < 0 || idx >= len(cfg.Areas) {
		return fmt.Errorf("%w: area %d", errIndexOutRange, idx)
	}

	area := cfg.Areas[idx]

	var confirm bool

	err := huh.NewConfirm().
		Title(fmt.Sprintf("Delete area '%s'?", area.Name)).
		Description(fmt.Sprintf("This will also remove %d goal(s). This cannot be undone.", len(area.Goals))).
		Affirmative("Delete").
		Negative("Cancel").
		Value(&confirm).
		Run()
	if err != nil {
		if errors.Is(err, huh.ErrUserAborted) {
			return errQuit
		}

		return err
	}

	if confirm {
		cfg.Areas = append(cfg.Areas[:idx], cfg.Areas[idx+1:]...)
		if cfg.Defaults.Area == area.Key {
			cfg.Defaults.Area = ""
		}
	}

	return nil
}

func deleteGoal(area *config.Area, idx int) error {
	if idx < 0 || idx >= len(area.Goals) {
		return fmt.Errorf("%w: goal %d", errIndexOutRange, idx)
	}

	goal := area.Goals[idx]

	var confirm bool

	err := huh.NewConfirm().
		Title(fmt.Sprintf("Delete goal '%s'?", goal.Name)).
		Description("This cannot be undone.").
		Affirmative("Delete").
		Negative("Cancel").
		Value(&confirm).
		Run()
	if err != nil {
		if errors.Is(err, huh.ErrUserAborted) {
			return errQuit
		}

		return err
	}

	if confirm {
		area.Goals = append(area.Goals[:idx], area.Goals[idx+1:]...)
	}

	return nil
}
